JavaScript事件

事件可以是浏览器行为(页面加载完成)或用户行为(点击,移动鼠标,键盘输入等)。

一、事件流

浏览器处理事件的过程:

  • 捕获阶段:事件从window对象开始沿着DOM树向下传播,知道事件发生的那个目标树元素 (用于提前拦截或处理事件)。
  • 目标阶段:事件到达event.target元素本身。绑定在目标元素上的事件监听器无论设置为在捕 获阶段还是冒泡阶段触发都会被执行(事件到达真正的目标元素,执行绑定在该 元素上的事件处理程序)。
  • 冒泡阶段:事件从目标元素开始,沿着DOM树向上传播(可以将单个事件监听器绑定在父元 素上利用冒泡机制处理所有子元素)。

二、事件处理方式

1、HTML处理方式

直接在HTML中编写JavaScript代码,容易全局命名空间污染等问题。

2、DOM属性方式

通过DOM元素on[event]属性分配函数,但每件事只能绑定一个处理函数(后赋值的会覆盖前面的),并且无法控制事件流阶段,只能在冒泡阶段触发。

3、事件监听器

使用addEventListener函数可以为同一事件添加多个处理函数,可以控制事件触发阶段,并且便于事件移除。

(1)addEventListener函数

基础语法:

element.addEventListener(eventName, handler [, options]);

element.addEventListener(eventName, handler [, useCapture]);

参数解释:

  • eventName:监听的事件类型
javascript 复制代码
//鼠标事件
//click:点击鼠标左键(鼠标按下释放后才触发)
//dblclick:双击左键(注意:双击会先触发两次click,再触发一次dblclick)
//mousedown:鼠标按下(刚按下就触发,不需要等鼠标释放)
//mouseup:鼠标释放时触发
//mouseenter:鼠标进入元素边界时触发
//mouseleave:鼠标离开元素边界时触发
//mousemove:鼠标在元素上移动时持续触发(例:拖拽,绘图,鼠标跟踪)


//键盘事件
//keydown:按下键盘任意键时立即触发(按住不放会持续触发)
document.addEventListener('keydown', (event) => {
    if (event.key === 'Enter') {
        console.log('按下了回车键');
    }
});
//keyup:释放键盘按键时触发(例:确认用户完成按键)


//表单事件
//submit:提交表单时触发
form.addEventListener('submit', (event) => {
    if (!validateForm()) {
        event.preventDefault(); // 阻止默认提交行为
    }
});
//change:表单元素的值改变且失去焦点后触发(适用于<input>,<select>(立即触发,其他都是失                                       去焦点触发),<textarea>)
//input:表单元素的值每次改变时立即触发
input.addEventListener('input', () => {
    console.log('正在输入:', input.value);
});
//focus:元素获取焦点时(例:点击输入框)
//blur:元素失去焦点时(例:点击其他位置)


//文档/窗口事件
//load:资源全部加载完成时触发(适用于<img>,<script>.window)
//DOMContentLoaded:HTML文档完全解析完成(比load快)
//scroll:元素滚动时持续触发
  • handler:事件触发时执行的回调函数,接收一个event对象作为参数;
javascript 复制代码
//事件对象event的常用属性:
function handler(event) {
    // 事件基本信息
    event.type;        // 事件类型 "click"
    event.target;      // 实际触发事件的元素
    event.currentTarget; // 绑定事件的元素 (this 也指向它)
    event.eventPhase;  // 当前阶段: 1捕获, 2目标, 3冒泡
    
    // 鼠标事件特有
    event.clientX;     // 相对视口的X坐标
    event.clientY;
    event.pageX;       // 相对文档的X坐标
    event.pageY;
    event.button;      // 鼠标按钮: 0左键, 1中键, 2右键
    
    // 键盘事件特有
    event.key;         // 按下的键名 "Enter", "a"
    event.code;        // 物理键码 "KeyA", "Space"
    event.ctrlKey;     // 是否按下Ctrl
    
    // 阻止默认行为
    event.preventDefault();
    
    // 停止传播
    event.stopPropagation();      // 阻止冒泡/捕获
    event.stopImmediatePropagation(); // 阻止当前元素的其他监听器
}

