Javascript_事件

Javascript事件

​ javascript中 事件 用于实现HTML元素与Javascript之间的交互,当浏览器窗口或文档中发生一些特定交互的瞬间。例如在页面加载完成、点击某个按钮、文本框输入文字等。当在这些事件发生的时候希望做一些事情(跳转页面、文本输入有效性等),可以用JS来实现。

​ 事件可以是浏览器行为也可以是用户行为

一、事件流

​ 事件流描述的是从页面中接收事件的顺序,因为页面文档中的HTML元素不是单独存在的,而是一个嵌套结构。所以当某个页面元素A触发某个事件的时候,不仅仅是直观上的某个元素触发了事件,包含元素A的容器元素也触发了相同的事件(比如点击了页面中的某个div触发点击事件后,实际上也点击了包含div的body元素,然后再一直往外扩散)。

事件冒泡 是目前大多数浏览器都支持的事件流(也是最常用的),即事件开始是有具体的元素接收然后逐级向上传播到较为不具体的元素(由内及外)。事件捕获 的思想与事件冒泡的思想 相反 (由外及里)

image-20220204101610438

image-20220204101624279

事件冒泡的顺序:

image-20220204101655051

二、事件处理程序

​ 事件是用户或浏览器自身执行的某种动作,响应 这个事件的 函数 就是事件处理程序。事件处理程序函数的开头都是on开头(点击事件的onclick、加载事件的onload)。而为事件指定处理程序的方式(JS代码和触发事件的HTML元素之间的绑定方式)有如下几种:

(一)、HTML事件处理程序

HTML元素内有对应的 属性 支持该HTML元素触发相应的事件处理程序,有两种写法。

1、直接在对应的事件里面写JS代码触发响应动作。

1
2
3
4
5
6
7
8
9
10
11
12
<!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>Document</title>
</head>
<body>
<input type="button" onclick="alert('Hello')" value="Show Message">
</body>
</html>

2、写好JS代码,然后属性绑定写好的JS函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!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>Document</title>
</head>
<body>
<input type="button" onclick="showMessage()" value="Show Message">
</body>
<script>
function showMessage(){
alert("hello");
}
var showMess = function(){
alert("hello");
}
//两种函数定义方式在使用HTML事件处理程序绑定HTML元素的时候都要加上()
</script>
</html>

缺点 : 计算机的编译顺序是由上至下的,如果JS函数定义在按钮之后而在解析JS函数之前就点击了按钮,这个时候会引发错误(按钮中的onclick属性绑定的函数还未定义)

(二)、DOM0 级事件处理程序

​ 这种方式是将一个函数赋值给一个事件处理程序属性(拿到元素引用,用点运算符访问事件处理程序属性,然后将函数赋值给属性)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!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>Document</title>
</head>
<body>

<input id="btn" type="button" value="Show Message">
<script>
var showMess = function(){
alert("hello");
}
var btn = document.getElementById("btn");
btn.onclick = showMess; //函数赋值给属性
</script>

</body>
</html>

(三)、DOM2 级事件处理程序

​ DOM2 级事件指定了两个方法用于处理 指定删除 事件处理程序的操作:addEventListner()removeEventListner()addEventListner() 用于注册元素事件, removeEventListner() 用于移除使用add方法注册的事件。

​ 这个函数接收三个参数:要处理的事件名(click、hover等)、事件处理程序函数(Object类型)、布尔值(不写默认false使用冒泡)。最后的布尔值指定的是ture代表在 捕获阶段 调用事件处理程序、false代表在 冒泡阶段 调用事件处理程序。(不建议使用true在捕获阶段调用事件处理程序)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!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>Document</title>
</head>
<body>

<input id="btn" type="button" value="Show Message">
<script>

var btn = document.getElementById("btn");
var hander = function(){
//this这里指的是btn引用本身,在IE事件处理程序中this代表的是window对象
//跨浏览器时得区分处理
alert(this.id);
alert("hello");
}
btn.addEventListener("click",hander,false);
// btn.removeEventListener("click",hander,false);
</script>

</body>
</html>

但是使用匿名函数会遇到无法删除事件的情况,原因是使用匿名函数的时候 事件处理程序函数 对应的是一个引用类型的Object对象,虽然代码一样但是在内存中对应着两个不同的引用,故无法删除事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!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>Document</title>
</head>
<body>

<input id="btn" type="button" value="Show Message">
<script>

