DOM leave 0:
- xxx.onclick = function(){ ... }
onclick="xxx()" == eval(xxx())
evalonclick是属性,具有唯一性
DOM leave 2:
xxx.onclick = function() {}只能给元素绑定一个onclick事件
xxx.addEventListener('click', function(){ ... }) 可以绑定多个事件,可看作队列(先进先出)。
xxx.removeEventListener('click', function() {}) 取消click队列。
删除事件队列:
function f1(){
console.log(1)
}
function f2(){
console.log(2)
}
function f3(){
console.log(3)
}
xxx.addEventListener('click', f1)
xxx.addEventListener('click', f2)
xxx.removeEventListener('click', f1)
xxx.addEventListener('click', f3)
xxx.removeEventListener('click', f3)
只有一次事件监听的按钮:
function f1(){
console.log(1)
xxx.removeEventListener('click', f1)
}
xxx.addEventListener('click', f1)
事件捕获与冒泡
html:
<div id="grand1">
爷爷
<div id="parent1">
父亲
<div id="child1">
儿子
</div>
</div>
</div>
js:
//1 当我点击儿子的时候,是否点击了父亲和爷爷?
// yes1
//2 当我点击儿子的时候,三个函数是否调用
grand1.addEventListener('click', function fn1(){
console.log('爷爷')
})
parent1.addEventListener('click', function fn2(){
console.log('父亲')
})
child1.addEventListener('click', function fn3(){
console.log('儿子')
})
// yes2
//3 请问fn1 fn2 fn3 的执行顺序
// 123 or 321
// 321
//不传第三个参数/传 儿子爸爸爷爷
//第三个参数传true 爷爷爸爸儿子
DMO事件模型
先从上往下(捕获阶段),再从下往上(冒泡阶段)。
如果第三个参数传的是false(或者不传,默认false),走右边(冒泡)。儿子爸爸爷爷。
如果第三个参数传的是true,走左边(捕获)。爷爷爸爸儿子。
child1.addEventListener('click', function fn3(){
console.log('儿子2')
},true)
child1.addEventListener('click', function fn3(){
console.log('儿子1')
})
如果是在本身触发,不区分捕获冒泡,按照代码顺序执行。
DOM事件模型续...
如何做到点击按钮显示浮层,点击别处隐藏浮层?
<html>:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div class="wrapper" id="wrapper">
<button id="clickMe">点我</button>
<div class="popover" id="popover">
浮层
</div>
</div>
</body>
</html>
<css>:
body{
border: 1px solid red;
}
.wrapper{
position: relative;
display: inline-block;
}
.popover{
background: white;
border: 1px solid red;
position: absolute;
left: 100%;
top: 0;
white-space: nowrap;
padding: 10px;
margin-left: 10px;
display: none;
}
.popover::before{
position: absolute;
right: 100%;
top: 5px;
border: 10px solid transparent;
border-right-color: red;
content: '';
}
.popover::after{
position: absolute;
right: 100%;
top: 5px;
border: 10px solid transparent;
border-right-color: white;
content: '';
margin-right: -1px;
}
这是一个很丑很丑的浮层
<js>:
clickMe.addEventListener('click', function fn1(){
popover.style.display = 'block'
console.log('block')
})
document.addEventListener('click', function fn2(){
popover.style.display = 'none'
console.log('none')
})
ok,首先这个方法是行不通的,因为当我们点击button时,我们也相当于点击document,在冒泡阶段会执行到fn2,并且我们知道在冒泡阶段函数的执行过程是fn1-fn2,所以最后点击按钮是不会显示浮层的。
接下来我们让button阻止冒泡:
clickMe.addEventListener('click', function fn1(e){
popover.style.display = 'block'
console.log('block')
e.stopPropagation() //阻止冒泡向上传播
})
这段代码会在button处中断冒泡过程,导致document不能执行函数fn2,所以这样做可以达到我们所要的效果,但是这里有一个bug,如果我们给浮层加上一个checkbox:
<div class="popover" id="popover">
<input type="checkbox">
浮层
</div>
有checkbox的浮层
这个时候我们点击checkbox,会隐藏浮层,为什么?
因为button和浮层都是在wrapper中,为兄弟关系,botton并不会被点击,但浮层依然在document中,事件会通知到document,导致浮层隐藏,那么怎么解决呢?
很简单,我们在浮层和button的父元素wrapper上添加事件并删除button的阻止冒泡代码:
clickMe.addEventListener('click', function fn1(e){
popover.style.display = 'block'
console.log('block')
})
wrapper.addEventListener('click', function(e){
e.stopPropagation() //阻止冒泡向上传播
})
因为冒泡阶段是从小到大,会经过父元素wrapper,我们只需要在wrapper上中断冒泡过程就ok了。
此时再点击checkbox就不会导致浮层隐藏。
但是这种方法会浪费内存,因为document和popover相对监听,如果有很多很多popover,那么document上就要做对应个数的监听,非常浪费内存,所以这个版本不太实用。
更新版:
$(clickMe).on('click',function () {
$(popover).show();
});
$(wrapper).on('click',false);
$(document).on('click',function () {
$(popover).hide();
});
请注意,这是个错误的示范,false会阻止所有默认事件以及冒泡,导致checkbox无法点击,并且依然没有改善内存问题。
再次更新:
$(clickMe).on('click',function(){
console.log('被点击了')
$(popover).show()
//每次点击button,document只监听一次click事件,关闭浮层后不再监听,这样就做到了节省内存
$(document).one('click',function(){
$(popover).hide()
})
})
$(wrapper).on('click', function(e){
e.stopPropagation() //阻止冒泡向上传播
})
再次更新版:点击按钮显示浮层和关闭浮层。
$(clickMe).on('click',function(){
if($(popover).css('display') === 'none'){
$(popover).show()
}else{
$(popover).hide()
}
$(document).one('click',function(){
$(popover).hide()
})
})
$(wrapper).on('click', function(e){
e.stopPropagation() //阻止冒泡
})
面试题:如果没有阻止冒泡,点击button不会出现浮层,为什么呢?
答:在冒泡阶段,执行clickMe的函数,执行完$(popover).show()之后就为document添加监听事件,导致document拥有函数,这个过程是在冒泡结束之前进行的,所以当clickMe的函数执行完之后,继续执行document的监听时间函数。
网友评论