09 DOM事件
loyalvi Lv7

09 DOM事件

要监听一个 <div> 元素的大小变化,可以通过以下方法实现:

方法 1:使用 ResizeObserver API(推荐)

ResizeObserver 是现代浏览器原生支持的 API,专门用于监听元素尺寸变化(包括 width、height、padding、border 等)。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 1. 创建 ResizeObserver 实例
const observer = new ResizeObserver((entries) => {
for (let entry of entries) {
// 获取元素的最新尺寸
const width = entry.contentRect.width;
const height = entry.contentRect.height;
console.log(`元素尺寸变化:宽度 ${width}px,高度 ${height}px`);
}
});
// 2. 获取目标元素
const divElement = document.querySelector('#yourDivId');
// 3. 开始监听
observer.observe(divElement);
// 4. 停止监听(可选)
// observer.unobserve(divElement);

优点

  • 精准监听元素尺寸变化。
  • 支持监听多个元素。
  • 无需依赖 CSS 属性(如动画或过渡)。

浏览器兼容性

  • 支持 Chrome 64+、Firefox 69+、Edge 79+、Safari 13.1+。
  • 如需兼容旧浏览器,需使用 polyfill。

方法 2:通过 window.resize 事件(间接监听)

如果元素尺寸变化是由窗口大小变化触发的(例如 CSS 响应式布局),可以通过监听 window 的 resize 事件间接判断。

代码示例

1
2
3
4
window.addEventListener('resize', () => {
const div = document.querySelector('#yourDivId');
console.log('窗口大小变化,div 尺寸:', div.offsetWidth, div.offsetHeight);
});

缺点

  • 仅能监听到由窗口大小变化触发的尺寸变化。
  • 无法监听到因内容、JavaScript 直接修改样式等导致的尺寸变化。

方法 3:使用 MutationObserver(不推荐)

如果元素尺寸变化是由属性(如 style 或 class)修改触发的,可以尝试用 MutationObserver 监听属性变化。

代码示例

1
2
3
4
5
6
7
8
9
10
11
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === 'style') {
const width = mutation.target.offsetWidth;
const height = mutation.target.offsetHeight;
console.log('元素样式变化,尺寸:', width, height);
}
});
});
const divElement = document.querySelector('#yourDivId');
observer.observe(divElement, { attributes: true });

缺点

  • 无法监听到由内容变化(如子元素增减)导致的尺寸变化。
  • 性能开销较大。

方法 4:CSS 动画监听(Hack 方法)

通过 CSS 动画和 transitionend 事件间接监听尺寸变化(需配合 CSS 过渡)。

代码示例

1
2
3
#yourDivId {
transition: width 0.1s, height 0.1s;
}
1
2
3
4
5
6
const divElement = document.querySelector('#yourDivId');
divElement.addEventListener('transitionend', (e) => {
if (e.propertyName === 'width' || e.propertyName === 'height') {
console.log('元素尺寸变化:', e.target.offsetWidth, e.target.offsetHeight);
}
});

缺点

  • 需要强制添加 CSS 过渡。
  • 仅能监听到通过 CSS 触发的尺寸变化。

总结

方法 适用场景 精准度 兼容性
ResizeObserver 任意尺寸变化(推荐) 高 现代浏览器
window.resize 窗口变化触发的尺寸变化 低 所有浏览器
MutationObserver 属性修改触发的尺寸变化 中 现代浏览器
CSS 动画监听 CSS 过渡触发的尺寸变化(不推荐) 低 所有浏览器
推荐直接使用 ResizeObserver,其他方法可作为兼容性兜底方案。

