js标准事件模型、事件注册的几种方法及其区别、事件委托机制
一、事件注册
-
事件的三要素:事件源、事件、事件处理函数
-
注册事件就是为事件源注册一种类型的事件并注册对该事件的处理函数
-
事件响应:是一种异步响应行为,一般不会返回值,对大多数的事件,会根据设置的事件处理函数有默认的动作,当然也可以设置返回值进行逻辑运算等
-
事件处理函数:一般不会传参,在标准浏览器中,事件处理函数会默认传递一个event实参,它是一个Event对象,当事件发生时,事件消息就会通过Event对象来进行传播
1、传统方式注册事件监听
- 在标签中添加事件监听属性,值为反应函数
<button onclick = 'change()'></button>
<script>
function change(){
alert('静态添加事件监听');
}
</script>
- 在js中获取元素后动态添加事件处理
<button></button>
<script>
let btn = document.querySelector('button');
btn.onclick = function(){
alert('动态添加事件监听1');
}
btn.onclick = function(){
alert('动态添加事件监听2');
}
</script>
注意:
- 事件具有唯一性,仅可以注册一次,若注册了多次,会以最后一次注册的事件为准。
- 传统的方式注册的事件使用:目标元素.事件 = null 来注销事件。
2、新的方法
-
addEventListener(eventType,function,useCapture):
eventType:事件类型,不同于传统事件类型,eventType要加引号并且不能在事件前加’on’;
function:事件响应函数,可以写成内部函数也可以写成外部函数;
useCapture:为Boolean类型,为true时在捕获阶段进行监听,为false时在冒泡阶段进行监听;当没有填写这一参数时,默认为false即冒泡 -
removeEventListener(eventType,function,useCapture):撤销事件,但是仅能撤销addEventListener注册的事件监听;eventType和function都要和注册时相同;
<button></button>
<script type="text/javascript">
let btn = document.querySelector('button');
function change(){
alert(点击按钮);
}
btn.addEventListener('click',change,false);
btn.removeEventListener('click',change,false);
</script>
对于IE6-8要使用attachEvent()和detachEvent()来添加监听事件以及移除监听事件。
3、区别
- 传统方法定义的事件具有唯一性,仅可以为一个元素定义一种事件
- 使用事件监听的方式可以为一个元素定义多个事件,事件的执行顺序不是由定义的顺序执行
- 事件监听的方式更加灵活便于使用
二、标准事件模型
事件处理函数中的this:在简单事件模型中,this代表当前事件对象, 与Event对象的srcEvent(IE)或者target(标准对象模型)所代表的意思相同。
1、 标准事件模型(IE8后)
js标准事件模型适用于IE8以后版本的浏览器,它分为三个部分:
-
事件传播
-
注册事件
-
注销事件
关于标准事件模型的事件传播:
- 捕获阶段:事件从Document对象沿着文档树向下传播到目标节点,如果目标节点的任何一个上级节点注册了相同的事件处理函数,那么事件在传播过程中就会首先在最靠近顶部的上级节点执行,并依次向下传播:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
.box{
width: 200px;
height: 200px;
background-color: red;
display: flex;
justify-content: center;
align-items: center;
}
.son{
width: 100px;
height: 100px;
background-color: #8A2BE2;
}
</style>
</head>
<body>
<div class="box">
<div class="son"></div>
</div>
<script type="text/javascript">
let box = document.querySelector('.box');
let son = document.querySelector('.son');
box.addEventListener('click',function(){
console.log('father');
},true);
son.addEventListener('click',function(){
console.log('son');
},true)
//点击son后:father son
//点击father后:father
//捕获事件先执行和点击的目标有同样事件的父节点,越靠近document的相同事件节点的先执行
//从树的顶部向下查找有相同事件的节点,越靠近根部的先执行
</script>
</body>
</html>
-
目标阶段:注册在目标节点的事件处理函数被执行
-
冒泡阶段:事件从目标节点向上触发,如果上级节点注册了相同类型的事件,将会逐级响应,依次向上传播
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
.box{
width: 200px;
height: 200px;
background-color: red;
display: flex;
justify-content: center;
align-items: center;
}
.son{
width: 100px;
height: 100px;
background-color: #8A2BE2;
}
</style>
</head>
<body>
<div class="box">
<div class="son"></div>
</div>
<script type="text/javascript">
let box = document.querySelector('.box');
let son = document.querySelector('.son');
box.addEventListener('click',function(){
console.log('father');
});
son.addEventListener('click',function(){
console.log('son');
})
// 点击son后: son father
//点击father后:father
//冒泡事件先执行点击的那个目标,再冒泡到父节点中设置有相同类型事件的节点上,并且不会从父节点传到子节点
</script>
</body>
</html>
2、阻止默认动作发生
(1)阻止冒泡
e.stopPropagation():其中,e是事件对象即在注册时传入事件处理函数的event
例如上面冒泡阶段例子,可以做如下更改:
box.addEventListener('click',function(e){
console.log('father');
});
son.addEventListener('click',function(e){
console.log('son');
e.stopPropagation();
})
//此时再点击son时就只会输出son了,son的冒泡被阻止了
3、标准事件模型常用的事件类型(对于传统的设置事件方式在标准事件模型的事件类型前加’on’)
(1)常用鼠标事件
事件 | 描述 |
---|---|
mouseover | 鼠标移入 |
mouseout | 鼠标移出 |
mouseenter | 鼠标移动到元素上 |
mouseleave | 鼠标移到元素外 |
mousemove | 鼠标移动 |
click | 点击 |
doubleclick | 双击鼠标 |
mousedown | 鼠标按下不松开 |
mouseup | 鼠标松开 |
例1:鼠标按下切换图片:
<img src="img/10.jpg"/>
<script type="text/javascript">
let img = document.querySelector('img');
img.addEventListener('mousedown',function(e){
img.src = "img/11.jpg";
})
img.addEventListener('mouseup',function(e){
img.src = "img/13.jpg";
})
</script>
例2:鼠标移入清除定时器:
<div id=""></div>
<script type="text/javascript">
let div = document.querySelector('div');
let count = 0;
let timer = null;
function auto(){
timer = setInterval(function(){
console.log(count += 1);
},2000);
}
auto();
div.addEventListener('mouseenter',function(e){
clearInterval(timer);
})
div.addEventListener('mouseout',function(e){
clearInterval(timer);
auto();
})
</script>
注意:当鼠标移入的目标有子元素时,最好使用mouseenter和mouseleave,没有子元素时可以使用mouseover和mouseout比如下例:
//father中有子元素,使用onmouseover会让小郭不稳定,鼠标抖动,使用onmouseenter会使效果稳定
//当时间对象没有子元素时使用onmouseover效率会更高
document.getElementsByClassName('father')[0].onmouseover = function(){
console.log('over');
}
document.getElementsByClassName('father')[0].onmouseenter = function(){
console.log('enter');
}
(2)常用键盘事件
事件 | 描述 |
---|---|
keyup | 放开按键 |
keydown | 按下按键 |
keypress | 按中按键 |
按一下按键顺序为:down/press/up,一直按下会执行down/press
document.addEventListener('keyup',function(e){
console.log('up');
});
document.addEventListener('keypress',function(e){
console.log('press');
});
document.addEventListener('keydown',function(e){
console.log('down');
});
例如,按下s键输入框自动聚焦:
<input type="text"/>
<script type="text/javascript">
//按下放开后
document.addEventListener('keyup',function(e){
//按下s键输入框自动聚焦
if(e.keyCode === 83){
let input = document.getElementsByTagName('input')[0];
input.focus();//聚焦
}
},true);
</script>
(3)其他事件
事件 | 描述 |
---|---|
focus | 获得焦点 |
blur | 失去焦点 |
input | 输入事件 |
change | 域内容发生改变 |
submit | 表单提交事件 |
**注意:**使用blur前一般要先使用focus
例1:输入框获得焦点并输入内容时,由p标签显示输入框的内容
<div class="box">
<p>show</p>
<input type="text"/>
</div>
<script type="text/javascript">
let p = document.querySelector('p');
let input = document.querySelector('input');
input.addEventListener('keyup',function(e){
p.innerHTML = input.value;
p.style.display = 'block';
});
input.addEventListener('focus',function(e){
if(input.value == ''){
p.style.display = 'none';
}else{
p.style.display = 'block';
}
});
input.addEventListener('blur',function(e){
p.style.display = 'none';
});
注意:
-
input在输入框输入内容时触发
-
change在输入框输入内容后,失去焦点时触发或者按回车键触发
三、事件委托
事件委托就是利用事件冒泡的原理,对要设置事件目标的父节点设置事件而不直接设置在子节点上,以点击事件为例,点击子节点时会冒泡到父节点上,从而执行了间接设置的事件,当父节点有很多子节点时,未来提高效率,仅在父节点上设置事件即可。
比如:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script type="text/javascript">
let ul = document.querySelector('ul');
ul.addEventListener('click',function(){
console.log('ul-li');
});
</script>
当点击任意一个li元素时,都会输出’ul - li’,点击li时,点击事件由li冒泡到ul上,ul上设置了事件监听,从而执行了父元素中的点击事件,日常开发中,可以利用这一机制,减少事件的绑定,提高页面效率。
当我们在事件反映函数中传入参数event时,可以通过e.target来获取当前的点击元素:
ul.addEventListener('click',function(e){
console.log('ul-li');
console.log(e.target);
});