前端面试必会DOM事件流、冒泡 、 捕获 、 委托
日期:2020-05-15
来源:程序思维浏览:2601次
(一)DOM事件流
① 定义
DOM事件流包括三个阶段:
捕获阶段
目标阶段
冒泡阶段
首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个是冒泡阶段,在这个阶段对事件做出响应
事件发生时会在元素节点之间按照特定的顺序传播,这个传播的过程就是DOM事件流
简单的说:事件的传播过程即DOM事件流
② 图解
③ 历史
当浏览器发展到第四代时(IE4及Netscape Communicator4),浏览器开发团队遇到了一个很有意思的问题:页面的哪一部分会拥有某个特定的事件?要明白这个问题问的是什么,可以想象画在一张纸上的一组同心圆。如果你把手指放在圆心上,那么你的手指指向的不是一个圆,而是纸上所有的圆,两家公司的浏览器开发团队在看待浏览器事件方面还是一致的。如果你单击了某个按钮,他们都认为单击事件不仅仅发生在按钮上。换句话说,在单机按钮的同时,你也单击了按钮的容器元素,甚至也单击了整个页面。
事件流描述的是从页面中接受事件的顺序。但有意思的是,IE 和 Netscape 开发团队居然提出了差不多完全相反的概念,IE的事件流是冒泡流,而Netscape Communicator的事件流是事件捕获流。
(二)冒泡
我给分别给div、section、body、html、window设置了点击事件,然后只点击了div,现在我们来分析一下此时的事件传播过程
首先,进入捕获阶段,实际的目标元素(div)在捕获阶段不会接收到事件。这意味着在捕获阶段,事件从window → html → body → section就停止了。下一个阶段是目标阶段,于是事件在div上发生,并在事件处理中被看成冒泡的一部分。然后事件冒泡发生,事件又传播回window。所以我们看到了下述的事件执行顺序。
事件的执行顺序是:
小 → 大
div → section → body → html → window
下面贴上代码(css部分就不贴了,问题不大)
html部分
<body>
<p>body</p>
<section>
<p>父盒子</p>
<div>子盒子</div>
</section>
</body>
JS部分
<script>
var oSection = document.querySelector("section");
var oDiv = document.querySelector("div");
var oHtml = document.documentElement;
var oBody = document.body;
oDiv.addEventListener("click",function(){
alert("我是子盒子")
})
oSection.addEventListener("click",function(){
alert("我是父盒子")
})
oHtml.addEventListener("click",function(){
alert("我是html")
})
oBody.addEventListener("click",function(){
alert("我是body")
})
window.addEventListener("click",function(){
alert("我是window")
})
</script>
(三)捕获
事件捕获如何触发呢?
这时需要用到 addEventListener( )方法的第三个参数(默认为false,即冒泡阶段),将第三个参数改为true,表示将此事件改为捕获阶段。
接下来我们看一下事件在捕获阶段的触发顺序:
根据上面的代码,我们先把父盒子section的点击事件变为捕获阶段
oSection.addEventListener("click",function(){
alert("我是父盒子")
},true)
首先,进入捕获阶段,实际的目标元素(div)在捕获阶段不会接收到事件。但我们把section的点击事件变为了捕获阶段发生,section的点击事件被捕获了,所以最先触发。 下一个阶段是目标阶段,于是事件在div上发生,并在事件处理中被看成冒泡的一部分。然后事件冒泡发生,事件又传播回window。所以我们才看到了下述的事件执行顺序。
此时的执行顺序是:
section → div → body → html → window
现在,我们将所有的事件都变成捕获阶段,看看效果:
此时的执行顺序是:
window → html → body → section → div
与冒泡阶段的执行顺序完全相反。
(四)在不同的阶段执行事件
多数支持DOM事件流的浏览器都实现了一种特定的行为:即使 “DOM2级事件” 规范明确要求捕获阶段不会涉及事件目标,但IE9,Safari,Chrome,Firefox 和 Opera9.5 以及更高版本都会在事件捕获阶段触发事件对象上的事件。结果就是有两个机会在目标对象上操作事件。
下面通过实例实践一下:
首先,让所有的事件在冒泡阶段才触发,然后给section绑定两个点击事件,一个在捕获阶段,一个在冒泡阶段
oSection.addEventListener("click",function(){
alert("我是父盒子,我在捕获阶段被触发了")
},true)//捕获阶段
oSection.addEventListener("click",function(){
alert("我是父盒子,我在冒泡阶段被触发了")
})
看看效果:
可以看到,section的两个点击事件都触发了,一个在捕获阶段,一个在冒泡阶段。
(五)提高内存和性能 – 事件委托
对 “事件处理程序过多” 问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一项类型的所有事件。例如,click事件会一直冒泡到window层次。也就是说,我们可以为整个页面指定一个onclick事件,而不必给每个可单击的元素分别添加事件
下面看看效果和代码:
① 实例
<script>
window.addEventListener("click", function (e) {
switch (e.target.className) {//我事先给每个元素设定了一个专属class
case "html":
alert("我是html");
break;
case "body":
alert("我是body");
break;
case "section":
alert("我是父盒子");
break;
case "div":
alert("我是子盒子");
break;
}
})
</script>
上述代码中,我们使用事件委托只为window添加了一个onclick事件处理函数,由于所有的元素都是window的子节点,而且它们的事件会冒泡,所以单击事件最终会被这个函数处理,然后通过检测属性的className来检测是哪个元素被点击,从而做出不同的动作。
上述代码中还用到了event事件对象,target返回触发此事件的元素。也就是说,谁触发了window的点击事件,target就是谁。
② 优点
上述代码与前面未使用事件委托的代码比一比,会发现这段代码的事前消耗更低,因为不需要取得DOM元素(或者只需要取得一个DOM元素),只添加了一个事件处理程序。虽然对于用户来说最终的结果相同,但是这种技术需要占用的内存更少。所有用到按钮的事件(多鼠标事件和键盘事件)都适合采用事件委托技术。
这样做与采取传统的做法相比具有如下优点:
window对象很快就可以访问,只要可单击的元素呈现在页面上,就可以立即具备适当的功能
在页面中设置事件处理程序所需的事件更少。只添加一个事件处理程序所需的DOM引用更少,所花的时间也更少
整个页面占据的内存空间更少,能够提升整体性能。
① 定义
DOM事件流包括三个阶段:
捕获阶段
目标阶段
冒泡阶段
首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个是冒泡阶段,在这个阶段对事件做出响应
事件发生时会在元素节点之间按照特定的顺序传播,这个传播的过程就是DOM事件流
简单的说:事件的传播过程即DOM事件流
② 图解
③ 历史
当浏览器发展到第四代时(IE4及Netscape Communicator4),浏览器开发团队遇到了一个很有意思的问题:页面的哪一部分会拥有某个特定的事件?要明白这个问题问的是什么,可以想象画在一张纸上的一组同心圆。如果你把手指放在圆心上,那么你的手指指向的不是一个圆,而是纸上所有的圆,两家公司的浏览器开发团队在看待浏览器事件方面还是一致的。如果你单击了某个按钮,他们都认为单击事件不仅仅发生在按钮上。换句话说,在单机按钮的同时,你也单击了按钮的容器元素,甚至也单击了整个页面。
事件流描述的是从页面中接受事件的顺序。但有意思的是,IE 和 Netscape 开发团队居然提出了差不多完全相反的概念,IE的事件流是冒泡流,而Netscape Communicator的事件流是事件捕获流。
(二)冒泡
我给分别给div、section、body、html、window设置了点击事件,然后只点击了div,现在我们来分析一下此时的事件传播过程
首先,进入捕获阶段,实际的目标元素(div)在捕获阶段不会接收到事件。这意味着在捕获阶段,事件从window → html → body → section就停止了。下一个阶段是目标阶段,于是事件在div上发生,并在事件处理中被看成冒泡的一部分。然后事件冒泡发生,事件又传播回window。所以我们看到了下述的事件执行顺序。
事件的执行顺序是:
小 → 大
div → section → body → html → window
下面贴上代码(css部分就不贴了,问题不大)
html部分
<body>
<p>body</p>
<section>
<p>父盒子</p>
<div>子盒子</div>
</section>
</body>
JS部分
<script>
var oSection = document.querySelector("section");
var oDiv = document.querySelector("div");
var oHtml = document.documentElement;
var oBody = document.body;
oDiv.addEventListener("click",function(){
alert("我是子盒子")
})
oSection.addEventListener("click",function(){
alert("我是父盒子")
})
oHtml.addEventListener("click",function(){
alert("我是html")
})
oBody.addEventListener("click",function(){
alert("我是body")
})
window.addEventListener("click",function(){
alert("我是window")
})
</script>
(三)捕获
事件捕获如何触发呢?
这时需要用到 addEventListener( )方法的第三个参数(默认为false,即冒泡阶段),将第三个参数改为true,表示将此事件改为捕获阶段。
接下来我们看一下事件在捕获阶段的触发顺序:
根据上面的代码,我们先把父盒子section的点击事件变为捕获阶段
oSection.addEventListener("click",function(){
alert("我是父盒子")
},true)
首先,进入捕获阶段,实际的目标元素(div)在捕获阶段不会接收到事件。但我们把section的点击事件变为了捕获阶段发生,section的点击事件被捕获了,所以最先触发。 下一个阶段是目标阶段,于是事件在div上发生,并在事件处理中被看成冒泡的一部分。然后事件冒泡发生,事件又传播回window。所以我们才看到了下述的事件执行顺序。
此时的执行顺序是:
section → div → body → html → window
现在,我们将所有的事件都变成捕获阶段,看看效果:
此时的执行顺序是:
window → html → body → section → div
与冒泡阶段的执行顺序完全相反。
(四)在不同的阶段执行事件
多数支持DOM事件流的浏览器都实现了一种特定的行为:即使 “DOM2级事件” 规范明确要求捕获阶段不会涉及事件目标,但IE9,Safari,Chrome,Firefox 和 Opera9.5 以及更高版本都会在事件捕获阶段触发事件对象上的事件。结果就是有两个机会在目标对象上操作事件。
下面通过实例实践一下:
首先,让所有的事件在冒泡阶段才触发,然后给section绑定两个点击事件,一个在捕获阶段,一个在冒泡阶段
oSection.addEventListener("click",function(){
alert("我是父盒子,我在捕获阶段被触发了")
},true)//捕获阶段
oSection.addEventListener("click",function(){
alert("我是父盒子,我在冒泡阶段被触发了")
})
看看效果:
可以看到,section的两个点击事件都触发了,一个在捕获阶段,一个在冒泡阶段。
(五)提高内存和性能 – 事件委托
对 “事件处理程序过多” 问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一项类型的所有事件。例如,click事件会一直冒泡到window层次。也就是说,我们可以为整个页面指定一个onclick事件,而不必给每个可单击的元素分别添加事件
下面看看效果和代码:
① 实例
<script>
window.addEventListener("click", function (e) {
switch (e.target.className) {//我事先给每个元素设定了一个专属class
case "html":
alert("我是html");
break;
case "body":
alert("我是body");
break;
case "section":
alert("我是父盒子");
break;
case "div":
alert("我是子盒子");
break;
}
})
</script>
上述代码中,我们使用事件委托只为window添加了一个onclick事件处理函数,由于所有的元素都是window的子节点,而且它们的事件会冒泡,所以单击事件最终会被这个函数处理,然后通过检测属性的className来检测是哪个元素被点击,从而做出不同的动作。
上述代码中还用到了event事件对象,target返回触发此事件的元素。也就是说,谁触发了window的点击事件,target就是谁。
② 优点
上述代码与前面未使用事件委托的代码比一比,会发现这段代码的事前消耗更低,因为不需要取得DOM元素(或者只需要取得一个DOM元素),只添加了一个事件处理程序。虽然对于用户来说最终的结果相同,但是这种技术需要占用的内存更少。所有用到按钮的事件(多鼠标事件和键盘事件)都适合采用事件委托技术。
这样做与采取传统的做法相比具有如下优点:
window对象很快就可以访问,只要可单击的元素呈现在页面上,就可以立即具备适当的功能
在页面中设置事件处理程序所需的事件更少。只添加一个事件处理程序所需的DOM引用更少,所花的时间也更少
整个页面占据的内存空间更少,能够提升整体性能。
- 上一篇:php发送post请求方法大全附源码
- 下一篇:深入理解CSS权重和计算面试必会
精品好课