事件类型 事件名称 是否冒泡
鼠标事件 click 是
dblclick 是
mousedown 是
mouseup 是
mousemove 是
mouseover 是
mouseout 是
mouseenter 否
mouseleave 否
键盘事件 keydown 是
keyup 是
keypress (已废弃) 是
表单事件 submit 是
change 是
input 是
focus 否
blur 否
focusin 是
focusout 是
invalid 是
窗口/文档事件 load 否
unload 否
resize 否
scroll 否
hashchange 否
DOMContentLoaded 否
beforeunload 否
visibilitychange 否
触摸事件 touchstart 是
touchend 是
touchmove 是
touchcancel 是
媒体事件 play 否
pause 否
ended 否
timeupdate 否
过渡/动画事件 transitionend 是
animationstart 是
animationend 是
animationiteration 是
剪贴板事件 copy 是
cut 是
paste 是
拖放事件 dragstart 是
drag 是
dragend 是
dragenter 是
dragover 是
dragleave 是
drop 是
其他事件 contextmenu 是

DOM事件

DOM事件是Web开发中非常重要的一个方面,它允许我们为页面上的元素添加交互性。以下是关于DOM事件的一些详细介绍:

1. 事件类型

1.1 用户界面事件

  • load:当文档加载完成时触发,常用于页面初始化操作。
    1
    2
    3
    window.addEventListener('load', function() {
    console.log('页面加载完成');
    });
  • DOMContentLoaded:当初始的HTML文档被完全加载和解析完毕时触发,不包括样式表、图片和子框架加载时间。
    1
    2
    3
    document.addEventListener('DOMContentLoaded', function() {
    console.log('DOM加载完成');
    });
  • resize:当窗口或框架被调整大小时触发。
    1
    2
    3
    window.addEventListener('resize', function() {
    console.log('窗口大小改变');
    });
  • scroll:当用户滚动文档或元素时触发。
    1
    2
    3
    window.addEventListener('scroll', function() {
    console.log('页面滚动');
    });

1.2 鼠标事件

  • click:鼠标点击事件,常用于按钮点击等操作。
    1
    2
    3
    document.getElementById('myButton').addEventListener('click', function() {
    console.log('按钮被点击');
    });
  • dblclick:鼠标双击事件。
    1
    2
    3
    document.getElementById('myElement').addEventListener('dblclick', function() {
    console.log('元素被双击');
    });
  • mousedown:鼠标按钮被按下时触发。
    1
    2
    3
    document.getElementById('myElement').addEventListener('mousedown', function(event) {
    console.log('鼠标按下,按钮:' + event.button);
    });
  • mouseup:鼠标按钮被释放时触发。
    1
    2
    3
    document.getElementById('myElement').addEventListener('mouseup', function(event) {
    console.log('鼠标释放,按钮:' + event.button);
    });
  • mousemove:鼠标在元素上移动时触发,事件频率较高,使用时需注意性能优化。
    1
    2
    3
    document.getElementById('myElement').addEventListener('mousemove', function(event) {
    console.log('鼠标移动,坐标:(' + event.clientX + ',' + event.clientY + ')');
    });
  • mouseover:鼠标指针移动到元素上时触发。
    1
    2
    3
    document.getElementById('myElement').addEventListener('mouseover', function() {
    console.log('鼠标移入元素');
    });
  • mouseout:鼠标指针离开元素时触发。
    1
    2
    3
    document.getElementById('myElement').addEventListener('mouseout', function() {
    console.log('鼠标移出元素');
    });
  • mouseenter:鼠标指针进入元素时触发,与mouseover不同,它不会在鼠标从子元素移动到父元素时触发。
    1
    2
    3
    document.getElementById('myElement').addEventListener('mouseenter', function() {
    console.log('鼠标进入元素');
    });
  • mouseleave:鼠标指针离开元素时触发,与mouseout不同,它不会在鼠标从父元素移动到子元素时触发。
    1
    2
    3
    document.getElementById('myElement').addEventListener('mouseleave', function() {
    console.log('鼠标离开元素');
    });

1.3 键盘事件

  • keydown:某个键盘按键被按下时触发。
    1
    2
    3
    document.addEventListener('keydown', function(event) {
    console.log('按键按下,键码:' + event.keyCode);
    });
  • keyup:某个键盘按键被释放时触发。
    1
    2
    3
    document.addEventListener('keyup', function(event) {
    console.log('按键释放,键码:' + event.keyCode);
    });
  • keypress:某个键盘按键被按下并产生字符值时触发,不过在现代浏览器中已不推荐使用,可使用keydown和keyup代替。
    1
    2
    3
    document.addEventListener('keypress', function(event) {
    console.log('字符键按下,字符:' + String.fromCharCode(event.charCode));
    });

