一 定义
事件处理程序/事件监听器:为响应事件而调用的函数
- 事件处理程序的名字以"on"开头
eg. click事件 -> onclick load事件 -> onload
二 HTML事件处理程序
1、表现方式:
方式1:在HTML指定动作指令
- 特定元素支持的每个事件都可以使用事件处理程序的名字以 HTML 属性的形式来指定
- 此时属性的值必须是能够执行的 JavaScript 代码
<input type="button" value="Click Me" onclick="console.log('Clicked')"/>
方式2:在HTML指定动作指令调用在页面其他地方定义的脚本
- 函数是在单独的<script>元素中定义的,而且也可以在外部文件中定义。作为事件处理程序执行的代码可以访问全局作用域中的一切
- 特点:
- 会创建一个函数来封装属性的值:这个函数有一个特殊的局部变量 event,其中保存的就是 event 对象
- 在这个函数中,this 值相当于事件的目标元素
<!-- 输出"click" -->
<input type="button" value="Click Me" onclick="console.log(event.type)">
<!-- 输出"Click Me" -->
<input type="button" value="Click Me" onclick="console.log(this.value)">
2、缺点
- 异步处理时机问题
// 有可能 HTML 元素已经显示在页面上,用户都与其交互了,而事件处理程序的代码还无法执行。比如在前面的例子中,如果showMessage()函数是在页面后面,在按钮中代码的后面定义的,那么当用户在 showMessage()函数被定义之前点击按钮时,就会发生错误。
// 为此,大多数 HTML 事件处理程序会封装在 try/catch 块中,以便在这种情况下静默失败,如下面的例子所示:
<input type="button" value="Click Me" onclick="try{showMessage();}catch(ex) {}">
//这样,如果在 showMessage()函数被定义之前点击了按钮,就不会发生 JavaScript 错误了,这是因为错误在浏览器收到之前已经被拦截了。
- 对事件处理程序作用域链的扩展在不同浏览器导致不同的结果
不同 JavaScript 引擎中标识符解析的规则存在差异,因此访问无限定的对象成员可能导致错误
- HTML与JavaScript强耦合
如果需要修改事件处 理程序,则必须在两个地方,即 HTML 和 JavaScript 中,修改代码。
解决方案:使用 JavaScript 指定事件处理程序
3、作用域:
动态创建的包装函数还有一个特别有意思的地方,就是其作用域链被扩展了。
在这个函数中, document 和元素自身的成员都可以被当成局部变量来访问
三 DOM0事件处理程序
1、定义:
- 把一个函数赋值给(DOM元素的)一个事件处理程序属性
- 要使用JavaScript指定事件处理程序,必须先取得要操作对象的引用
2、使用
- 每个元素(包括window和document)都有通常小写的事件处理程序属性
let btn = document.getElementById("myBtn");
btn.onclick = function() {
console.log("Clicked");
};
- 使用 DOM0 方式为事件处理程序赋值时,所赋函数被视为元素的方法。
- 事件处理程序会在元素的作用域中运行,即 this 等于元素
let btn = document.getElementById("myBtn");
btn.onclick = function() {
console.log(this.id); // "myBtn"
};
// 点击按钮,这段代码会显示元素的 ID。这个 ID 是通过 this.id 获取的。
// 不仅仅是 id,在事件处理程序里通过 this 可以访问元素的任何属性和方法。
// 以这种方式添加事件处理程序是注册在事件流的冒泡阶段的。
- 通过将事件处理程序属性的值设置为 null,可以移除通过 DOM0 方式添加的事件处理程序
btn.onclick = null; // 移除事件处理程序 把事件处理程序设置为 null,再点击按钮就不会执行任何操作了。
四 DOM2事件处理程序
1、版本:DOM2 Events为事件处理程序的赋值和移除定义了方法
- addEventListener()
- removeEventListener()
- 作用:在所有的DOM节点上
- 参数:参数名 事件处理函数 一个布尔值
- true:表示在捕获阶段调用事件处理程序
- false(默认值):表示在冒泡阶段调用事 件处理程序
let btn = document.getElementById("myBtn");
btn.addEventListener("click", () => {
console.log(this.id);
}, false);
2、优点:可以为同一个事件添加多个事件处理程序
3、注意点:
- 调用 addEventListener()和 removeEventListener()时传入的得是同一个函数
let btn = document.getElementById("myBtn");
let handler = function() {
console.log(this.id);
};
btn.addEventListener("click", handler, false);
// 其他代码
btn.removeEventListener("click", handler, false); // 有效果
- 事件处理程序会被添加到事件流的冒泡阶段,主要原因是跨浏览器兼容性好
把事件处理程序注册到捕获阶段通常用于在事件到达其指定目标之前拦截事件
如果不需要拦截,则不要使用事件捕获
4、作用域:与 DOM0 方式类似,这个事件处理程序同样在被附加到的元素的作用域中运行
五 IE事件处理程序
1、方法
- attachEvent()
- detachEvent()
- 参数:事件处理程序的名字 事件处理函数
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function() {
console.log("Clicked");
});
区别 | DOM使用 addEventListener() | IE使用 attachEvent() |
给元素添加方法 | 可以给一个元素添加多个事件处理程序 第一个参数是"click" | 可以给一个元素添加多个事件处理程序 第一个参数是"onclick" |
事件执行顺序 | 顺序触发 | 反向触发 |
匿名函数移除 | 无法移除 | 无法移除 |
移除函数 | removeEventListener() | detachEvent() |
事件处理程序的作用域 | 事件处理程序中的 this 值等于目标元素 | 事件处理程序是在全局作用域中运行的, this 等于 window |
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function() {
console.log(this === window); // true
});
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function() {
console.log("Clicked");
});
btn.attachEvent("onclick", function() {
console.log("Hello world!");
});
// 控制台中会先打印出"Hello world!",然后再打印出"Clicked"
var btn = document.getElementById("myBtn");
var handler = function() {
console.log("Clicked");
};
btn.attachEvent("onclick", handler);
// 其他代码
btn.detachEvent("onclick", handler);
六 跨浏览器事件处理程序
1、测试:主要依赖能力检测
2、要点:要确保事件处理代码具有最大兼容性,只需要让代码在 冒泡阶段运行即可
示例:根据需要分别使用 DOM0 方式、 DOM2 方式或 IE 方式来添加事件处理程序
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;
}
}
};
3、思路:
- 首先检测传入元素上是否存在 DOM2 方式。
- 如果有 DOM2 方式,就使用该方式,传入事件类型和事件处理函数,以及表示冒泡阶段的第三个参数 false
- 否则,如果存在 IE 方式,则使用该方式。注意这时候必须在事件类型前加上"on",才能保证在 IE8 及更早版本中有效。
- 最后是使用 DOM0 方式(在现代浏览器中不会到这一步)。注意使用 DOM0 方式时使用了中括号计算属性名,并将事件处理程序或 null 赋给了这个属性
//可以像下面这样使用 EventUtil 对象:
let btn = document.getElementById("myBtn")
let handler = function() {
console.log("Clicked");
};
EventUtil.addHandler(btn, "click", handler);
// 其他代码
EventUtil.removeHandler(btn, "click", handler)