今天整理大红书上第13章事件,发现很多以前没注意到的东西,特此整理下来:
坑1:移除事件监听
我们知道addEventListener所添加的事件处理程序只能通过removeEventListener来移除,但是两次的参数必须一致,所以如果第二个参数是匿名函数就永远无法移除了:
var btn = document.getElementById("myBtn");
btn.addEventListener("click",function(){
alert(this.id);
},false);
btn.removeEventListener("click",function(){
alert(this.id);
},false);//无效
此时这个事件监听并没有被移除,因为这两个匿名函数其实是完全不同的两个。
如果想要移除可以写成:
var btn = document.getElementById("myBtn");
function alertId(){
alert(this.id);
}
btn.addEventListener("click",alertId,false);
btn.removeEventListener("click",alertId,false);//有效
坑2:执行顺序的不同
可以添加多个事件处理程序:
var btn = document.getElementById("myBtn");
btn.addEventLisrener("onclick",function(){
alert("Clicked");
});//先触发
btn.addEventLisrener("onclick",function(){
alert("又Clicked");
});//后触发
但是IE情况下截然相反:
var btn = document.getElementById("myBtn");
btn.attchEvent("onclick",function(){
alert("Clicked");
});//后触发
btn.attchEvent("onclick",function(){
alert("又Clicked");
});//先触发
支持IE事件处理程序的浏览器有IE和Opera
补充一下:ie事件处理程序的this指向的是window,DOM事件流中this永远等于currentTarget。
3.结合事件委托来看事件对象的属性
事件对象event包含“同创建它的特定事件相关的属性和方法”,其中有两个属性与事件委托紧密相关。
currentTarget 注册事件处理程序的那个元素(this始终等于此值)
target 事件的真正目标
document.body.onclick = function(e){
console.log(e.currentTarget === document.body);//true
console.log(e.target === document.getElementById("myBtn"));//true
}
点击myBtn会发现打印出的结果都是true,具体流程是这样的:事件真正目标是myBtn按钮,但按钮上并未注册事件处理程序,随着click事件冒泡到document.body,他们发现document.body上注册了事件处理程序,事件才得到了处理。
阻止冒泡可以使用stopPropagation(),这个方法不止可以阻止冒泡还可以阻止捕获(对比cancelBubble只能取消冒泡,因为ie不支持事件捕获,这也解释了为啥attachEvent没有第三个参数)。注意我们还有个stopImmediatePropagation,比起stopPropagation(),他多做了一步:将事件对于当前节点的监听也取消了。笔试选择题里常常会把取消默认事件和阻止冒泡写在一起混淆视听,可以使用 preventDefault()来取消其默认行为,但能用这个方法的事件必须cancelable 属性设置为 true(这个事件表示是否可以取消,一般都是true不要担心)
eventPhase属性表示事件在事件流哪个阶段:
btn.onclick = function(event){
alert(event.eventPhase); //2 表示实际目标接收到事件
};
document.body.addEventListener("click", function(event){
alert(event.eventPhase); //1 表示捕获阶段
}, true); //true - 事件句柄在捕获阶段执行
document.body.onclick = function(event){
alert(event.eventPhase); //3 表示冒泡阶段
};
千万不要忘记addEventListener第三个参数为true的时候表示在捕获阶段执行(这也是为啥大家都把他设成false)。
事件委托的优点是:
①减少事件注册节省内存:只需要在父节点绑定,其下子节点都能监听到。
②简化了节点更新时候的相应事件更新:不用在新增加的子节点上绑定事件;删除某事件也不用remove事件监听。
建议在浏览器卸载页面之前移除页面中的所有事件处理程序,不然他们就会留在内存中,这个时候事件委托的意义就体现出来了,敲好移除的。
4.各种事件小李子
曾经出现过的一道面试题(不是我遇上的
自定义一个右键菜单,主要的思路还是右键点击会出现菜单,左键点击会消失,关键还是知道调用哪个事件。
<div id="myDiv">
Right click or Ctrl+click me to get a custom context menu.
Click anywhere else to get the default context menu
<ul id="myMenu" style="position:absolute;visibility:hidden;background-color:silver">
<li><a href="#">A</a></li>
<li><a href="#">B</a></li>
<li><a href="#">C</a></li>
</ul>
</div>
<script>
window.onload = function(){
var oDiv = document.getElementById("myDiv");
oDiv.addEventListener("contextmenu",function(e){
var oMenu = document.getElementById("myMenu");
oMenu.style.left = e.clientX+"px";
oMenu.style.top = e.clientY+"px";
oMenu.style.visibility = "visible";
e.preventDefault();
},false);
oDiv.addEventListener("click",function(e){
var oMenu = document.getElementById("myMenu");
oMenu.style.visibility = "hidden";
},false);
}
</script>
e.preventDefault();是我认为的最重要的话,去掉默认的右键菜单。
在此处我们用到了visibility:visible 和visibility:hidden,尹深一下~
我们一直把visibility:visible看作opacity:1,把visibility:hidden看作opacity:0,但是在和transition结合使用的情况下其实是不一样的,让我们来看三个对比李子~
①只使用visibility
#btn{
visibility: visible;
/*opacity: 1;*/
transition: all 1s ease 3s;
}
#btn:hover {
visibility: hidden;
/*opacity: 0;*/
}
没有淡入淡出效果,在3s后立刻消失。
②只使用opacity
可以实现淡入淡出效果。此时我们在js中为btn添加一个onclick的事件处理程序。
oBtn.onclick = function(){
alert("疼死我了");
}
会发现在btn消失之后我们依然可以点击这个按钮并显示“疼死我了”
③既使用visibility又使用opacity
可以实现淡入淡出效果的同时,加在btn上的事件随着btn的消失也没办法触发了。
HTML5当中有一个新的API——visibilityState,用于记录当前标签页是否处于“激活状态”。当我们没有观看某个标签页时,这个标签页上的广告啊视频啊幻灯片小动画啥的可以先不用加载,这样既减小了服务器压力,毕竟用户不占用带宽了嘛,也可以节省客户端的内存。
document.addEventListener( "visibilitychange" , function(){}) 我们对于这个事件进行监听,后面是相应的事件处理程序。
var div = document.getElementById("parent");
document.addEventListener('visibilitychange',function(){
if(document.hidden){
div.style.animationPlayState = 'paused';
}else{
div.style.animationPlayState = 'running';
}
})
上面这段代码说的是当页面处于背景标签页或者最小化状态时document.hidden的情况下,暂停运动;否则继续运动。
一般只用document.hidden(表示背景标签页或者最小化)以及document.visible(表示前景标签页没有最小化)两种状态。
此处我们的css代码为:
#parent {
width: 300px;
height: 300px;
background: red;
animation: loop 2s alternate infinite linear;
}
@keyframes loop {
0% {
border-radius: 30px;
}
100% {
border-radius: 150px;
}
}
可以得到在30到150之间来回无限变化的边圆,非常平滑,而且我们可以控制它的暂停与开始,使用object.style.animationPlayState()即可。
我曾自己写了一个js代码表示在30到150之间来回无限变化的边圆,不平滑,也许这就是css的好处吧,因为设置了linear线性变化嘛。而且js代码还要判断方向,但在css中一个alternate就解决了。
附上js代码(比比css节省了多少事):
var count = 1;
setInterval(function(){
var oDiv = document.getElementById("parent");
var radius = window.getComputedStyle(oDiv)["borderRadius"];
if(count===0){
oDiv.style.borderRadius = parseInt(radius)-10+'px';
if(radius==="30px"){
count = 1;
}
}else{
oDiv.style.borderRadius = parseInt(radius)+10+'px';
if(radius==="150px"){
count = 0;
}
}
},100);
完毕~