1.4 表单事件

  • submit:表单提交时触发,常用于表单验证等操作。
    1
    2
    3
    4
    document.getElementById('myForm').addEventListener('submit', function(event) {
    event.preventDefault(); // 阻止表单默认提交行为
    console.log('表单提交');
    });
  • change:表单域内容改变后触发,适用于input、select、textarea等元素。
    1
    2
    3
    document.getElementById('myInput').addEventListener('change', function() {
    console.log('输入框内容改变');
    });
  • input:表单域内容正在改变时触发,与change事件不同,它在输入过程中实时触发。
    1
    2
    3
    document.getElementById('myInput').addEventListener('input', function() {
    console.log('输入框内容正在改变');
    });
  • focus:元素获得焦点时触发。
    1
    2
    3
    document.getElementById('myInput').addEventListener('focus', function() {
    console.log('输入框获得焦点');
    });
  • blur:元素失去焦点时触发。
    1
    2
    3
    document.getElementById('myInput').addEventListener('blur', function() {
    console.log('输入框失去焦点');
    });

2. 事件流

DOM事件的传播遵循特定的路径,称为事件流。事件流分为三个阶段:

  • 捕获阶段:事件从window对象开始,逐级向下传递,直到到达目标元素的父元素。
  • 目标阶段:事件到达目标元素。
  • 冒泡阶段:事件从目标元素开始,逐级向上传递,直到window对象。

3. 事件监听器

通过addEventListener方法可以为元素添加事件监听器,它接受三个参数:

  • 事件类型:字符串,表示事件的名称,如'click'、'mouseover'等。
  • 事件处理函数:当事件触发时要执行的函数。
  • 布尔值或选项对象:用于指定事件监听器的行为。如果为true,表示在捕获阶段触发事件处理函数;如果为false(默认值),表示在冒泡阶段触发事件处理函数。也可以传入一个选项对象,如{capture: true}表示捕获阶段触发,{once: true}表示事件处理函数只执行一次后自动移除等。
    1
    2
    3
    4
    5
    6
    document.getElementById('myElement').addEventListener('click', function() {
    console.log('点击事件');
    }, false); // 指定在冒泡阶段触发
    document.getElementById('myElement').addEventListener('click', function() {
    console.log('捕获阶段点击事件');
    }, true); // 指定在捕获阶段触发

4. 事件对象

当事件触发时,会创建一个事件对象,并作为参数传递给事件处理函数。事件对象包含了许多有用的属性和方法,例如:

  • target:触发事件的元素。
  • currentTarget:绑定事件监听器的元素。
  • type:事件的类型,如'click'、'mouseover'等。
  • eventPhase:事件当前所处的阶段,1表示捕获阶段,2表示目标阶段,3表示冒泡阶段。
  • preventDefault():阻止事件的默认行为。
  • stopPropagation():停止事件的进一步传播,即阻止事件冒泡或捕获。
  • stopImmediatePropagation():停止事件的进一步传播,并阻止同一个事件的其他监听器被调用。
    1
    2
    3
    4
    5
    6
    document.getElementById('myElement').addEventListener('click', function(event) {
    console.log('触发事件的元素:' + event.target);
    console.log('绑定事件监听器的元素:' + event.currentTarget);
    event.preventDefault(); // 阻止默认行为
    event.stopPropagation(); // 停止事件传播
    });

5. 移除事件监听器

使用removeEventListener方法可以移除之前添加的事件监听器,它需要传入与addEventListener相同的参数,即事件类型和事件处理函数。

1
2
3
4
5
6
function handleClick() {
console.log('点击事件');
}
document.getElementById('myElement').addEventListener('click', handleClick);
// 移除事件监听器
document.getElementById('myElement').removeEventListener('click', handleClick);

掌握DOM事件的相关知识,可以帮助我们为网页添加丰富的交互效果,提升用户体验。

事件冒泡

