目录
event.stopPropagation() & event.stopImmediatePropagation()
2. stopImmediatePropagation()方法
event.target & event.currentTarget
Event对象
Event 对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态。
事件通常与函数结合使用,函数不会在事件发生前被执行!
当一个事件发生的时候,和当前这个对象发生的这个事件有关的一些详细信息(包括导致事件的元素、事件的类型、以及其它与特定事件相关的信息等。这个对象是在执行事件时,浏览器通过函数传递过来的。)都会被临时保存到一个指定的地方——event对象,供我们在需要的时候调用
获取event对象
在 W3C 规范中,event 对象是随事件处理函数传入的,Chrome、FireFox、Opera、Safari、IE9.0及其以上版本都支持这种方式;但是对于 IE8.0 及其以下版本,event 对象必须作为 window 对象的一个属性。
IE、Chrome:event是一个内置的全局对象
标准下/FF:事件对象是通过事件函数的第一个参数传入(如果一个函数是被事件调用的,那么这个函数定义的第一个参数就是事件对象)
如何处理兼容性:
document.onclick = function fn(event){
var ev = window.event||event;
alert('处理兼容');
}
隐藏在监听函数中的"event"
当监听的事件发生时,浏览器会去执行我们通过addEventListener()
注册的事件处理程序函数。
这个时候,EventListener 会去创建一个「事件对象」 (Event Object),里面包含了所有与这个事件相关的属性,并且以「参数」的形式传给我们的处理程序函数:
<button id="btn">Click</button>
......
var btn = document.getElementById('btn');
// 参数 e 就是上面所说的事件对象
// 因为是参数,当然可以自己定名称
btn.addEventListener('click', function(e){
console.log(e);
}, false);
当点击<button>
后,可以从console
看到event
对象中提供了这么多东西:
像是
type
: 表示事件的名称target
: 表示触发事件的元素bubbles
:表示这事件是否是在「冒泡」阶段触发(true
/false
)pageX
/pageY
:表示事件触发时,鼠标座标在网页的相对位置
其余的属性这里就不一一介绍,不过要注意的是,每个「事件对象」所提供的属性都会根据触发的事件而稍微不同。
event. preventDefault()
HTML中部分元素会有默认行为,像是<a>
标签默认页面跳转或是锚点定位,或是表单的submit
等等...,
如果我们需要在这些元素上绑定事件,那么适当地取消它们的默认行为就是很重要的一件事。
比如,有一个通往baidu的链接<a>
:
<a id="link" href="https://www.baidu.com">百度</a>
假设今天点击这个link时,我希望浏览器执行console.log('米淇淋你好帅!');
那么根据先前所说,我可以先注册click
事件:
var link = document.querySelector('#link');
link.addEventListener('click', function (e) {
console.log('米淇淋你好帅!');
}, false);
结果你却发现,即便我们在<a>
中去注册了click
事件,但是当我点击这个link的时候,浏览器开始会console.log出"米淇淋你好帅!",但最后baidu的网页依旧会覆盖我想要的内容。
可是我希望执行的是console.log('米淇淋你好帅!');
而不是直接把我带偏了去到baidu的网站,那么我们该怎么做,才能避免呢?
这时候如果调用event.preventDefault()
方法,默认事件行为将不再触发:
var link = document.querySelector('#link');
// 在 事件处理函数中 加上 e.preventDefault();
link.addEventListener('click', function (e) {
e.preventDefault();
console.log('米淇淋你好帅!');
}, false);
这个时候,再试着点击link一次,你会发现浏览器默认的跳转页面的行为不见了,console.log('米淇淋你好帅!');
也可顺利执行啦哈哈。
但要注意的是,event.preventDefault()
并不会阻止事件向上传递(即事件冒泡) 。
另外,值得一提的是,下面这样设置也可以让a标签仅仅当做一个普通的按钮,点击实现一个功能,不想页面跳转,也不想锚点定位:
<a href="javascript:;">链接</a>
此外,在事件处理函数的最后加上return false;
也会有event.preventDefault()
的效果,但切记不可以加在前面,若是加在前面事件处理函数就直接gg了。
event.stopPropagation() & event.stopImmediatePropagation()
1. event.stopPropagation() 方法
阻止事件向上冒泡传递,阻止任何父事件处理程序被执行。
接下来我们看个例子:
<div>
<div id="parent">
父元素
<div id="child">子元素</div>
</div>
</div>
......
var parent = document.getElementById('parent');
var child = document.getElementById('child');
child.addEventListener('click', function () {
console.log('child bubbling');
}, false);
parent.addEventListener('click', function () {
console.log('parent bubbling');
}, false);
document.body.addEventListener('click', function () {
console.log('body bubbling');
}, false);
document.documentElement.addEventListener('click', function () {
console.log('html bubbling');
}, false);
document.addEventListener('click', function () {
console.log('document bubbling');
}, false);
window.addEventListener('click', function () {
console.log('window bubbling');
}, false);
当我点击的是「子元素」的时候,通过console.log
可以观察到事件触发的顺序为:
child bubbling
parent bubbling
body bubbling
html bubbling
document bubbling
window bubbling
而如果在「子元素」中加入event.stopPropagation() 方法,其余保持原样的话:
child.addEventListener('click', function (e) {
console.log('child bubbling');
e.stopPropagation();
}, false);
再次点击「子元素」,则只出现:
child bubbling
其余父事件不会触发,即event.stopPropagation() 方法阻止了事件向上冒泡传递,阻止任何父事件处理程序被执行。
2. stopImmediatePropagation()方法
既能阻止事件向父元素冒泡,也能阻止元素同事件类型的其它监听器被触发。而 stopPropagation 只能实现前者的效果。
我们来看个例子:
<body>
<button id="btn">click me to stop propagation</button>
</body>
......
var btn = document.querySelector('#btn');
btn.addEventListener('click', function(e) {
console.log('btn click 1');
//e.stopImmediatePropagation();
});
btn.addEventListener('click', function() {
console.log('btn click 2');
});
document.body.addEventListener('click', function() {
console.log('body click');
});
document.documentElement.addEventListener('click', function() {
console.log('html click');
});
document.addEventListener('click', function() {
console.log('document click');
});
window.addEventListener('click', function() {
console.log('window click');
});
当我点击button的时候,通过console.log可以观察到事件触发的顺序为:
btn click 1
btn click 2
body click
html click
document click
window click
而如果在「btn的第一个监听函数」中加入event.stopImmediatePropagation() 方法,其余保持原样的话:
btn.addEventListener('click', function(e) {
console.log('btn click 1');
e.stopImmediatePropagation();
});
再次点击button,则只出现:
btn click 1
所以说,使用 stopImmediatePropagation() 方法后,点击按钮时,仅触发设置了stopImmediatePropagation() 方法的监听器,与此同时按钮的其余同类型点击事件不触发。
event.target & event.currentTarget
老实说并不能好好用文字描述这两者的区别,我们直接看个例子:
<style>
#a{
width: 200px;
height: 200px;
background: yellow ;
}
#b{
width: 150px;
height: 150px;
background: green;
}
#c{
width: 100px;
height: 100px;
background: grey;
}
#d{
width: 50px;
height: 50px;
background: black;
}
</style>
......
<div id="a">
<div id="b">
<div id="c">
<div id="d"></div>
</div>
</div>
</div>
......
document.getElementById('a').addEventListener('click', function(e) {
console.log(
'target:' + e.target.id + '¤tTarget:' + e.currentTarget.id
)
})
document.getElementById('b').addEventListener('click', function(e) {
console.log(
'target:' + e.target.id + '¤tTarget:' + e.currentTarget.id
)
})
document.getElementById('c').addEventListener('click', function(e) {
console.log(
'target:' + e.target.id + '¤tTarget:' + e.currentTarget.id
)
})
document.getElementById('d').addEventListener('click', function(e) {
console.log(
'target:' + e.target.id + '¤tTarget:' + e.currentTarget.id
)
})
jsbin 点这里。
当我们点击最里层黑色区域的元素d的时候,会依次输出:
target:d¤tTarget:d
target:d¤tTarget:c
target:d¤tTarget:b
target:d¤tTarget:a
从输出中我们可以看到,event.target
指向引起触发事件的元素,而event.currentTarget
则是事件绑定的元素,只有被点击的那个目标元素的event.target
才会等于event.currentTarget
。也就是说,event.currentTarget
始终是监听事件者,而event.target
是事件的真正发出者。
另外,值得一提的是,function内部的this
指的也就是event.currentTarget
。
事件代理
由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件代理或叫事件委托(Event Delegation)。
具体可看我的另一篇文章 JS事件代理和事件委托
本文参考