面试通关:浏览器🐾DOM事件🐾
浏览器中的 DOM 事件是指由用户在网页上进行操作时触发的事件
,例如点击按钮、输入文本、滚动页面等。这些事件可以通过 JavaScript 来捕获和处理,以实现交互效果和功能。
本文会比较全面地讲述浏览器的DOM事件,包含一些基本知识和一些面试中经常提问的问题的总结。
话不多说,直接开整。
事件级别
DOM 事件可以分为三个级别:
DOM0
级事件、DOM2
级事件和DOM3
级事件。
本章内容大家了解即可。
DOM0
DOM0 级事件
:也称为传统事件模型
,是最早期的事件处理方式。通过直接在 HTML 元素上添加事件处理函数来实现事件处理
.
例如在按钮元素上添加 onclick 属性。这种方式简单直接,但不够灵活,且无法添加多个事件处理函数
。
示例:
<button onclick="myFunction()">点击我</button>
DOM2
DOM2 级事件
:引入了事件流(event flow)
的概念,事件处理分为捕获阶段
(capturing phase)、目标阶段
(target phase)和冒泡阶段
(bubbling phase)。通过addEventListener() 方法
来添加事件监听器,可以同时添加多个事件处理函数,并且可以控制事件的传播和处理。
示例:
document.getElementById('myButton').addEventListener('click', myFunction);
DOM3
DOM3 级事件
:在 DOM2 级事件的基础上增加了更多的事件类型和方法
,包括鼠标滚轮事件、键盘事件、文本输入事件等
。同时引入了命名空间(namespace)的概念,可以为事件类型添加自定义命名空间,方便事件管理和移除。其中一个重要的特性是可以通过事件对象的 preventDefault() 方法和 stopPropagation() 方法来阻止事件的默认行为和停止事件传播
。
示例:
// 获取按钮元素
var button = document.getElementById('myButton');
// 添加点击事件监听器
button.addEventListener('click', function(event) {
// 阻止按钮的默认行为(阻止按钮提交表单)
event.preventDefault();
console.log('Button clicked!');
});
// 添加文档级点击事件监听器
document.addEventListener('click', function(event) {
// 阻止事件传播到文档级
event.stopPropagation();
console.log('Document clicked!');
});
总的来说,DOM2 级事件和 DOM3 级事件相对于 DOM0 级事件更加灵活和强大,推荐使用它们来处理事件
。同时,现代的 JavaScript 框架和库(如jQuery、React等)也提供了更便捷和高效的事件处理方式,可以根据实际需求选择合适的方式来处理 DOM 事件。
那DOM1
级事件呢?
实际上,DOM1 级事件并不是一个独立的事件级别
,而是指 W3C DOM 规范的第一级,它定义了 DOM 的基本结构和操作方式,包括节点、元素、属性、文档等的表示和访问方法。在 DOM1 级事件中,并没有定义具体的事件处理方式或方法,事件处理是在后续的 DOM2 级事件规范中引入的。因此,DOM1 级事件并不是一个独立的事件级别,而是 DOM 标准的起点
。
事件模型
DOM事件模型是描述浏览器中事件处理的一种机制
。DOM事件模型基于事件流(event flow)
的概念,事件流描述了事件从页面中的元素传播到文档根节点的过程
。
事件流(Event Flow)
:事件流描述了事件从事件目标传播到文档根节点的过程
。事件流分为捕获阶段(capturing phase)、目标阶段(target phase)和冒泡阶段(bubbling phase)。
捕获阶段
:事件从文档根节点向事件目标传播的阶段。目标阶段
:事件到达事件目标的阶段。冒泡阶段
:事件从事件目标向文档根节点传播的阶段。
更详细的流程,我们可以参考事件流的官网:
获取根元素 (<html>
标签)
怎么用js获取根元素 (<html>
标签)呢?
在 JavaScript 中,document.documentElement
是一个表示整个文档的根元素的属性。通常情况下,根元素是 <html>
标签。通过 document.documentElement
可以访问到整个文档的根元素。
var rootElement = document.documentElement;
一般来说,document.documentElement
是一个只读属性,用于获取根元素。您可以使用它来访问根元素的属性,如 document.documentElement.style
来访问根元素的样式属性。
请注意,document.documentElement
不是一个方法,而是一个属性,因此不需要在后面加括号。
事件捕获
DOM事件捕获是指事件传播的第一个阶段,在这个阶段中,事件从window开始向目标元素传播
。在DOM事件捕获阶段中,事件会从最外层的祖先节点逐级向下传播,直到达到目标元素。
在实际应用中,可以通过addEventListener方法的第三个参数
来指定事件处理程序在捕获阶段执行:
element.addEventListener('click', handleEvent, true);
事件捕获的用途可以包括拦截事件、进行事件委托等。在事件捕获阶段可以在祖先元素上监听事件,并在事件到达目标元素之前对事件进行处理或拦截。
如果要在DOM事件的捕获阶段触发事件处理程序,可以在使用addEventListener()
方法注册事件监听器时,将第三个参数设置为true
。示例如下:
const element = document.getElementById('myElement');
element.addEventListener('click', function() {
console.log('目标阶段 - 点击了元素');
}, false);
element.addEventListener('click', function() {
console.log('事件捕获阶段 - 点击了元素');
}, true);
在上述示例中,第一个事件处理程序将在事件冒泡阶段(即目标阶段)触发,而第二个事件处理程序将在事件捕获阶段触发。
总的来说,事件捕获是DOM事件传播的第一个阶段,通过设置事件监听器的第三个参数为true
可以开启事件捕获阶段。
事件冒泡
DOM事件冒泡
是DOM事件传播的另一个阶段,它是指事件从目标元素向最外层祖先元素传播的过程
。在事件冒泡阶段,事件会从目标元素开始,逐级向上冒泡至最外层祖先元素。
如果要在DOM事件的冒泡阶段触发事件处理程序,可以在使用addEventListener()
方法注册事件监听器时,将第三个参数设置为false
(默认值)。示例如下:
const element = document.getElementById('myElement');
element.addEventListener('click', function() {
console.log('事件冒泡阶段 - 点击了元素');
}, false);
element.addEventListener('click', function() {
console.log('目标阶段 - 点击了元素');
}, true);
在上述示例中,第一个事件处理程序将在事件冒泡阶段触发,而第二个事件处理程序将在事件捕获阶段触发。
总的来说,事件冒泡是DOM事件传播的第二个阶段,通过设置事件监听器的第三个参数为
false(或不设置,默认为false)可以开启事件冒泡阶段。
如果希望阻止事件继续向上冒泡
,可以使用 event.stopPropagation()
方法。这样可以阻止事件传播到父元素或祖先元素,使得事件只在当前元素上触发。
element.addEventListener('click', function(event) {
event.stopPropagation(); // 阻止事件冒泡
});
总的来说,DOM 事件冒泡是一种事件传播机制,使得事件可以在整个元素树中传播,方便事件处理和事件代理。
需要注意的是,调用stopPropagation()
方法只会阻止事件继续向上冒泡,而不会影响事件的默认行为。如果要同时阻止事件的默认行为,可以使用事件对象的preventDefault()
方法。
Event对象常见的应用
event.preventDefault()
: 可以阻止事件的默认行为,比如阻止链接的跳转、表单的提交等。event.stopPropagation()
: 可以阻止事件在DOM树中的传播,即阻止事件冒泡。event.stopImmediatePropagation()
: 除了阻止事件冒泡外,还可以阻止其他事件处理程序被调用。event.target
: 返回触发事件的元素,即事件最初发生的元素。event.currentTarget
: 返回绑定事件处理程序的元素,即事件当前正在处理的元素。
event.preventDefault()
event.preventDefault()
是事件对象中的一个方法,用于阻止事件的默认行为。
在前端开发中,有时候我们需要在特定的事件触发时阻止浏览器执行默认的行为,比如点击链接时阻止跳转、提交表单时阻止刷新页面等。
例如,在一个点击链接的事件处理函数中,可以使用 event.preventDefault()
来阻止链接的默认跳转行为:
document.querySelector('a').addEventListener('click', function(event) {
event.preventDefault(); // 阻止链接的默认跳转行为
// 然后可以在这里添加自定义的处理逻辑
});
在上面的例子中,当用户点击链接时,事件处理函数首先调用了 event.preventDefault()
方法,从而阻止了链接的默认跳转行为,接着可以根据需求添加自定义的处理逻辑。
总之,event.preventDefault()
是一个常用的方法,用于在事件触发时阻止浏览器执行默认的行为,让开发者可以更灵活地控制事件的处理逻辑。
event.stopPropagation() & event.stopImmediatePropagation()
event.stopPropagation()
和event.stopImmediatePropagation()
都是用于控制事件传播的方法,它们可以在事件处理函数中调用来影响事件的传播行为。
event.stopPropagation()
: 这个方法用于阻止事件继续向上冒泡传播,即阻止事件传播到父元素
。
如果在子元素的事件处理函数中调用了 event.stopPropagation()
,则父元素上绑定的相同事件类型的处理函数不会再被触发。
parentElement.addEventListener('click', function(event) {
console.log('父元素被点击');
});
childElement.addEventListener('click', function(event) {
event.stopPropagation(); // 阻止事件冒泡
console.log('子元素被点击');
});
在上面的例子中,当点击子元素时,子元素的事件处理函数会被触发,并且调用了 event.stopPropagation()
方法,阻止了事件向父元素的传播。因此,父元素上的点击事件处理函数不会被执行。
event.stopImmediatePropagation()
: 这个方法与event.stopPropagation()
类似,都是用于阻止事件传播,但stopImmediatePropagation()
不仅停止事件向上冒泡,还会阻止其他事件处理函数的执行
。
element.addEventListener('click', function(event) {
event.stopImmediatePropagation(); // 阻止事件传播并阻止其他事件处理函数的执行
console.log('第一个事件处理函数');
});
element.addEventListener('click', function(event) {
console.log('第二个事件处理函数');
});
在上面的例子中,当点击元素时,第一个事件处理函数中调用了 event.stopImmediatePropagation()
方法,导致第二个事件处理函数不会被执行。
总之,event.stopPropagation()
和 event.stopImmediatePropagation()
都是用于控制事件传播的方法,开发者可以根据需要灵活地使用它们来管理事件的传播行为和处理逻辑。
event.target & event.currentTarget
event.target
和 event.currentTarget
都是用于获取事件目标元素的属性。
event.target
:这个属性返回触发事件的目标元素,即实际触发事件的元素
。无论事件经过多少层嵌套,event.target
总是指向最内层的元素,即事件最初发生的元素。
<div id="outer">
<p id="inner">Click me!</p>
</div>
<script>
document.getElementById('outer').addEventListener('click', function(event) {
console.log(event.target.id); // 输出 inner
});
</script>
在上面的例子中,当点击 <p>
元素时,事件会冒泡到父元素 <div>
上,但是 event.target
仍然指向 <p>
元素,因为它是真正触发事件的元素。
event.currentTarget
:这个属性返回绑定事件处理函数的元素,即当前正在处理事件的元素
。无论事件是在当前元素上触发还是在子元素上冒泡上来,event.currentTarget
总是指向绑定了事件处理函数的元素。
<div id="outer">
<p id="inner">Click me!</p>
</div>
<script>
document.getElementById('outer').addEventListener('click', function(event) {
console.log(event.currentTarget.id); // 输出 outer
});
</script>
在上面的例子中,无论点击 <p>
元素还是 <div>
元素,事件处理函数中的 event.currentTarget
都指向 <div>
元素,因为事件处理函数是绑定在 <div>
元素上的。
总之,event.target
和 event.currentTarget
都是事件对象中用于获取元素信息的属性,开发者可以根据需要灵活地使用它们来操作事件的目标元素和绑定事件处理函数的元素。
自定义事件
在DOM事件中,我们可以使用
new Event()
和new CustomEvent()
来创建自定义事件。
这两种方法的区别在于
new CustomEvent()
允许我们传递一些额外的信息(比如自定义数据)
,而new Event()
只能创建一个简单的事件对象。
使用 new Event()
创建自定义事件:
var event = new Event('customEvent');
使用 new CustomEvent()
创建自定义事件,并传递额外的信息:
var eventData = { detail: '自定义事件的额外信息'};
var customEvent = new CustomEvent('customEvent', eventData);
在上面的例子中,new Event()
创建了一个名为'customEvent'的简单自定义事件。而 new CustomEvent()
则创建了一个名为'customEvent'的自定义事件,并且通过 eventData
传递了额外的信息。
当需要传递额外的信息给事件处理函数时,通常会选择使用 new CustomEvent()
,因为它允许我们传递更多的数据,而不仅仅是事件的类型。例如:
document.addEventListener('customEvent', function(e) {
console.log('自定义事件被触发了!附加信息为:' + e.detail);
});
在这个例子中,事件处理函数可以通过 e.detail
获取到传递的额外信息。
总的来说,new Event()
和 new CustomEvent()
都可以用来创建自定义事件,但如果需要传递额外的信息,建议使用 new CustomEvent()
。
事件委托(事件代理)
事件委托(也称为事件代理)是一种常见的优化技术,
通过将事件处理程序绑定到父元素而不是每个子元素上,来提高性能和代码的简洁性
。当子元素触发事件时,事件会冒泡到父元素,然后可以通过事件对象中的target
属性来确定实际触发事件的子元素,从而执行相应的操作。
事件委托的原理是利用事件冒泡机制,将事件处理程序绑定到父元素上,然后通过判断 event.target
来确定实际触发事件的元素,并执行相应的操作。
下面是一个简单的示例来说明事件委托的原理:
<ul id="parentList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
<script>
const parentList = document.getElementById('parentList');
parentList.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
console.log('Clicked on:', event.target.textContent);
}
});
</script>
在上面的示例中,我们将 click 事件处理程序绑定到父元素 parentList
上。当用户点击列表中的任何一个子元素时,事件会冒泡到父元素,然后通过 event.target
属性来确定实际触发事件的子元素是哪一个,从而打印出相应的消息。
事件委托的优点包括:
简化代码
:减少了事件处理程序的数量,更容易维护和管理。提高性能
:减少了大量重复绑定事件处理程序的开销。对动态添加的元素生效
:无需重新绑定事件处理程序,新添加的元素也能受到事件委托的管理。
总的来说,事件委托是一种优化技术,可以提高性能和简化代码逻辑,特别适用于处理大量子元素的情况。
转载自:https://juejin.cn/post/7376860165323587636