事件冒泡(Event Bubbling)是浏览器中事件传播的一种机制,指的是当一个事件在 DOM 树中发生时,该事件会从事件的目标元素(事件的起始点)开始,逐级向上传播到根元素的过程。以下是关于事件冒泡的一些关键点和示例:

工作原理

  • 传播路径:
    • 当一个事件在某个元素上触发时,该事件首先在该元素上触发,然后逐级向上传播到其父元素,直到达到根元素(通常是 <html> 标签)。
  • 默认行为:
    • 大多数鼠标事件(如 click、mouseover)和一些键盘事件(如 keydown)默认具有冒泡行为。
    • 有些事件默认不冒泡,例如 focus、blur 和 load 等。

示例

假设有一个嵌套的 HTML 结构:

1
2
3
4
5
<div id="outer">
<div id="inner">
<button id="button">Click me</button>
</div>
</div>

在 JavaScript 中为这些元素添加事件监听器:

1
2
3
4
5
6
7
8
9
document.getElementById('button').addEventListener('click', function() {
console.log('Button clicked');
});
document.getElementById('inner').addEventListener('click', function() {
console.log('Inner div clicked');
});
document.getElementById('outer').addEventListener('click', function() {
console.log('Outer div clicked');
});

当点击按钮时,控制台输出会依次显示:

1
2
3
Button clicked
Inner div clicked
Outer div clicked

阻止事件冒泡

  • event.stopPropagation():
    • 可以在事件处理函数中调用 event.stopPropagation() 方法来阻止事件继续冒泡。
    1
    2
    3
    4
    document.getElementById('button').addEventListener('click', function(event) {
    console.log('Button clicked');
    event.stopPropagation(); // 阻止事件冒泡
    });
  • 返回 false:
    • 在使用 jQuery 或某些旧的浏览器事件模型时,返回 false 也可以阻止事件冒泡(同时还会阻止默认行为).
    1
    2
    3
    4
    $('#button').click(function() {
    console.log('Button clicked');
    return false; // 阻止事件冒泡和默认行为
    });

事件捕获

  • 事件捕获与冒泡:
    • 事件冒泡是事件传播的“冒泡阶段”,而事件捕获是事件传播的“捕获阶段”。
    • 在捕获阶段,事件从根元素开始向目标元素传播。
    • 可以通过在 addEventListener 的第三个参数中设置 true 来使用事件捕获。
    1
    2
    3
    document.getElementById('outer').addEventListener('click', function() {
    console.log('Outer div captured');
    }, true);

应用场景

  • 事件委托:
    • 利用事件冒泡,可以在父元素上设置事件监听器来处理子元素的事件,从而减少事件监听器的数量,提高性能。
    1
    2
    3
    4
    5
    document.getElementById('outer').addEventListener('click', function(event) {
    if (event.target.tagName === 'BUTTON') {
    console.log('Button clicked');
    }
    });

    事件冒泡是浏览器事件处理机制的重要组成部分,理解其工作原理和如何控制事件传播对于编写高效的事件处理代码非常重要.

冒泡的事件

在浏览器中,大多数鼠标事件和一些键盘事件默认具有冒泡行为。以下是一些常见的可以冒泡的事件:

鼠标事件

  • click:鼠标点击事件。
  • dblclick:鼠标双击事件。
  • mousedown:鼠标按下事件。
  • mouseup:鼠标释放事件。
  • mousemove:鼠标移动事件。
  • mouseover:鼠标进入元素事件(注意:mouseover 事件在进入目标元素的子元素时也会触发,因此它具有冒泡特性).
  • mouseout:鼠标离开元素事件(同样具有冒泡特性).
  • mouseenter 和 mouseleave:这两个事件不冒泡,但它们的行为类似于 mouseover 和 mouseout,但只在进入或离开目标元素时触发,不进入子元素.
  • contextmenu:右键点击事件(显示上下文菜单).

键盘事件

  • keydown:按下键盘上的任意键时触发。
  • keyup:释放键盘上的任意键时触发。
  • keypress:按下键盘上的字符键时触发(在现代浏览器中已不推荐使用,keydown 和 keyup 可以替代).

