事件的三个特点
- TOM (代码)订阅/关注/监听了 XXX(用户)
- XXX (用户)发生了变化
- TOM(代码)得到通知
捕获&冒泡
操作系统最先知道用户点击了鼠标,浏览器次之
child 被点击了,意味着 parent 也被点击了
如果同时监听了child和parent,那么
捕获阶段:先通知parent,后通知child
123456child.addEventListener('click',function(){console.log('child')},true)parent1.addEventListener('click',function(){console.log('parent1')},ture) //添加ture参数,表示捕获阶段冒泡阶段:先通知chlid,后通知parent
123456child.addEventListener('click',function(){console.log('child')},flase)parent1.addEventListener('click',function(){console.log('parent1')},flase) //默认为flase,冒泡阶段
W3C事件模型:支持两种阶段
12button.addEventListener('click',fn,true) //永远别用button.addEventListener('click',fn)
如何监听事件
当同一个对象使用.onclick的写法触发多个方法的时候,后一个方法会把前一个方法覆盖掉,也就是说,在对象的onclick事件发生时,只会执行最后绑定的方法。而用事件监听则不会有覆盖的现象,每个绑定的事件都会被执行。
- DOM level 0 事件: button.onclick = function(){}
- DOM level 2 事件: button.addEventListener(‘click’, function(){})
e/event
|
|
e.target&e.currentTarget
- target:触发事件的元素,即点击的元素
- currentEvent:被监听的元素
以下图片先点击绿色边框(parent),然后再点击红色区域(child)
阻止默认事件 e.preventDefault()
如果加在父元素上,对其子元素也有阻止事件的作用,所以尽量用在要阻止事件的具体子元素上,以免让其他事件失效(图中阻止了a链接的跳转作用,点击百度不会发生跳转)
停止冒泡e.stopPropagation()
propagation:传播的意思。 用户点击了子元素,不要向父元素通知。
事件委托
事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。简而言之就是委托它们的父级代为执行事件。
没有时间委托,逐个监听事件
监听是占用内存的,既然4个
li
做了相同的事情,那么直接监听父元素,同样点击li
元素可以实现。(但是有一个问题就是父元素里面的所有内容都会触发事件,包括点击父元素的padding。)那么添加一个if条件
e.target.nodeName === 'LI'
,让触发事件的目标在元素LI
上,此时解决了上面li
元素外和父容器之间也会触发事件的问题。(但是又引申出了一个新的问题,就是点击LI
的后代元素无法触发事件。这和初衷不相符。)再修改一下if条件,
e.target.nodeName ==='LI'||e.target.parentNode.nodeName === 'LI'
, 让LI
的span元素也能触发事件,此时的委托事件正好满足这个例子。(但是如果li有多个后代元素,又无法满足条件了。)再完善一下,我要点击后代元素触发事件,要判断其父元素是不是
LI
,如果不是,继续网上找,直到找到li
。(但这样会出现找不到LI
的情况,一直网上找到document,再网上就返回null了。)继续完善,网上找的过程中,如果一直到监听的哪一层元素还没找到,那就停止,返回null。
123456789101112131415161718//较为完整的事件委托代码var ul = document.querySelector('ul')function f(e){let el = e.targetwhile(el.nodeName !== 'LI'){if(el === ul){el = nullbreak;}el = el.parentNode}if(el){console.log('ok')}else {console.log('no')}}ul.addEventListener('click',f)