对于事件处理函数中this的默认指向:

  • useCapture:布尔值
  • options:配置

注:passive设置为true后如果调用preventDefault会报错

signal相当于外部遥控器,让其他代码可以主动的,批量的移除监听器

添加多个监听器

移除事件监听器:removeEventListener()

三种移除方式对比:

(2)事件监听器的一个简单示例

三、事件委托

利用事件冒泡机制,将多个子元素的事件监听绑定到其共同的父元素上,由父元素上统一处理子元素事件。

javascript 复制代码
//委托:只创建 1 个监听器
list.addEventListener('click', function(event) {
    if (event.target.matches('li')) {
        handler(event);
    }
});



// 动态添加新元素
const list = document.getElementById('list');

//事件委托:新添加的 li 自动具有点击功能
list.addEventListener('click', function(event) {
    if (event.target.tagName === 'LI') {
        console.log('点击了动态元素:', event.target.textContent);
    }
});

// 动态添加新项目(无需再绑定事件)
const newItem = document.createElement('li');
newItem.textContent = '动态添加的项目';
list.appendChild(newItem); // 点击这个新元素也能触发事件

注:并且在事件委托中只需移除一个监听器即可。

1、处理嵌套元素的情况

html 复制代码
<ul id="list">
    <li>
        <span>项目1</span>
        <button>删除</button>
    </li>
    <li>
        <span>项目2</span>
        <button>删除</button>
    </li>
</ul>
javascript 复制代码
//错误:点击 span 或 button 时,event.target 不是 LI
list.addEventListener('click', function(event) {
    if (event.target.tagName === 'LI') { // 点击 span 或 button 时不会触发
        console.log('点击了 li');
    }
});

//正确:使用 closest() 向上查找匹配的元素
list.addEventListener('click', function(event) {
    const li = event.target.closest('li');
    if (li) {
        console.log('点击了 li 或其子元素:', li.textContent);
    }
});

//另一种方式:检查事件是否来自特定选择器的元素
list.addEventListener('click', function(event) {
    if (event.target.matches('li, li *')) { // li 或 li 的任何子元素
        const li = event.target.closest('li');
        console.log('相关 li:', li.textContent);
    }
});

2、closest和matches区别:

四、创建和触发自定义事件

javascript 复制代码
// 创建自定义事件
const customEvent = new CustomEvent('myEvent', {
  detail: { message: '这是自定义数据', time: new Date() },
  bubbles: true,
  cancelable: true
});

// 监听自定义事件
element.addEventListener('myEvent', function(event) {
  console.log('自定义事件触发:', event.detail.message);
});

// 触发自定义事件
element.dispatchEvent(customEvent);

// 简便方式
element.addEventListener('customClick', (e) => {
  console.log('收到:', e.detail);
});

element.dispatchEvent(new CustomEvent('customClick', {
  detail: { id: 123, data: '测试' }
}));
相关推荐
ONExiaobaijs2 小时前
Java jdk运行库合集
java·开发语言·python
wangjialelele2 小时前
二刷C语言后,一万字整理细碎知识点
c语言·开发语言·数据结构·c++·算法·cpp
EEEzhenliang2 小时前
CSS的注释
前端·css
mjhcsp2 小时前
P3145 [USACO16OPEN] Splitting the Field G(题解)
开发语言·c++·算法
猪猪侠|ZZXia2 小时前
# Shell+web+hook的系统构建发布运维系统之API及前端: zzxia-lollipop-remote-gan
运维·前端
rit84324992 小时前
UVE算法提取光谱特征波长的MATLAB实现与应用
开发语言·算法·matlab
Coder_Boy_2 小时前
基于SpringAI的在线考试系统-教学管理与用户管理模块联合回归测试文档
java·前端·数据库·人工智能·spring boot
阿蒙Amon2 小时前
C#每日面试题-简述反射
开发语言·面试·c#
希赛网2 小时前
网工面试:常问技术问题汇总(3)
服务器·前端·网络·网络工程师·ospf·网工面试·技术面