表单事件

  • submit:表单提交事件。
  • reset:表单重置事件.
  • change:表单元素的值发生变化时触发(对于 <input>、<select> 和 <textarea> 等元素).
  • input:表单元素的值发生变化时触发,与 change 类似,但 input 事件在每次输入时都会触发,而 change 事件在元素失去焦点时触发.

触摸事件

  • touchstart:触摸开始事件。
  • touchend:触摸结束事件。
  • touchmove:触摸移动事件。
  • touchcancel:触摸取消事件.

自定义事件

  • 自定义事件(通过 dispatchEvent 创建的事件)默认可以冒泡,除非在创建事件时显式设置 bubbles 属性为 false。

不冒泡的事件

  • focus 和 blur:焦点事件不冒泡。
  • load:资源加载完成事件不冒泡。
  • unload:资源卸载事件不冒泡.
  • error:错误事件不冒泡.
  • reset 和 submit:虽然 reset 和 submit 事件可以冒泡,但在某些情况下,它们的行为可能会有所不同,具体取决于浏览器的实现和上下文.
    理解哪些事件可以冒泡以及如何控制事件传播对于编写有效的事件处理代码非常重要,特别是在复杂的 DOM 结构中,事件冒泡可以用于事件委托等技术,从而提高性能和可维护性.

不能冒泡

在浏览器中,有一些事件默认不冒泡。以下是一些常见的不冒泡事件:

焦点事件

  • focus:当元素获得焦点时触发。例如,当用户点击或使用键盘导航使输入框获得焦点时。
  • blur:当元素失去焦点时触发。例如,当用户点击其他元素或切换到其他窗口时,当前元素会触发 blur 事件.

资源加载事件

  • load:当文档或资源完成加载时触发。例如,当整个页面加载完成时,window 对象会触发 load 事件.
  • error:当资源加载失败时触发。例如,如果图片无法加载,img 元素会触发 error 事件.

表单事件

  • reset:当表单被重置时触发。例如,当用户点击表单中的重置按钮时。
  • submit:虽然 submit 事件在某些情况下可以冒泡(例如在表单元素上),但在 form 元素上直接触发时通常不冒泡。具体行为可能因浏览器而异.

其他事件

  • beforeunload:当窗口即将卸载时触发,通常用于提示用户确认是否离开页面.
  • hashchange:当 URL 的哈希值发生变化时触发,例如在单页应用中用于路由变化.
  • resize:当窗口大小改变时触发.
  • scroll:当用户滚动页面时触发.
  • select:当用户选择文本时触发,例如在输入框中选择文本时.
  • textInput:当用户输入文本时触发,类似于 input 事件,但更具体地关注文本输入.

注意事项

  • 事件冒泡与捕获:
    • 事件冒泡是事件传播的“冒泡阶段”,而事件捕获是事件传播的“捕获阶段”。
    • 虽然一些事件不冒泡,但它们仍然可以被捕捉到,通过在 addEventListener 中设置第三个参数为 true 来使用事件捕获.
    1
    2
    3
    window.addEventListener('load', function() {
    console.log('Page loaded');
    }, true); // 使用捕获阶段监听 load 事件
  • 自定义事件:
    • 自定义事件可以设置是否冒泡。在创建自定义事件时,可以通过 new Event 的 bubbles 属性来控制其是否冒泡.
    1
    2
    const customEvent = new Event('customEvent', { bubbles: false });
    element.dispatchEvent(customEvent);

    了解哪些事件不冒泡有助于更好地设计事件处理策略,特别是在复杂的 DOM 结构中,避免不必要的事件传播和处理逻辑.

Vue

多个修饰符

你可以将 .stop 与其他修饰符组合使用,例如 .prevent(阻止默认行为)。

1
<button @click.stop.prevent="handleButtonClick">Click me</button>

阻止默认事件:
e.preventDefault()
e.returnValue = false  (IE)
阻止冒泡:
e.stopPropagation()
e.cancelBubble = true (IE)

由 Hexo 驱动 & 主题 Keep
访客数 访问量