事件:
JavaScript 和 HTML的交互是通过事件实现的。
事件是某个行为或者触发,比如点击、鼠标移动:
- 当用户点击鼠标时
- 当网页已加载时
- 当图像已加载时
- 当鼠标移动到元素上时
- 当用户触发按键时
事件流
-
事件冒泡
事件开始时由最具体的元素接受,然后逐级向上传播到较为不具体的元素
-
事件捕获
不太具体的节点更早接受事件,而最具体的元素最后接受事件,和事件冒泡相反
-
DOM事件流
DOM2级事件规定事件流包括三个阶段,事件捕获阶段,处于目标阶段,时间冒泡阶段,首先发生的是事件捕获,为截取事件提供机会,然后是实际目标接受事件,最后是冒泡阶段
注:Opera、Firefox、Sarfari都支持DOM事件流,IE不支持事件流,只支持时间冒泡
事件传播机制
当一个事件发生以后,它会在不同的DOM节点之间传播(propagation)。这种传播分为三个阶段:
- 第一阶段:从window对象传导到目标节点,称为“捕获阶段”(capture phase)。
- 第二阶段:在目标节点上触发,称为“目标阶段”(target phase)。
- 第三阶段:从目标节点传导回window对象,称为“冒泡阶段”(bubbling phase)。
这种三阶段的传播模型,会使得一个事件在多个节点上触发。
比如:
<div>
<p>Click Me</p>
</div>
如果对这两个节点的click
事件都设定监听函数,则click
事件会被触发四次。<div>
和<p>
节点的捕获阶段和冒泡阶段各一次:
- 捕获阶段:事件从
<div>
向<p>
传播时,触发<div>
的click
事件; - 目标阶段:事件从
<div>
到达<p>
时,触发<p>
的click
事件; - 目标阶段:事件离开
<p>
时,触发<p>
的click
事件; - 冒泡阶段:事件从
<p>
传回<div>
时,再次触发<div>
的click
事件。
用户点击网页的时候,浏览器总是假定click
事件的目标节点,就是点击位置的嵌套最深的那个节点。所以<p>
节点的捕获和冒泡阶段都会显示为target
阶段。
event.stopPropagation()
stopPropagation
方法阻止事件在DOM中继续传播,即取消进一步的事件捕获或冒泡,防止再触发定义在别的节点上的监听函数,但是不包括在当前节点上新定义的事件监听函数。
我们可以在button的事件处理程序中调用stopPropagation()
从而避免注册在body上的事件发生。
var handler = function(e){
alert(e.type);
e.stopPropagation();
}
addEvent(document.body, 'click', function(){alert('Clicked body')});
var btnClick = document.getElementById('btnClick');
addEvent(btnClick, 'click', handler);
//若是注释掉e.stopPropagation();在点击button的时候,由于事件冒泡,body的click事件也会触发,但是调用后这句后,事件会停止传播。
event.preventDefault()
preventDafault
方法取消浏览器对当前事件的默认行为,比如点击链接后,浏览器跳转到指定页面,或者按一下空格键,页面向下滚动一段距离。该方法生效的前提是,事件的cancelable
属性为true
如果为fales
,则调用该方法没有任何效果。
该方法不会阻止事件的进一步传播(stopPropagation方法可用于这个目的)。只要在事件的传播过程中使用了preventDefault
方法,该事件的默认方法就不会执行。
//html代码为
//<input type="checkbox" id="my-checkbox"/>
var cb = document.getElementById('my-checkbox');
cb.addEventListener('click', function(e){
e.preventDafault();
},);
上面代码为点击单选框事件,设置监听函数,取消默认行为。由于浏览器的默认行为是选中单选框,所以这段代码会导致无法选中单选框。
利用这个方法,可以为文本输入框设置校验条件。如果用户的输入不符合条件,就无法将字符输入文本框。
function checkName(e){
if(e.charCode < 97 || e.charCode > 122){
e.preventDafault();
}
}
//keypress监听函数,只能输入小写字母,否则输入事件的默认事件(写入文本框)将本取消。
如果监听函数最后返回布尔值false(return false),浏览器也不会触发默认行为,与preventDafault方法有等同效果。
事件代理
由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理
定义:事件代理就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。(delegation)。
var ul = document.querySelector('ul');
ul.addEventListener('click', function(event){
if(event.target.tagName.toLowerCase() === 'li'){
//...
}
})
上面代码的click
事件的监听函数定义在<ul>
节点,但是实际上,它处理额是子节点<li>
的click
事件。这样的好处是,只要定义一个监听函数,就能处理多个子节点的事件,且以后再添加子节点,监听函数依然有效。
写一个 Demo,演示事件传播的过程,演示阻止传播的效果
- 事件传播
//html部分
<body>
<style>
* {
padding: 10px;
margin: 0;
}
.box,
.container,
.target {
border: 1px solid;
}
</style>
<div class="box">box
<div class="container">container
<div class="target">
target
</div>
</div>
</div>
</body>
//js部分
<script>
//为了减少代码量,写一个$函数
function $(selector){
return document.querySelector(selector);
}
$('.box').addEventListener('click', function(e){
console.log('box click...in 捕获阶段');
}, true);
$('.container').addEventListener('click', function(e){
console.log('container click...in 捕获阶段');
}, true);
$('.target').addEventListener('click',function(e){
console.log('target click...in 捕获阶段');
}, true);
$('.target').addEventListener('click', function(e){
console.log('target click...in 冒泡阶段');
}, false);
$('.container').addEventListener('click', function(e){
console.log('container click...in 冒泡阶段');
}, false);
$('.box').addEventListener('click', function(e){
console.log('box click...in 冒泡阶段');
}, false);
</script>
点击target时效果图
事件传播预览地址
- 阻止事件传播
点击target时发现在container捕获阶段时事件被阻止传播了
阻止事件传播预览
网友评论