上一次学习了DOM 的事件,理解了冒泡事件和捕获事件,触发的机制,今天学习一下具体的应用场景,或者说在哪个地方容易踩坑。
做一个小demo,点击按钮出现浮层,点击其它地方关闭浮层,写一个简单css
<style>
.wrapper{
position:relative;
display:inline-block;
}
.popover{
position:absolute;
border:1px solid red;
left:100%;
top:0;
padding:10px;
margin-left:10px;
background:white;
display: none; /*默认隐藏*/
}
.popover::before{
position:absolute;
content:'';
top:5px;
right:100%;
border:10px solid transparent;
border-right-color:red;
}
.popover::after{
position:absolute;
content:'';
top:5px;
right:100%;
border:10px solid transparent;
border-right-color:white;
margin-right:-1px;
}
</style>
<div id="wrapper" class='wrapper'>
<button id="clickMe">点我</button>
<div id="popover" class="popover">
<input type="checkbox">浮层
</div>
</div>
<script>
clickMe.addEventListener('click',function(){
popover.style.display = 'block';
});
</script>
那现在我要点击页面空白地方关闭呢?该用什么方法,很容易想到监听文档,如下代码
document.addEventListener('click',function(){
popover.stely.display = 'none';
});
但是实际上这样写了之后,按钮都失效了,怎么点都没有反应。这是为什么呢?
理解上一篇讲的捕获和冒泡事件后就很好理解这点了,可以DOM事件之捕获、冒泡。
我们没有指定监听是在捕获还是冒泡阶段,浏览器默认是冒泡阶段,当我们点击按钮时,捕获阶段没有发生什么时候,但是冒泡阶段就不一样了,首先button
上函数先触发,然后document
上函数也触发了,导致准备出现的浮层又被隐藏了。
那你可能要问,button
上的事件执行了没?其实这两个事件都执行了,只是时间太短,浏览器默认一起执行了,可以在里面加一个debugger
,就可以看到了。
clickMe.addEventListener('click',function(){
popover.style.display = 'block';
});
那该怎么解决呢?最简单的方法是,除了要执行popover.style.display = 'block'
,还要阻止事件传播
clickMe.addEventlistener('click',function(){
popover.style.display = 'block';
});
popover.addEventListener('click',function(e){
e.stopPropagation();
});
这里为什么添加在按钮的父元素上面呢?如果不添加在父元素上面,点击浮层的时候,浮层也会被关闭。
如果页面上有很多监听器的话,这个方法是比较浪费内存的,比较省内存的方法用JQuery 做
$(clickMe).on('click',function(){
$(popover).show();
$(document).one('click',function(){
$(popover).hide();
});
});
$(wrapper).on('click',function(e){
e.stopPropagation();
})
一开始不监听,只在popover
`show`的时候监听一次,马上关掉,这叫做清理战场。$(wrapper).on('click',false)
和下面的代码完全等价
$(wrapper).on('click',function(e){
e.preventDefault(); //阻止默认事件
e.stopPropagation(); //阻止传播
})
但是如果页面中有checkbox
,你在它的父元素任何一层,包括checkbox
自己,添加了组织默认事件那么这个checkbox
就没办法被check
。
这里有个问题,如果没有阻止事件传播,向下面这样,会发生什么事情呢?
$(clickMe).on('click',function(){
$(popover).show();
$(document).one('click',funtion(){
$(popover).hide();
});
});
当然了,和之前一样,什么事情也不会发生,那当我点击按钮之后里面都发生了那些事情呢?
当我点击了按钮之后,它会做两件事情,首先把popover
`show出来,然后把
hide函数添加到
document上面,当事件传播到
document`,就会又把它给隐藏了。
可以给它添加一个setTimeout()
函数来解决这个问题
$(clickMe).on('click',function(){
$(popover).show();
setTimeout(function(){
$(document).one('click',function(){
$(popover).hide();
})
},0)
});
setTimeout(fn,0)
这个0
不是马上执行,而是尽快执行,具体是在冒泡结束在执行这里的函数,也就是说,当冒泡结束后,在把监听事件添加到document
上面,等待用户下次点击在执行。
<style>
.red.active{
background:red;
}
.blue.active{
background:blue;
}
.green.active{
background:green;
}
.yellow.active{
background:yellow;
}
.orange.active{
background:orange;
}
.purple.active{
background:purple;
}
div{
border:1px solid black;
display:flex;
flex:1;
padding:20px;
transition:1s;
border-radius:100%;
}
*{
padding:0;
margin:0;
box-sizing:border-box;
}
.red{
height:300px;
width:300px;
}
</style>
<div class="red">
<div class="blue">
<div class="green">
<div class="yellow">
<div class="orange">
<div class="purple"></div>
</div>
</div>
</div>
</div>
</div>
<script>
var n = 1
$('div').on('click',function(e){
setTimeout(function(){
$(e.currentTarget).addClass('active')
},n*500)
n+=1
})
</script>
总结:
- 同时监听
button
和document
,点啥都没反应,因为两个函数都执行了,用阻止事件传播解决了,比较浪费内存 - 好一定的方法是用jQuery 做,点击
button
后在监听document
,关闭了就不再监听,不阻止事件传播,点啥也没反应,两种解决方法:一种是阻止事件传播,另一种是添加一个setTimeout()
函数。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。