var btn = document.getElementById("btn");

btn.addEventListener("click",function(){
alert(this.id);
alert("hello");
},false);

btn.removeEventListener("click",function(){
alert(this.id);
alert("hello");
},false);
//两个匿名函数对应着不同的引用,代码一样但是内存中引用不一样,故无法删除事件
</script>

</body>
</html>

(四)、IE事件处理程序

类比于DOM2事件处理程序的两个方法:attachEvent() 与 detachEvent() 。

IE事件流使用事件冒泡,所以只有两个函数参数:事件处理程序名称 与 事件处理程序函数

使用attach加上的只能detach删除,注意点也同上述的DOM2的匿名函数一样。

只在IE和Opera浏览器支持

(五)、跨浏览器的事件处理程序

编写条件判断对不同浏览器所支持的事件处理程序进行分类处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var EventUtil={
addHandler:function(element,type,hander){
if(element.addEventListener){
element.addEventListener(type,hander,false);
}else if(element,attachEvent){
element.attachEvent("on"+type,hander);
}else{
element["on"+type]=hander;
}
},
removeHandler:function(element,type,hander){
if(element.removeEventListener){
element.removeEventListener(type,hander,false);
}else if(element.detachEvent){
element.detachEvent("on"+type,hander);
}else {
element["on"+type]=null;
}
}
}

调用时 EventUntil.addhander();

三、事件对象

​ 在触发DOM上的某个事件时,会产生一个事件对象event。当一个事件发生的时候,和当前这个对象发生的这个事件有关的一些详细信息(包括导致事件的元素、事件的类型、以及其它与特定事件相关的信息等。这个对象是在执行事件时,浏览器通过函数传递过来的。)都会被临时保存到一个指定的地方——event对象,供我们在需要的时候调用。

(一)、DOM中的事件对象

​ 当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为实参传递进响应函数,但是这个事件对象是看不见的,用形参来接收再使用。 事件对象中封装了当前事件相关的一切信息 (鼠标坐标、键盘按键哪个有被按下等)

与相应事件有关的信息都保存在浏览器传入事件处理函数的事件对象中,常用DOM0方式操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!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>Document</title>
</head>
<body>
<div id="areaDiv" style="border: 2px solid black;height: 50px;"></div>
<br>
<div id="MsgDiv" style="border: 2px solid black;height: 30px;"></div>

<script>
// 获取大Div
var areaDiv = document.getElementById("areaDiv");
/*
在DOM结点调用事件属性的时候,
赋值给属性的事件处理函数中浏览器会传入一个事件对象进事件处理函数中,
包含了该事件属性在时间触发时的一切信息
*/
areaDiv.onmousemove = function(event){
// alert(event.clientX); //事件对象的属性,显示鼠标的横坐标
var x = event.clientX;
var y = event.clientY;
var MsgDiv = document.getElementById("MsgDiv");
MsgDiv.innerHTML = "x:" + x + ",y:" + y;
//显示鼠标在areaDiv中移动时鼠标位置的坐标(这个信息保存在浏览器传入的事件对象中)
}

</script>

</body>
</html>

image-20220204153333411

(二)、IE中的事件对象

​ 与DOM中的事件对象不一样,IE浏览器早期版本IE8以前,event对象不是传入到事件处理函数中的实参,event对象保存在window对象下作为全局变量使用,这里考虑到的是浏览器的兼容性问题,区别于DOM的事件对象。(下面是IE8不兼容的情况)

image-20220204154321003

解决办法:

1
2
var x = window.event.clientX;
var y = window.event.clientY;

(三)、跨浏览器的事件对象

​ 使用条件判断处理浏览器兼容性问题解决事件对象形式。

1
2
3
4
areaDiv.onmousemove = function(event){
event = event || window.event; //处理兼容问题
...
}

(四)、事件对象的属性(target与currentTarget)

事件对象有两个常用的属性:target与currentTarget;

​ target代表的是真正发生的事件的DOM结点(你点击了什么target就代表什么),currentTarget代表的是当前事件发生在哪个DOM结点上(由于事件冒泡的原因,由内层往外层冒泡的过程中,会涉及到多个DOM结点,这个时候使用currentTarget定位到目前具体到哪个DOM结点上了),这里的target属性在 事件委托 上会用到。

区分点 :event.target 与 this

