一、 基本概念
1.1 什么是事件?事件的组成部分?事件绑定?
(1) 事件是指在文档或者浏览器中发生的一些特定交互瞬间;是元素天生具备的行为方式(和写不写JS代码没有关系)。当我们去操作元素的时候会触发元素的很多事件。事件是javaScript和DOM之间交互的桥梁。
比如打开某一个网页,浏览器加载完成后会触发 load 事件(onload不是事件),当鼠标悬浮于某一个元素上时会触发 hover 事件(onhover不是事件),当鼠标点击某一个元素时会触发 click 事件(onclick不是事件)等等。浏览器会把一些常用事件挂载到元素对象的私有属性上,让我们可以实现DOM0事件绑定,
(2) 事件的组成部分:事件源 . 事件类型 = 预处理函数
事件源:真正触发事件的那个元素;
事件类型 : 例:onmousemove 、onmouseover等;
预处理函数:function ( ){ };
(3) 事件绑定(DOM 0级事件):给当前元素的某个事件绑定方法,目的是让当前元素某个事件被触发时,做出一些反应
1.2 事件处理、事件处理程序
事件处理就是当事件被触发后,浏览器响应这个事件的行为,而这个行为所对应的代码即为事件处理程序(也叫事件处理函数、事件句柄)。
事件处理程序的名字以"on"开头,因此click事件的事件处理程序就是onclick,load事件的事件处理程序就是onload。
总之,事件就是一个动作瞬间,如鼠标点击;事件处理程序是一个过程,处理事件发生时的函数的函数。
1.3 事件监听器
监听器是一个专门用于对其他对象身上发生的事件或状态改变进行监听和相应处理的对象,当被监视的对象发生情况时,立即采取相应的行动,即当被监听对象发生上述事件后,监听器某个方法立即被执行。
事件监听器(DOM 2级事件),就是让计算机监视一个事件是否发生,从而执行一些写好的程序,来让HTML元素对事件作出反应;
一般情况下,监听事件是对应于dom元素而言的,同时也需要声明所监听事件种类。 比如可以为一个按钮设置监听事件,点击之后自动调用相应函数处理。 同时可以在用函数处理时使用事件对象(event object)来获得关于触发这个事件的对象的信息。
1.4 事件流
事件流又称为事件传播,指的是事件的流向,事件的执行顺序。
当事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程就叫 DOM 事件流 。
由于在 HTML 中的标签都是相互嵌套的,可以将元素想象成一个大盒子里装一个小盒子, document 是最外面的大盒子,给所有盒子都绑定点击事件,点击小盒子大盒子的事件也会被触发,这个就叫事件的传播。那么是先执行大盒子的单击事件,还是先执行小盒子的单击事件 ?
现代浏览器(指 IE6-IE8 除外的浏览器,包括 IE9+、FireFox、Safari、Chrome 和 Opera 等)事件流包含两种事件流模型和三个过程。两种方式是事件捕获和事件冒泡;三个过程,分别是捕获阶段、目标阶段和冒泡阶段,下图形象地说明这个过程:
Event.prototype:
- 0 NONE:默认值,不代表任何意思
- 1
CAPTURING_PHASE 捕获阶段
- 2
AT_TARGET 目标阶段(当前事件源)
- 3
BUBBLING _PHASE :冒泡阶段
- 在冒泡型事件流中对于div的click事件传播顺序为div>body>html>document
- 在捕获型事件流中对于div的click事件传播顺序为document>html>body>div
- 并非所有的事件都会经过冒泡阶段。所有的事件都要经过捕获阶段和处于目标阶段,但是有些事件会跳过冒泡阶段:如,获得输入焦点的focus事件、失去输入焦点的blur事件 、mouseenter 、mouseleave 、load 、 unload 、 resize。
二、JavaScript怎么实现事件处理(事件注册)?
事件分为DOM 0级事件和Dom 2级事件,DOM2级事件也叫做事件监听。
DOM 0级事件绑定的缺点是如果事件相同,后者的事件会覆盖前者的事件。
DOM2级事件绑定可以为同一个对象的同一个事件绑定多个事件处理程序,且前者事件不会被覆盖,会依次执行。
DOM0级事件和DOM2级事件可以共存,不互相覆盖,但是dom0之间依然会覆盖。
2.1DOM 0级事件(冒泡阶段触发)——传统注册方式
DOM0事件绑定的原理
给当前元素的某一私有属性(onXXX)赋值的过程;(之前属性默认值是null,如果我们赋值了一个函数,就相当于绑定了一个方法)
当我们赋值成功(赋值一个函数),此时浏览器会把DOM元素和赋值的的函数建立关联,以及建立DOM元素的行为监听,当某一行为被用户触发,浏览器会把赋值的函数执行;
DOM0事件绑定的特点:
只有DOM元素天生拥有这个私有属性(onxxx事件私有属性),我们赋值的方法才叫事件绑定,否则属于设置自定义属性
移除事件绑定的时候,我们只需要赋值为null(eventTarget.事件类型 = null;)
在DOM0事件绑定中,只能给当前元素的某一个事件行为绑定一个方法,绑定多个方法,最后一次的绑定的会替换前面绑定的
2.1.1 HTML 内联属性(on+事件名=‘处理函数’)
即在 HTML 元素里直接填写与事件相关的属性,属性值为事件处理程序。示例如下:
<button onclick="console.log('You clicked me!');"></button>
onclick 对应着 click 事件,所以当按钮被点击后,便会执行事件处理程序,即控制台输出 You clicked me!。
不过我们需要指出的是,这种方式将 HTML 代码与 JavaScript 代码耦合在一起,不利于代码的维护,所以应该尽量避免使用这样的方式。
2.1.2 在JavaScript代码中绑定DOM 属性(element.onclick)
绑定事件监听函数
通过直接设置某个 DOM 节点的属性来指定事件和事件处理程序,上代码:
const btn = document.getElementById("btn");
btn.onclick = function(e) {
console.log("You clicked me!");
};
上面示例中,首先获得 btn 这个对象,通过给这个对象添加 onclick 属性的方式来监听 click 事件,这个属性值对应的就是事件处理程序。这段程序也被称作 DOM 0 级事件处理程序。
2.1.3 传统注册方式的解绑事件
eventTarget.事件类型 = null;
(1)将解绑事件写在函数里的任何位置,其注册事件只会执行一次;
(2)将解绑事件写在函数内的最后,注册事件执行完在销毁将不执行。
(3)将解绑事件写在函数外的注册事件前,解绑事件对其无效;
(4)将解绑事件写在函数外的注册事件后,注册事件将不执行。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>传统注册解除绑定</title>
</head>
<body>
<button class='btn1'>传统注册</button>
<script>
var btn1 = document.querySelector('.btn1');
var btn2 = document.querySelector('.btn2');
// 传统注册
// btn1.onclick = null;//对应(3)
btn1.onclick = function(e) {
btn1.onclick = null;//对应(1)
console.log('我是传统注册,明天周日了')
// btn1.onclick = null;//对应(2)
}
// btn1.onclick = null;//对应(4)
</script>
</body>
</html>
2.1.4 常用的DOM0级事件监听汇总
常见的鼠标事件监听
事件名 | 事件描述 |
---|---|
onclick | 当鼠标单击某个对象 |
ondblclick | 当鼠标双击某个对象 |
onmousedown | 当某个鼠标按键在某个对象上被按下 |
onmouseup | 当某个鼠标按键在某个对象上被松开 |
onmousemove | 当某个鼠标按键在某个对象上被移动 |
mouseover | 鼠标滑过 |
onmouseenter | 当鼠标进入某个对象(相似事件onmouseover) |
onmouseleave | 当鼠标离开某个对象(相似事件onmouseout) |
常见的键盘事件监听
事件名 | 事件描述 |
---|---|
onkeypress | 当某个键盘的键被按下(系统按钮,如箭头键和功能键无法得到识别) |
onkeydown | 当某个键盘的键被按下(系统按钮可以识别,并且会先于onkeypress发生) |
onkeyup | 当某个键盘的键被松开 |
常见的表单事件监听
事件名 | 事件描述 |
---|---|
onchange | 当用户改变某个表单域的内容时,会触发 |
onfocus | 当某元素获得焦点(比如tab键或鼠标点击) |
onblur | 当某元素失去焦点 |
onsubmit | 当表单被提交 |
onreset | 当表单被重置 |
oninput | 当用于正在修改表单域的内容 |
常见的页面事件监听
事件名 | 事件描述 |
---|---|
onload | 当页面或图像被完成加载 |
onunload | 当用户退出页面 |
2.2 DOM 2级事件——监听注册方式
关于事件监听起初Netscape制定了JavaScript的一套事件驱动机制(即事件捕获)。随即IE也推出了自己的一套事件驱动机制(即事件冒泡)。最后W3C规范了两种事件机制,分为捕获阶段、目标阶段、冒泡阶段,详细介绍请看下文。IE8以前IE一直坚持自己的事件机制(前端人员一直头痛的兼容性问题),IE9以后IE也支持了W3C规范。
DOM2级事件绑定的原理
DOM2级事件绑定使用的addEventListener/attachEvent方法都是在eventTarget这个内置类的原型上定义的,我们调用的时候,首先要通过原型链找到这个方法,然后执行完成事件绑定的效果.
浏览器会给当前元素的某个事件行为开辟一个事件池(事件队列),浏览器有一个统一的事件池,每个元素绑定的行为都放在这里,通过相关标志区分,当我们通过addEventListener/attachEvent进行事件绑定的时候,会把绑定的方法放在事件池中;
当元素的某一行为被触发,浏览器回到对应事件池中,把当前放在事件池的所有方法按序依次执行
DOM2级事件绑定的特点
浏览器会把一些常用事件挂载到元素对象的私有属性上,让我们可以实现DOM0事件绑定,所有DOM0支持的行为,DOM2都可以用,DOM2还支持DOM0没有的事件行为,例如:onDOMContentLoaded(所有的DOM0和IE6-8的DOM2都不支持)//当前浏览器中的DOM结构加载完成,就会触发这个事件。
DOM2中可以给当前元素的某一事件行为绑定多个不同方法(因为绑定的所有方法都放在事件池中);
事件的移除:事件类型、绑定的方法、传播阶段三个完全一致,才可以完成移除(因此在绑定方法时,尽量不要用匿名函数,否则不好移除)
2.2.1 支持W3C标准事件监听器的浏览器使用:addEventListener()
element.addEventListener(event, listener, useCapture)
//第一个参数是事件的类型(例如“ click”或“ mousemove”,与IE不同的是前面不用加on)。
//第二个参数是事件发生时我们要调用的监听函数。
//第三个参数是一个布尔值,指定是否使用捕获事件。此参数是可选的(默认是false true代表捕获,由外向里,很少用; false代表冒泡,由里向外)
//标准的事件监听函数如下: