【必须掌握】js中的事件(看javascript高级编程第四版的记录)

码农天地 -
【必须掌握】js中的事件(看javascript高级编程第四版的记录)
事件

JavaScript 与 HTML 的交互是通过事件实现的,事件代表文档或浏览器窗口中某个有意义的时刻。

1.事件流(掌握)

事件流模拟了事件接收的顺序。IE 和 Netscape 开发团队提出了几乎完全相
反的事件流方案。IE 将支持事件冒泡流,而 Netscape Communicator将支持事件捕获流。

1.1 事件冒泡 (理解、使用)

从最具体的元素(文档中最深的节点)开始触发,然后向上传播到没有那么具体的元素(文档)。
例如:

<!DOCTYPE html>
<html>
<head>
<title>Event Bubbling Example</title>
</head>
<body>
<div id="myDiv">Click Me</div>
</body>
</html>

点击页面的 div 元素后,click事件会以如下顺序发生:

1). <div>

2). <body>

3). <html>

4). document

如图:

IE5.5及早期版本会跳过 <html>
元素(从 <body> 直接到 document )。现代浏览器中的事件会一直冒泡到 window 对象。

1.2 事件捕捉(了解)

**事件捕获的意思是最不具体的节
点应该最先收到事件,而最具体的节点应该最后收到事件。事件捕获实际上是为了在事件到达最终目标
前拦截事件**。

如果前面的例子使用事件捕获,则点击 <div> 元素会以下列顺序触发click 事件:

(1) document

(2) <html>

(3) <body>

(4) <div>

**由于旧版本浏览器不支持,因此实际当中几乎不会使用事件捕获。通常建议使用事件冒泡,特殊情
况下可以使用事件捕获。**

1.3 Dom事件流 (掌握)

DOM2 Events 规范规定事件流分为 3 个阶段:事件捕捉、达到目标、事件冒泡

第一步:事件捕捉最先发生,为提前拦截事件提供可能。第二部:实际的目标元素接收到事件。第三步:最后一个阶段是冒泡,最迟要在这个阶段响应事件。以上面那个 HTML 为例:点击 <div> 元素会以如下图 所示的顺序触发事件。

==**在 DOM 事件流中,实际的目标( <div> 元素)在捕获阶段不会接收到事件。这是因为捕获阶段从
document 到 <html> 再到 <body> 就结束了。**== 下一阶段,即会在 <div> 元素上触发事件的“到达目标”
阶段,通常在事件处理时被认为是冒泡阶段的一部分(稍后讨论)。然后,冒泡阶段开始,事件反向传
播至文档。

大多数支持 DOM事件流的浏览器实现了一个小小的拓展。虽然 DOM2 Events规范明确捕获阶段不
命中事件目标,==**但现代浏览器都会在捕获阶段在事件目标上触发事件。最终结果是在事件目标上有两个
机会来处理事件。**==

注意:注意 所有现代浏览器都支持 DOM事件流,只有 IE8 及更早版本不支持。

2.事件处理程序

事件意味着用户或浏览器执行的某种动作。比如,单击( click )、加载( load )、鼠标悬停
( mouseover )。为响应事件而调用的函数被称为事件处理程序(或事件监听器)。事件处理程序的名字
以 "on" 开头,因此 click 事件的处理程序叫作 onclick ,而 load 事件的处理程序叫作 onload 。有
很多方式可以指定事件处理程序。

2.1 HTML事件处理程序(了解)

特定元素支持的每个事件的代码都可以像 HTML 属性一样的写法。此时属性
的值必须是能够执行的 JavaScript 代码。
例如:

<input type="button" value="Click Me" onclick="console.log('Clicked')"/>