event.target返回的是触发事件的对象(元素),this返回的是绑定事件的对象(元素)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!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>Document</title>
</head>
<body>
<ul>
<li>岛田庄司</li>
<li>鲇川哲也</li>
<li>柯南道尔</li>
<li>阿加莎克里斯蒂</li>
</ul>
<script>
var ul = document.querySelector("ul");
// DOM2级写法,为ul绑定事件
ul.addEventListener("click",function(event){
console.log(event.target); //输出触发事件的元素 li
console.log(this); //输出绑定事件的元素 ul
})
</script>
</body>
</html>

image-20220205210232245

四、事件类型

​ 浏览器中发生的事件类型大致上有UI(user interface)事件、焦点事件、鼠标事件、滚轮事件、文本事件、键盘事件、合成事件、变动事件

(一)、UI事件(用户与页面元素交互时触发)

UI事件往下有几种分支:load、unload、abort、error、select、resize、scroll

  1. load:页面完全加载后触发

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    <!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>Document</title>
    </head>

    <body>
    <script>
    //自行封装
    var EventUtil = {
    addHandler: function(element, type, handler){
    if (element.addEventListener){
    element.addEventListener(type, handler, false);
    } else if (element.attachEvent){
    element.attachEvent("on" + type, handler);
    } else {
    element["on" + type] = handler;
    }
    },
    removeHandler: function(element, type, handler){
    if (element.removeEventListener){
    element.removeEventListener(type, handler, false);
    } else if (element.detachEvent){
    element.detachEvent("on" + type, handler);
    } else {
    element["on" + type] = null;
    }
    }
    };
    //调用方法
    EventUtil.addHandler(window,"resize",function(event){
    alert("resized");
    });
    </script>
    </body>

    </html>
  2. unload: 页面文档被完全卸载时触发,多用于清除引用,以防内存泄漏。

  3. resize: 浏览器窗口大小调整时触发

  4. scroll:滚动鼠标时触发

(二)、焦点事件(元素获得或者失去焦点时触发)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!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>Document</title>
</head>
<body>

<input type="text" name="" id="text">

<script>
var t = document.getElementById("text");
t.onfocus = function(){
console.log("focused"); //点击文本框获取焦点输出
}
t.onblur = function(){
console.log("blured"); //点击文本框后点击空白处失去焦点输出
}
</script>
</body>
</html>

(三)、鼠标与滚轮事件

  • click—用户单击鼠标左键或按下回车键触发
  • dbclick—用户双击鼠标左键触发。
  • mousedown—在用户按下了任意鼠标按钮时触发。
  • mouseenter—在鼠标光标从元素外部首次移动到元素范围内时触发。此事件不冒泡
  • mouseleave—元素上方的光标移动到元素范围之外时触发。不冒泡
  • mousemove—光标在元素的内部不断的移动时触发。
  • mouseover—鼠标指针位于一个元素外部,然后用户将首次移动到另一个元素边界之内时触发。
  • mouseout—用户将光标从一个元素上方移动到另一个元素时触发。
  • mouseup—在用户释放鼠标按钮时触发。

​ 与具体的DOM0属性用法类似,但要注意的是鼠标所处的位置坐标有说法:客户区坐标位置页面坐标位置 还有 屏幕坐标位置

​ 1. 客户区坐标位置:视口(特殊的指定位置(某个div内))中鼠标的坐标.

1
2
clientX
clientY

​ 2. 页面坐标位置:具体页面中鼠标的坐标。当页面很长的时候需要滚动,这个时候鼠标相对于这个较长的页面所处的坐标值

1
2
pageX
pageY
    3. 屏幕坐标位置:(显示屏大小的位置)中鼠标的坐标.(以左上角为坐标原点)
1
2
screenX
screenY

(四)、键盘与文本事件

keydown:当用户按下键盘上的任意键时触发。按住不放,会重复触发。

keypress:当用户按下键盘上的字符键时触发。按住不放,会重复触发。

keyup:当用户释放键盘上的键时触发。

五、内存与性能

​ 在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因;如果要用事件委托,就会将所有的操作放到js程序里面,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能;

​ JS的事件处理程序在事件触发的时候执行,然而当一个JS函数存在多个事件的时候会涉及到多个函数,这对内存的消耗是十分巨大的。对于事件处理程序过多的解决办法是利用 事件委托 。事件委托利用了 事件冒泡 ,只使用一个事件处理程序就可以管理一种类型的所有事件 。详见另篇 事件冒泡与事件委托


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!