每日一句
You're in control of your own life!
释义:你的人生你作主!
概念理解
- DOM 即 文档对象模型。
文档对象模型是一种与编程语言及平台无关的API(Application programming Interface),借助于它,程序能够动态地访问和修改文档内容、结构或显示样式。
- DOM0,DOM2,DOM3
W3C协会早在1988年就开始了DOM标准的制定,W3C DOM标准可以分为DOM1,DOM2,DOM3三个版本。 DOM1级主要定义的是HTML和XML文档的底层结构。
- DOM0怎么来的?
其实是没有的,叫的人多了就有了DOM0, 在1998 年 10 月 DOM1级规范成为 W3C 的推荐标准,在此之前的实现我们就习惯称为DOM0级,其实本是没有这个标准的。
DOM0就是直接通过 onclick写在html里面的事件;
DOM2是通过addEventListener绑定的事件, 还有IE下的DOM2事件通过attachEvent绑定;
DOM3是一些新的事件;
DOM2级和DOM3级的目的在于扩展DOM API,以满足操作XML的所有需求,同时提供更好的错误处理及特性检测能力。
事件流描述的是从页面中接受事件的顺序。
IE和Netscape开发团队居然提出了两个截然相反的事件流概念。
-
IE的事件流是事件冒泡流
-
Netscape的事件流是事件捕获流
而addEventLister的第三个参数时支持冒泡与捕获
以下摘自[MDN]:
target.addEventListener(type, listener, options);
target.addEventListener(type, listener, useCapture); // 捕获true,冒泡false 默认为false.
target.addEventListener(type, listener, useCapture, wantsUntrusted ); // Gecko/Mozilla onlytrue, 则事件处理程序会接收网页自定义的事件。此参数只适用于 Gecko(chrome的默认值为true,其他常规网页的默认值为false),主要用于附加组件的代码和浏览器本身
事件冒泡
事件的传播为:从事件开始的具体元素,一层层往上传播,直到window对象。
现代浏览器都支持事件冒泡,IE9、Firefox、Chrome和Safari则将事件一直冒泡到window对象。
事件捕获
事件的传播为:即从上至下,从document逐级向下传播到目标元素。
IE9、Firefox、Chrome和Safari目前也支持这种事件流模型,但是有些老版本的浏览器不支持,所以很少人使用事件捕获,而是用事件冒泡的多一点。
事件委托(代理)
传统的事件处理中,需要为每个元素添加事件处理器。js事件委托(代理)则是一种简单有效的技巧,通过它可以把事件处理器添加到一个父级元素上,从而避免把事件处理器添加到多个子级元素上。
每个函数都是对象,都会占用内存,内存中的对象越多,性能就越差。对事件处理程序过多问题的解决方案就是事件委托。
事件委托利用事件冒泡,只指定一个事件处理程序即可,就可以管理某一个类型的所有事件。
使用场景
如果你想要在大量子元素(包括动态添加的)中单击任何一个就可以运行一段代码,这个时候可以把事件监听器设置在父节点上。
实现方式 jquery 中的 on
jquery在1.7的版本之后,最流行的事件监听方法是
$(元素).on(事件名,执行函数),它还有一种事件委托的写法$(委托给哪个元素).on(事件名,被委托的元素,执行函数)
手写事件委托demo
//错误版(bug在于如果用户点击的是li里边的span,就没法触发fn)
ul.addEventListener('click', function(e)){
if(e.target.tagName.toLowerCase() === 'li'){
fn();
}
}
//高级版
function delegate(element,eventType,selector,fn){
element.addEventListener(eventType, e=>{
let el = e.target
while(!el.matches(selector)){
if(element === el){
el = null;
break;
}
el = el.parentNode
}
el && fn.call(el,e,el)
})
}
优点:1.减少内存消耗; 2.可动态绑定事件(可以监听动态元素或不存在的元素)
局限性:
-
focus、blur本身没有冒泡机制,就不能委托;
-
mousemove、mouseout 虽然有事件冒泡,但是只能不断通过位置去计算定位,对性能消耗高,因此也是不适合于事件委托的;
小结:
-
适合用事件委托的事件:
click,mousedown,mouseup,keydown,keyup,keypress。 -
Js中事件是否支持冒泡
对于
focus,blur,change,submit,reset,select等不会冒泡的事件,在标准游览器中,我们可以设置addEventListener的最后一个参数为true轻松搞定。
IE就有点麻烦,要用focusin代替focus,focusout代替blur,selectstart代替select。
change,submit与reset就复杂了,必须利用其他事件来模拟,还要判断事件源的类型,selectedIndex,keyCode等等,jQuery有插件用很复杂的方式来实现……
onselect事件发生在mouseup事件之后,而onselectstart 事件发生在mousedown并mousemove事件之后。
如果元素被阻止冒泡了,千万别去用事件委托的方式监听事件,因为事件委托的原理是利用事件冒泡,当冒泡被阻止,就无法监听了。
冒泡,捕获,委托三者关系
事件捕获和冒泡是现代浏览器的执行事件的两个不同阶段
事件委托是利用冒泡阶段的运行机制来实现的
所有冒泡皆可取消,默认动作有的可以取消有的不能取消。
- 滚动事件scroll event
如果真想要阻止滚动,可以阻止wheel和touchstart的默认动作。
注意:你还有找准滚动条所在的元素,但是滚动条还能用,可以css让滚动条display: none;或者overflow: hidden可以直接取消滚动条,但此时JS仍然可以修改scrollTop。
Cancelable是用来取消(也可以说是阻止)默认动作的。
事件绑定的两种方法
-
DOM0级事件绑定
elmemnt.onclick=function(){} -
DOM2级事件绑定
- 标准浏览器:
curEle.addEventListener('click',function(){},false)- IE6-8:
curEle.attachEvent('onclick',function(){})
DOM2事件流三个阶段:捕获阶段,目标阶段,冒泡阶段。
如何阻止冒泡事件
- 在支持addEventListener()的浏览器中,可以调用事件对象的stopPropagation()方法以阻止事件的继续传播。如果在同一对象上定义了其他处理程序,剩下的处理程序将依旧被调用,但调用stopPropagation()之后任何其他对象上的事件处理程序将不会被调用。不仅可以阻止事件在冒泡阶段的传播,还能阻止事件在捕获阶段的传播。
event.stopPropagation()
- IE9之前的IE不支持stopPropagation()方法,而是设置事件对象cancelBubble属性为true来实现阻止事件进一步传播。
e.cancelBubble = true
取消默认事件
w3c的方法是e.preventDefault(),IE则是使用e.returnValue = false;
javascript的return false只会阻止默认行为,而是用[jQuery]的话则既阻止默认行为又防止对象冒泡。
JS支持事件吗
不支持,DOM事件不属于JS的功能,属于浏览器提供的DOM的功能。
JS只是调用了DOM提供的addEventListener而已。
事件捕获和冒泡的区别
执行顺序不同,触发顺序是:先捕获,后冒泡
// css
#parent {
width: 300px;
height: 150px;
padding-top: 25px;
text-align: center;
color:#fff;
background-color: blue;
}
#child {
width: 200px;
height: 100px;
margin: 0 auto;
line-height: 100px;
background-color: red;
}
// js
parent.addEventListener('click', () => {
console.log('冒泡parent')
})
child.addEventListener('click', () => {
console.log('冒泡child')
})
parent.addEventListener('click', () => {
console.log('捕获parent')
},true)
child.addEventListener('click', () => {
console.log('捕获child')
}, true)
- 点击父级元素,先执行捕获,再执行冒泡
- 点击子元素,先捕获父级-子级,再执行冒泡子级-父级
- 点击空白区域时,跟点击父级元素一样,这怎么理解呢?
哈哈。。。这里的parent指向了window,没有按标准写法来。