注意:因为属性的值是 JavaScript 代码,所以必须要先经过转义 HTML 语法字符才能使用,比如和号( & )、双引号( " )、小于号( < )和大于号( > )。

在 HTML 中定义的事件处理程序可以调用在页面其他地方定义的脚本:
<script>
function showMessage() {
    console.log("Hello world!");
}
</script>
<input type="button" value="Click Me" onclick="showMessage()"/>
作为事件处理程序执行的代码可以访问全局作用域中的一切。

以这种方式指定的事件处理程序,首先,会创建一个函数来封装属性的值。这个
函数有一个特殊的局部变量 event ,其中保存的就是 event 对象

<!-- 输出"click" -->
<input type="button" value="Click Me" onclick="console.log(event.type)">
在下面这个函数中, this 值相当于事件的目标元素:
<!-- 输出"Click Me" -->
<input type="button" value="Click Me" onclick="console.log(this.value)">
**这个动态创建的函数的用域链被扩展了。在这个函数中,

document 和元素自身的成员都可以被当成局部变量来访问**

这意味着事件处理程序可以更方便地访问自己的属性。
例如:
```
<!-- 输出"Click Me" -->
<input type="button" value="Click Me" onclick="console.log(value)">
```
如果这个元素是一个表单输入框,则作用域链中还会包含表单元素,事件处理程序对应的函数等价
于如下这样:

```html
<form method="post">
<input type="text" name="username" value="">
<input type="button" value="Echo Username" onclick="console.log(username.value)">
</form>
```

在 HTML 中指定事件处理程序有一些问题:

时机问题

有可能 HTML 元素已经显示
在页面上,用户都与其交互了,而事件处理程序的代码还无法执行,这样就会发生错误,

解决方法:

大多数 HTML 事件处理程序会封装在 try / catch 块中,
以便在这种情况下静默失败,如下面的例子所示:

<input type="button" value="Click Me" onclick="try{showMessage();}catch(ex) {}">
浏览器兼容问题

对事件处理程序作用域链的扩展在不同浏览器中可能导致不同的结果

使用 HTML 指定事件处理程序的最后一个问题是 HTML 与 JavaScript 强耦合

所以:==**很多开发者不使用 HTML
事件处理程序,而使用 JavaScript 指定事件处理程序**==

2.2 DOM0事件处理程序(掌握)

在 JavaScript 中指定事件处理程序的传统方式是把一个函数赋值给(DOM 元素的)一个事件处理程
序属性。
写法如下:

let btn = document.getElementById("myBtn");
btn.onclick = function() {
    console.log("Clicked");
};

事件处理程
序会在元素的作用域中运行,即 this 等于元素。下面的例子演示了使用 this 引用元素本身:

let btn = document.getElementById("myBtn");
btn.onclick = function() {
    console.log(this.id); // "myBtn"
};

**以这种方式添加事件处理程序是注册在事件流的
冒泡阶段的。**

通过将事件处理程序属性的值设置为 null ,可以移除通过 DOM0 方式添加的事件处理程序,如下
面的例子所示:

btn.onclick = null; 
// 把事件处理程序设置为 null ,再点击按钮就不会执行任何操作了。

**注意:如果事件处理程序是在 HTML 中指定的,则 onclick 属性的值是一个包装相应
HTML 事件处理程序属性值的函数。这些事件处理程序也可以通过在 JavaScript 中将相应
属性设置为 null 来移除。**

2.3 DOM2事件处理程序(掌握)

DOM2 Events 为事件处理程序的赋值和移除定义了两个方法:addEventListener()removeEventListener(),这两个方法暴露在所有 DOM 节点上,他们接收3个参数事件名事件处理函数一个布尔值,true 表示在捕获阶段调用事件处理函数,false(默认值)表示在冒泡阶段调用事件处理程序
例如:

let btn = document.getElementById("myBtn");
btn.addEventListener("click", () => {
    console.log(this.id);
}, false);

使用 DOM2
方式的主要优势是可以为同一个事件添加多个事件处理程序:

let btn = document.getElementById("myBtn");
btn.addEventListener("click", () => {
    console.log(this.id);
}, false);
btn.addEventListener("click", () => {
    console.log("Hello world!");
}, false);

多个事件处理程序以添加顺序来触发,因此前面的代码会先
打印元素 ID,然后显示消息“Hello world!”。

==通过 addEventListener() 添加的事件处理程序只能使用 removeEventListener() 并传入与添
加时同样的参数来移除。这意味着使用 addEventListener() 添加的匿名函数无法移除==

大多数情况下,事件处理程序会被添加到事件流的冒泡阶段,主要原因是跨浏览器兼容性好。把事
件处理程序注册到捕获阶段通常用于在事件到达其指定目标之前拦截事件。如果不需要拦截,则不要使
用事件捕获。

2.4 IE事件处理程序(了解)

IE 实现了与 DOM 类似的方法,即 attachEvent() 和 detachEvent() 。这两个方法接收两个同样
的参数:事件处理程序的名字和事件处理函数。因为 IE8 及更早版本只支持事件冒泡,所以使用
attachEvent() 添加的事件处理程序会添加到冒泡阶段。详情参考 (红宝书第四版497页)

2.5 跨浏览器事件处理程序(了解)

自己编写跨浏览
器事件处理代码也很简单,主要依赖能力检测。要确保事件处理代码具有最大兼容性,只需要让代码在
冒泡阶段运行即可。详情可参考 (红宝书第四版498页)

3. 事件对象

在 DOM 中发生事件时,所有相关信息都会被收集并存储在一个名为 event 的对象中。这个对象包
含了一些基本信息,比如导致事件的元素、发生的事件类型,以及可能与特定事件相关的任何其他数据。
例如,鼠标操作导致的事件会生成鼠标位置信息,而键盘操作导致的事件会生成与被按下的键有关的信
息。所有浏览器都支持这个 event 对象,尽管支持方式不同。

3.1 DOM对象事件(理解)

在 DOM 合规的浏览器中,event 对象是传给事件处理函数的唯一参数。不管以哪种方式(DOM0或者DOM2)指定事件处理程序,都会传入这个 event 对象。
下面的例子展示了在两种方式下都可以使
用事件对象:

let btn = document.getElementById("myBtn");
btn.onclick = function(event) {
    console.log(event.type); // "click"
};
btn.addEventListener("click", (event) => {
    console.log(event.type); // "click"
}, false);

在通过 HTML 属性指定的事件处理程序中,同样可以使用变量 event 引用事件对象。例如:

<input type="button" value="Click Me" onclick="console.log(event.type)">

如前所述,事件对象包含与特定事件相关的属性和方法。不同的事件生成的事件对象也会包含不同
的属性和方法。不过,所有事件对象都会包含下表列出的这些公共属性和方法。(本文只列举了下面几个公共属性,更多公共参数具体可参考《红宝书第四版500页》)

属性/方法类 型读/写说明bubbles布尔值只读表示事件是否冒泡currentTarget元素只读当前事件处理程序所在的元素eventPhase整数只读表示调用事件处理程序的阶段:1代表捕获阶段,2代表到达目标,3代表冒泡阶段stopPropagation()函数只读用于取消所有后续事件捕获或事件冒泡。只有 bubbles为 true 才可以调用这个方法target元素只读事件目标type字符串只读被触发的事件类型

在事件处理程序内部, this 对象始终等于 currentTarget 的值,而 target 只包含事件的实际
目标。如果事件处理程序直接添加在了意图的目标,则 this 、 currentTarget 和 target 的值是一样
的。下面的例子展示了这两个属性都等于 this 的情形:

let btn = document.getElementById("myBtn");
btn.onclick = function(event) {
    console.log(event.currentTarget === this); // true
    console.log(event.target === this); // true
};

如果这个事件处理程序是添加到按钮的父节点(如 document.body )上,
那么它们的值就不一样了。例如:

document.body.onclick = function(event) {
    console.log(event.currentTarget === document.body); // true
    console.log(this === document.body); // true
    console.log(event.target === document.getElementById("div"));     // true
    console.log(event.currentTarget === event.target)
};

**这种情况下点击按钮, ==this== 和 ==currentTarget== 都等于 document.body ,这是因为==它是注册事件
处理程序的元素==。==而 target 属性等于按钮本身,这是因为那才是 click 事件真正的目标, target 属性等于触发事件的文档中最深的节点==。由于按钮
本身并没有注册事件处理程序,因此 click 事件冒泡到 document.body ,从而触发了在它上面注册的
处理程序。**

type 属性在一个处理程序处理多个事件时很有用。比如下面的处理程序中就使用了 event.type :

let btn = document.getElementById("myBtn");
let handler = function(event) {
    switch(event.type) {
        case "click":
            console.log("Clicked");
        break;
        case "mouseover":
            event.target.style.backgroundColor = "red";
        break;
        case "mouseout":
            event.target.style.backgroundColor = "";
        break;
    }
};
btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;

注意:event 对象只在事件处理程序执行期间存在,一旦执行完毕,就会被销毁。

3.2 IE事件对象(了解)

与 DOM 事件对象不同, IE 事件对象可以基于事件处理程序被指定的方式以不同方式来访问。

**如果

事件处理程序是使用 DOM0 方式指定的,则 event 对象只是 window 对象的一个属性**

**如果是使用 HTML属性方式指定的事件处理程序,则 event 对象同样可以通过变量 event 访问(与

DOM 模型一样)**。

详情请参考(红宝书第四版502页

3.3 跨浏览器事件(了解)

本文简约只是简约的描述,具体可参考(红宝书第四版505页


虽然 DOM 和 IE 的事件对象并不相同,但它们有足够的相似性可以实现跨浏览器方案。

DOM事件对象中包含 IE 事件对象的所有信息和能力,只是形式不同。这些共性可让两种事件模型之间的映射成
为可能。

var EventUtil = {
    addHandler: function(element, type, handler) {
    // 为节省版面,删除了之前的代码。具体的代码可参考(红宝书498页)
    },
    getEvent: function(event) {
        return event ? event : window.event;
    },
    getTarget: function(event) {
        return event.target || event.srcElement;
    },
    preventDefault: function(event) {
        if (event.preventDefault) {
            event.preventDefault();
        } else {
            event.returnValue = false;
        }
    },
    removeHandler: function(element, type, handler) {
        // 为节省版面,删除了之前的代码。具体的代码可参考(红宝书498页)
    },
    stopPropagation: function(event) {
        if (event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    }
};


面是使用 EventUtil 中这个方法统一获取 event 对象的一个例子:

btn.onclick = function(event) {
    event = EventUtil.getEvent(event);
};
4. 事件类型(掌握)

DOM3 Events定义了如下事件类型。

用户界面事件( UIEvent ):涉及与 BOM交互的通用浏览器事件。焦点事件( FocusEvent ):在元素获得和失去焦点时触发。鼠标事件( MouseEvent ):使用鼠标在页面上执行某些操作时触发。滚轮事件( WheelEvent ):使用鼠标滚轮(或类似设备)时触发。输入事件( InputEvent ):向文档中输入文本时触发。键盘事件( KeyboardEvent ):使用键盘在页面上执行某些操作时触发。合成事件( CompositionEvent ):在使用某种 IME(Input Method Editor,输入法编辑器)输入

字符时触发。

4.1 用户界面事件(掌握)

==**重点:知道了 img 元素可以触发 load 事件、error 事件。
知道了 resize 事件、scroll事件在不同浏览器中的差异,以及处理方法**==

用户界面事件或 UI 事件不一定跟用户操作有关。这类事件在 DOM 规范出现之前就已经以某种形
式存在了,保留它们是为了向后兼容。UI 事件主要有以下几种。

<font color="red">DOMActivate</font>:元素被用户通过鼠标或键盘操作激活时触发(比 click 或 keydown 更通用)。

这个事件在 DOM3 Events 中已经废弃。因为浏览器实现之间存在差异,所以不要使用它。

load :在 window 上当页面加载完成后触发,在窗套( <frameset> )上当所有窗格( <frame> )

都加载完成后触发,在 <img> 元素上当图片加载完成后触发,在 <object> 元素上当相应对象加
载完成后触发。

unload :在 window 上当页面完全卸载后触发,在窗套上当所有窗格都卸载完成后触发,在

<object> 元素上当相应对象卸载完成后触发。

abort :在 <object> 元素上当相应对象加载完成前被用户提前终止下载时触发。error :在 window 上当 JavaScript 报错时触发,在 <img> 元素上当无法加载指定图片时触发,

<object> 元素上当无法加载相应对象时触发,在窗套上当一个或多个窗格无法完成加载时
触发。

select :在文本框( <input> 或 textarea )上当用户选择了一个或多个字符时触发。resize :在 window 或窗格上当窗口或窗格被缩放时触发。scroll :当用户滚动包含滚动条的元素时在元素上触发。 <body> 元素包含已加载页面的滚动条。

大多数 HTML 事件与 window 对象和表单控件有关。

load事件

可以用load事件实现图片预加载具体可参考(红宝书第四版)

window.addEventListener("load", () => {
    let image = new Image();
    image.addEventListener("load", (event) => {
        console.log("Image loaded!");
    });
    image.src = "smile.gif";
});

这里调用 Image 构造函数创建了一个新图片,并给它设置了事件处理程序。有些浏览器会把 Image
对象实现为 <img> 元素,但并非所有浏览器都如此。所以最好把它们看成是两个东西。

unload事件

与 load 事件相对的是 unload 事件, unload 事件会在文档卸载完成后触发。 unload 事件一般是
在从一个页面导航到另一个页面时触发,最常用于清理引用,以避免内存泄漏。

resize 事件

当浏览器窗口被缩放到新高度或宽度时,会触发 resize 事件。这个事件在 window 上触发,因此
可以通过 JavaScript 在 window 上或者为 <body> 元素添加 onresize 属性来指定事件处理程序。优先使
用 JavaScript 方式:

window.addEventListener("resize", (event) => {
    console.log("Resized");
});

**不同浏览器在决定何时触发 resize 事件上存在重要差异。无论如何,都应该避免在这个事件处理程序中执行过多
计算。否则可能由于执行过于频繁而导致浏览器响应明确变慢。**

注意:浏览器窗口在最大化和最小化时也会触发 resize 事件。

scroll 事件

虽然 scroll 事件发生在 window 上,但实际上反映的是页面中相应元素的变化。在不同浏览器中有差异。下面的代码演示了如何处理差异:(具体可参考红宝书第四版509页

window.addEventListener("scroll", (event) => {
    if (document.compatMode == "CSS1Compat") {
        console.log(document.documentElement.scrollTop);
    } else {
        console.log(document.body.scrollTop);
    }
});
4.2 焦点事件

焦点事件在页面元素获得或失去焦点时触发。这些事件可以与 document.hasFocus() 和
document.activeElement 一起为开发者提供用户在页面中导航的信息。焦点事件有以下 6 种。

blur :当元素失去焦点时触发。这个事件不冒泡,所有浏览器都支持。<font color="red">DOMFocusIn</font> :当元素获得焦点时触发。这个事件是 focus 的冒泡版。Opera 是唯一支持这个事

件的主流浏览器。DOM3 Events废弃了 DOMFocusIn ,推荐 focusin

<font color="red">DOMFocusOut</font> :当元素失去焦点时触发。这个事件是 blur 的通用版。Opera 是唯一支持这个事

件的主流浏览器。DOM3 Events废弃了 DOMFocusOut ,推荐 focusout 。

focus :当元素获得焦点时触发。这个事件不冒泡,所有浏览器都支持focusin :当元素获得焦点时触发。这个事件是 focus 的冒泡版focusout :当元素失去焦点时触发。这个事件是 blur 的通用版。

焦点事件中的两个主要事件是 focus 和 blur ,这两个事件在 JavaScript 早期就得到了浏览器支持。
它们最大的问题是不冒泡。这导致IE后来又增加了 focusin 和 focusout ,IE 新增的这两个事件已经被 DOM3 Events标准化。

当焦点从页面中的一个元素移到另一个元素上时,会==依次发生==如下事件。

(1) focuscout 在失去焦点的元素上触发。

(2) focusin 在获得焦点的元素上触发。

(3) blur 在失去焦点的元素上触发。

(4) DOMFocusOut 在失去焦点的元素上触发。

(5) focus 在获得焦点的元素上触发。

(6) DOMFocusIn 在获得焦点的元素上触发。

其中, blur 、 DOMFocusOut 和 focusout 的事件目标是失去焦点的元素,而 focus 、 DOMFocusIn
和 focusin 的事件目标是获得焦点的元素。

4.3 鼠标和滚轮事件

DOM3 Events
鼠标是用户的主要定位设备。DOM3 Events 定义了 9 种鼠标事件。

click :在用户单击鼠标主键(通常是左键)或按键盘回车键时触发。这主要是基于无障碍的考

虑,让键盘和鼠标都可以触发 onclick 事件处理程序。

dblclick :在用户双击鼠标主键(通常是左键)时触发。这个事件不是在 DOM2 Events中定义

的,但得到了很好的支持,DOM3 Events 将其进行了标准化。

mousedown :在用户按下任意鼠标键时触发。这个事件不能通过键盘触发。mouseenter :在用户把鼠标光标从元素外部移到元素内部时触发。这个事件不冒泡,也不会在

光标经过后代元素时触发。 mouseenter 事件不是在 DOM2 Events 中定义的,而是 DOM3 Events
中新增的事件。

mousemove :在鼠标光标在元素上移动时反复触发。这个事件不能通过键盘触发。mouseout :在用户把鼠标光标从一个元素移到另一个元素上时触发。移到的元素可以是原始元

素的外部元素,也可以是原始元素的子元素。这个事件不能通过键盘触发。

mouseover :在用户把鼠标光标从元素外部移到元素内部时触发。这个事件不能通过键盘触发。mouseup :在用户释放鼠标键时触发。这个事件不能通过键盘触发。

**页面中的所有元素都支持鼠标事件。除了 mouseenter 和 mouseleave ,所有鼠标事件都会冒泡,
都可以被取消,而这会影响浏览器的默认行为。**
由于事件之间存在关系,因此取消鼠标事件的默认行为也会影响其他事件。
例如:如果 mousedown 和 mouseup 中的任意一个事件被取消,那么 click 事件就不会触发。
这 4 个事件永远会按照如下顺序触发:

(1) mousedown

(2) mouseup

(3) click

(4) mousedown

(5) mouseup

(6) click

(7) dblclick

click 和 dblclick 在触发前都依赖其他事件触发, mousedown 和 mouseup 则不会受其他事件影响。
IE8 及更早版本的实现中有个问题,这会导致双击事件跳过第二次 mousedown 和 click 事件。相
应的顺序变成了:

(1) mousedown

(2) mouseup

(3) click

(4) mouseup

(5) dblclick

鼠标事件还有一个名为滚轮事件的子类别。滚轮事件只有一个事件 mousewheel ,反映的是鼠标滚
轮或带滚轮的类似设备上滚轮的交互。

本小节以下详情内容可查看红宝书

客户端坐标 (红宝书第四版511页)页面坐标 (红宝书第四版512页)屏幕坐标 (红宝书第四版513页

鼠标事件不仅是在浏览器窗口中发生的,也是在整个屏幕上发生的。可以通过 event 对象的
screenX 和 screenY 属性获取鼠标光标在屏幕上的坐标。下图 展示了浏览器中触发鼠标事件的光标
的屏幕坐标。

可以像下面这样获取鼠标事件的屏幕坐标:

let div = document.getElementById("myDiv");
div.addEventListener("click", (event) => {
    console.log(`Screen coordinates: ${event.screenX}, ${event.screenY}`);
});
修饰键(红宝书第四版514页

虽然鼠标事件主要是通过鼠标触发的,但有时候要确定用户想实现的操作,还要考虑键盘按键的状
态。键盘上的修饰键 Shift、Ctrl、Alt 和 Meta 经常用于修改鼠标事件的行为。DOM 规定了 4 个属性来表
示这几个修饰键的状态: shiftKey 、 ctrlKey 、 altKey 和 metaKey 。这几属性会在各自对应的修饰
键被按下时包含布尔值 true ,没有被按下时包含 false 。在鼠标事件发生的,可以通过这几个属性来
检测修饰键是否被按下。

相关元素 (红宝书第四版514页

只有在元素上单击鼠标主键(或按下键盘上的回车键)时 click 事件才会触发,因此按键信息并
不是必需的。对 mousedown 和 mouseup 事件来说, event 对象上会有一个 button 属性,表示按下或
释放的是哪个按键。DOM 为这个 button 属性定义了 3 个值:0 表示鼠标主键、1 表示鼠标中键(通常
也是滚轮键)、2 表示鼠标副键。按照惯例,鼠标主键通常是左边的按键,副键通常是右边的按键。

鼠标按键 (红宝书第四版516页)额外事件信息 (红宝书第四版516页)mousewheel 事件 (红宝书第四版516页)触摸屏设备 (红宝书第四版517页

iOS 和 Android 等触摸屏设备的实现大相径庭,因为触摸屏通常不支持鼠标操作。在为触摸屏设备
开发时,要记住以下事项。

不支持 dblclick 事件。双击浏览器窗口可以放大,但没有办法覆盖这个行为。**单指点触屏幕上的可点击元素会触发 mousemove 事件。如果操作会导致内容变化,则不会再触

发其他事件。如果屏幕上没有变化,则会相继触发 mousedown 、 mouseup 和 click 事件。点
触不可点击的元素不会触发事件。可点击元素是指点击时有默认动作的元素(如链接)或指定
了 onclick 事件处理程序的元素。**

mousemove 事件也会触发 mouseover 和 mouseout 事件。双指点触屏幕并滑动导致页面滚动时会触发 mousewheel 和 scroll 事件。无障碍问题 (红宝书第四版517页)4.4 键盘与输入事件

键盘事件包含 3 个事件:

keydown ,用户按下键盘上某个键时触发,而且持续按住会重复触发。keypress ,用户按下键盘上某个键并产生字符时触发,而且持续按住会重复触发。Esc 键也会

触发这个事件。DOM3 Events废弃了 keypress 事件,而推荐 textInput 事件。

keyup ,用户释放键盘上某个键时触发。

**这里 keydown 和 keypress 事件会在文本框出现变化之前触发,而 keyup
事件会在文本框出现变化之后触发。如果一个字符键被按住不放, keydown 和 keypress 就会重复触
发,直到这个键被释放。**

注意:**键盘事件支持与鼠标事件相同的修饰键。 shiftKey 、 ctrlKey 、 altKey 和 metaKey
属性在键盘事件中都是可用的。IE8 及更早版本不支持 metaKey 属性。**

DOM3 的变化

尽管所有浏览器都实现了某种形式的键盘事件,DOM3 Events 还是做了一些修改。比如,DOM3
Events规范并未规定 charCode 属性,而是定义了 key 和 char 两个新属性。

其中, key 属性用于替代 keyCode ,且包含字符串。在按下字符键时, key 的值等于文本字符(如
“k”或“M”);在按下非字符键时, key 的值是键名(如“Shift”或“ArrowDown”)。 char 属性在按
下字符键时与 key 类似,在按下非字符键时为 null 。

==由于缺乏跨浏览器支持,因此不建议使用 key 、 keyIdentifier 、和 char 。==
==**DOM 和 IE 的 event 对
象都支持 keyCode 属性。**==

一旦有了字母编码,就可以使用 String.fromCharCode() 方法将其转换为实际的字符了。

window.addEventListener("keypress", (event) => {
    console.log( String.fromCharCode(event.keyCode));
});

本小节从 4.textInput以后的内容了解即可

textInput 事件(红宝书第四版521页

DOM3 Events 规范增加了一个名为 textInput 的事件,其在字符被输入到可编辑区域时触发。作
为对 keypress 的替代, textInput 事件的行为有些不一样。一个区别是 keypress 会在任何可以获
得焦点的元素上触发,而 textInput 只在可编辑区域上触发。另一个区别是 textInput 只在有新字
符被插入时才会触发,而 keypress 对任何可能影响文本的键都会触发(包括退格键)。

设备上的键盘事件(红宝书第四版522页

任天堂 Wii 会在用户按下 Wii 遥控器上的键时触发键盘事件。虽然不能访问 Wii 遥控器上所有的键,
但其中一些键可以触发键盘事件

4.5 合成事件(了解即可)

合成事件是 DOM3 Events 中新增的,用于处理通常使用 IME 输入时的复杂输入序列。

4.6 <font color="red">变化事件</font>(已废弃)

这些事件已经被废弃,浏览器已经在有计划地停止对它们的支持。变化事件已经被
Mutation Observers所取代

4.7 HTML5 事件(暂时了解即可,后面必须知道)

DOM 规范并未涵盖浏览器都支持的所有事件。很多浏览器根据特定的用户需求或使用场景实现了
自定义事件。HTML5 详尽地列出了浏览器支持的所有事件。本节讨论 HTML5 中得到浏览器较好支持
的一些事件。注意这些并不是浏览器支持的所有事件。

contextmenu 事件

Windows 95 通过单击鼠标右键为 PC 用户增加了上下文菜单的概念。不久,这个概念也在 Web 上得
以实现。

contextmenu 事件冒泡,因此只要给 document 指定一个事件处理程序就可以处理页面上的所有
同类事件。
这个事件在所有浏览器中都可以取消,在 DOM 合规的浏览器
中使用 event.preventDefault() ,在 IE8 及更早版本中将 event.returnValue 设置为 false 。contextmenu 事件应该算一种鼠标事件,因此 event 对象上的很多属性都与光标位置有关。通常,自
定义的上下文菜单都是通过 oncontextmenu 事件处理程序触发显示,并通过 onclick 事件处理程序
触发隐藏的。来看下面的例子:

<!DOCTYPE html>
<html>

<head>
  <title>ContextMenu Event Example</title>
</head>

<body>
  <div id="myDiv">Right click or Ctrl+click me to get a custom context menu.
    Click anywhere else to get the default context menu.</div>
  <ul id="myMenu" >
    <li><a href="http://www.somewhere.com"> somewhere</a></li>
    <li><a href="http://www.wrox.com">Wrox site</a></li>
    <li><a href="http://www.somewhere-else.com">somewhere-else</a></li>
  </ul>
</body>
<script>
  window.addEventListener("load", (event) => {
    let div = document.getElementById("myDiv");
    div.addEventListener("contextmenu", (event) => {
      event.preventDefault();
      let menu = document.getElementById("myMenu");
      menu.style.left = event.clientX + "px";
      menu.style.top = event.clientY + "px";
      menu.style.visibility = "visible";
    });
    document.addEventListener("click", (event) => {
      document.getElementById("myMenu").style.visibility = "hidden";
    });
  });
</script>

</html>

当鼠标点击右键就会出现这样的效果,类似于在windows桌面上点击右键弹出的菜单:

beforeunload 事件

beforeunload 事件会在 window 上触发,用意是给开发者提供阻止页面被卸载的机会。这个事件
会在页面即将从浏览器中卸载时触发,如果页面需要继续使用,则可以不被卸载。
这个事件会向用户显示一个确认框,其中的消
息表明浏览器即将卸载页面,并请用户确认是希望关闭页面,还是继续留在页面上,如图:

实现上图的代码:

window.addEventListener("beforeunload", (event) => {
let message = "I'm really going to miss you if you go.";
    event.returnValue = message;
    return message;
});

DOMContentLoaded 事件

**window 的 load 事件会在页面完全加载后触发,因为要等待很多外部资源加载完成,所以会花费
较长时间。而 DOMContentLoaded 事件会在 DOM 树构建完成后立即触发,而不用等待图片、JavaScript
文件、CSS 文件或其他资源加载完成**。相对于 load 事件, DOMContentLoaded 可以让开发者在外部资
源下载的同时就能指定事件处理程序,从而让用户能够更快地与页面交互。

要处理 DOMContentLoaded 事件,需要给 document 或 window 添加事件处理程序(实际的事件
目标是 document ,但会冒泡到 window )。

document.addEventListener("DOMContentLoaded", (event) => {
    console.log("Content loaded");
});

DOMContentLoaded 事件的 event 对象中不包含任何额外信息(除了 target 等于 document )。

**DOMContentLoaded 事件通常用于添加事件处理程序或执行其他DOM操作。这个事件始终在 load
事件之前触发**。

对于不支持 DOMContentLoaded 事件的浏览器,可以使用超时为 0 的 setTimeout() 函数,通过
其回调来设置事件处理程序,比如:

setTimeout(() => {
    // 在这里添加事件处理程序
}, 0);

**考虑到各种影响因素,以上代码不一定保证能在 load 事
件之前执行超时回调。**

readystatechange 事件(红宝书第四版525页)

这个有点神秘的事件旨在提供文档或元素加载状态的信息,但行为有时候并不稳定。

**注意 使用 readystatechange 只能尽量模拟 DOMContentLoaded ,但做不到分毫不差。
load 事件和 readystatechange 事件发生的顺序在不同页面中是不一样的。**

pageshow 与 pagehide 事件(红宝书第四版527页)

Firefox 和 Opera 开发了一个名为往返缓存(bfcache,back-forward cache)的功能,此功能旨在使用
浏览器“前进”和“后退”按钮时加快页面之间的切换。

Firefx决定提供一些事件,把往返缓存的行为暴露出来。

hashchange 事件

HTML5 增加了 hashchange 事件,用于在 URL 散列值(URL 最后 # 后面的部分)发生变化时通知
开发者。这是因为开发者经常在 Ajax 应用程序中使用 URL 散列值存储状态信息或路由导航信息。

onhashchange 事件处理程序必须添加给 window ,每次 URL 散列值发生变化时会调用它。

event
对象有两个新属性: oldURL 和 newURL 。这两个属性分别保存变化前后的 URL,而且是包含散列值的
完整 URL。下面的例子展示了如何获取变化前后的 URL:

window.addEventListener("hashchange", (event) => {
    console.log(`Old URL: ${event.oldURL}, New URL: ${event.newURL}`);
});

如果想确定当前的散列值,最好使用 location 对象:

window.addEventListener("hashchange", (event) => {
    console.log(`Current hash: ${location.hash}`);
});
4.8 设备事件 (了解)(红宝书第四版528页)

随着智能手机和平板计算机的出现,用户与浏览器交互的新方式应运而生。为此,一批新事件被发
明了出来。设备事件可以用于确定用户使用设备的方式。W3C 在 2011 年就开始起草一份新规范,用于
定义新设备及设备相关的事件。

orientationchange 事件
苹果公司在移动 Safari 浏览器上创造了 orientationchange 事件,以方便开发者判断用户的设备
是处于垂直模式还是水平模式。deviceorientation 事件(红宝书第四版529页)devicemotion 事件(红宝书第四版531页)4.9 触摸及手势事件(了解)

Safari 为 iOS 定制了一些专有事件,以方便开发者。因为 iOS 设备没有鼠标和键盘,所以常规的鼠
标和键盘事件不足以创建具有完整交互能力的网页。同时,WebKit 也为 Android 定制了很多专有事件,
成为了事实标准,并被纳入 W3C 的 Touch Events规范。本节介绍的事件只适用于触屏设备。

触摸事件

触摸事件有如下几种:

touchstart :手指放到屏幕上时触发(即使有一个手指已经放在了屏幕上)。touchmove :手指在屏幕上滑动时连续触发。在这个事件中调用 preventDefault() 可以阻止滚动。touchend :手指从屏幕上移开时触发。touchcancel :系统停止跟踪触摸时触发。文档中并未明确什么情况下停止跟踪。

**这些事件都会冒泡,也都可以被取消。尽管触摸事件不属于 DOM 规范,但浏览器仍然以兼容 DOM
的方式实现了它们。因此,每个触摸事件的 event 对象都提供了鼠标事件的公共属性: bubbles 、
cancelable 、 view 、 clientX 、 clientY 、 screenX 、 screenY 、 detail 、 altKey 、 shiftKey 、
ctrlKey 和 metaKey 。**

除了这些公共的 DOM 属性,触摸事件还提供了以下 3 个属性用于跟踪触点。

touches : Touch 对象的数组,表示当前屏幕上的每个触点。targetTouches : Touch 对象的数组,表示特定于事件目标的触点。changedTouches : Touch 对象的数组,表示自上次用户动作之后变化的触点。

每个 Touch 对象都包含下列属性。

clientX :触点在视口中的 x 坐标。clientY :触点在视口中的 y 坐标。identifier :触点 ID。pageX :触点在页面上的 x 坐标。pageY :触点在页面上的 y 坐标。screenX :触点在屏幕上的 x 坐标。screenY :触点在屏幕上的 y 坐标。target :触摸事件的事件目标。

当手指点触屏幕上的元
素时,依次会发生如下事件(包括鼠标事件):
(1) touchstart

(2) mouseover

(3) mousemove (1 次)

(4) mousedown

(5) mouseup

(6) click

(7) touchend

手势事件(红宝书第四版533页)

iOS 2.0 中的 Safari 还增加了一种手势事件。手势事件会在两个手指触碰屏幕且相对距离或旋转角度
变化时触发。手势事件有以下 3 种。

gesturestart :一个手指已经放在屏幕上,再把另一个手指放到屏幕上时触发。gesturechange:任何一个手指在屏幕上的位置发生变化时触发。gestureend: 其中一个手指离开屏幕时触发。

**注意 触摸事件也会返回 rotation 和 scale 属性,但只在两个手指触碰屏幕时才会变
化。一般来说,使用两个手指的手势事件比考虑所有交互的触摸事件使用起来更容易一些。**

事件参考(红宝书第四版534页)

本节给出了 DOM 规范、HTML5 规范,以及概述事件行为的其他当前已发布规范中定义的所有浏
览器事件。这些事件按照 API 和/或规范分类。

5. 内存于性能
特别申明:本文内容来源网络,版权归原作者所有,如有侵权请立即与我们联系(cy198701067573@163.com),我们将及时处理。

Tags 标签

加个好友,技术交流

1628738909466805.jpg