浏览器的事件传播机制,捕获事件与冒泡事件的区别,js注册事件监听与移除事件监听,事件代理的优点
浏览器中事件触发分三个阶段
捕获:从window往事件触发处进行传播,遇到注册的捕获事件会被触发
目标:传播到事件最内层触发处时,执行其上绑定的注册事件
冒泡:从事件触发处往window传播,遇到注册的冒泡事件会被触发
我们可以通过addEventListener进行事件监听,第三个参数代表是否使用捕获事件useCapture,默认为false,即默认监听冒泡事件
<!DOCTYPE html> <html lang="en"> <body> <div id="root"> <div id="sub"></div> </div> <script> let root = document.getElementById('root') let sub = document.getElementById('sub') window.addEventListener('click',function () { console.log('window propagation') },false) window.addEventListener('click',function () { console.log('window capture') },true) document.addEventListener('click',function () { console.log('document propagation') },false) document.addEventListener('click',function () { console.log('document capture') },true) document.body.addEventListener('click',function () { console.log('document body propagation') },false) document.body.addEventListener('click',function () { console.log('document body capture') },true) root.addEventListener('click',function () { console.log('root propagation') },false) root.addEventListener('click',function () { console.log('root capture') },true) sub.addEventListener('click',function () { console.log('sub capture') },true) sub.addEventListener('click',function (e) { console.log('sub propagation') },false) </script> </body> </html>
点击sub,我们可以看到结果如下,window最先获取捕获事件,一直传递到sub,然后sub冒泡一直再传递到window。
window capture document capture document body capture root capture sub capture sub propagation root propagation document body propagation document propagation window propagation
需要注意的一点是,最内层sub是先执行绑定的捕获事件还是执行绑定的冒泡事件,取决于代码的顺序,先监听的哪个就执行哪个。
//如果sub先注册冒泡事件,则会先触发冒泡事件,这个特性仅限最内层被点击的元素 sub.addEventListener('click',function (e) { console.log('sub propagation') },false) sub.addEventListener('click',function () { console.log('sub capture') },true)
除了采取addEventListener来监听元素的事件,我们还可以采用类似onClick这种来监听,两者区别是:
onclick事件在同一时间只能指向唯一对象
addEventListener给一个事件注册多个listener
addEventListener对任何DOM都是有效的,而onclick仅限于HTML
addEventListener可以控制listener的触发阶段,(捕获/冒泡)。对于多个相同的事件处理器,不会重复触发,不需要手动使用removeEventListener清除
有时我们想取消事件的默认处理方式,比如type='submit'的按钮,点击时会自动提交表单,我们可以通过preventDefault来阻止该行为
sub.addEventListener('click',function (e) { e.preventDefault() console.log('sub propagation') },false)
如果我们不想让事件冒泡,则可以通过stopPropagation来阻止
root.addEventListener('click',function () { console.log('root propagation') },false) sub.addEventListener('click',function (e) { e.stopPropagation() //阻止后,点击sub不再执行root绑定的冒泡事件 console.log('sub propagation') },false)
除了stopPropagation,我们还可以使用stopImmediatePropagation来阻止事件冒泡,stopImmediatePropagation除了能阻止事件冒泡,还能阻止元素绑定的其他事件,如sub元素同时绑定了捕获和冒泡事件,我们在捕获阶段就给他stopImmediatePropagation,则不再执行冒泡事件。
sub.addEventListener('click',function (e) { e.stopImmediatePropagation() //该元素注册的其他事件则不再处理 console.log('sub propagation') },false) sub.addEventListener('click',function () { console.log('sub capture') },true)
取消事件监听我们可以采用removeEventListener,取消事件监听要求注册时不能使用匿名函数,否则没法取消,第三个参数为true,代表取消捕获事件,否则代表取消冒泡事件
let captureHandle = function () { console.log('sub capture') } sub.addEventListener('click',captureHandle,true) sub.removeEventListener('click',captureHandle,true)
假如我们要给列表页添加事件监听,如果列表页数量很多,那就要加载相应数量的监听函数,这样很浪费内存,而且如果列表内容是js动态添加的,每次添加都要再去单独添加监听,我们可以通过代理的方式把监听事件放到父元素上,通过e.target来获取目标元素。
<ul id="data"> <li>事件代理</li> <li>事件代理</li> <li>事件代理</li> <li>事件代理</li> <li>事件代理</li> </ul> <script> document.getElementById('data').addEventListener('click',function(e){ e.target.style.color='red' }) </script>
相对给每个元素添加事件监听,代理有以下好处
节省内存
对于动态添加的子元素,无需单独添加监听
可统一撤销监听事件
-END-
点赞(0)