-
什么是事件流?
ans:事件流:描述从页面中接收事件的顺序
(1) 事件冒泡:事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点
(2) 事件捕获:不太具体的节点先接收事件,而具体的节点最后接收到事件
(3) “DOM2级事件”:规定事件流包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段 -
事件“捕获”和“冒泡”执行次数和事件的执行顺序
ans:按照DOM2级事件
(1) 事件执行次数:元素绑定多少个事件,就执行多少次。前提是:
- 事件确实被触发
- 事件绑定几次就算几个事件,就算类型和功能完全一样也不会被覆盖
(2) 事件执行顺序:关键是判断是否为目标元素
- 非目标元素:根据W3C的标准执行:捕获->目标元素->冒泡(不依据事件绑定顺序)
- 目标元素:依据事件绑定顺序:先绑定的事件先执行(不依据捕获冒泡标准)
- 最终顺序:父元素捕获->目标元素事件1->目标元素事件2->子元素捕获->子元素冒泡->父元素冒泡
- 子元素事件执行前提是事件确实“落”到子元素布局区域上,而不是简单的具有嵌套关系
- 事件模型
ans:即事件处理程序。事件就是用户或浏览器自身执行的某种动作,而响应某个事件的函数就叫做事件处理程序(或事件监听器)。浏览器的事件模型,就是 通过监听函数对事件作出反应。
(1) HTML事件处理程序:HTML的 on- 属性
<input type="button" value="Click Me" onclick="doSomething()">
元素的事件监听属性,都是on+事件名,例如onload, onclick。使用这个方法指定的监听代码,只会在冒泡阶段触发。
缺点:HTML代码和JavaScript代码高度耦合,若要更换事件处理程序,则要同时改动HTML代码和JavaScript代码。另外,若用户在HTML元素一出现在页面上就触发相应事件,而此时事件处理程序若不具备执行条件(例如函数还没被解析出来),就会出现错误。
(2) DOM0级事件
将一个函数赋值给事件处理程序 属性,见代码:
var btn = document.getElementById("myBtn");
btn.onclick = function() {
console.log("Clicked");
}
用这种方式指定的监听函数,同样只会在冒泡阶段触发。
缺点:同一个事件只能定义一个监听函数,如果定义两次onclick属性,后面的会覆盖前面的。
(3)DOM2级事件处理程序
“DOM2级事件”定义了两个方法:addEventListener和 removeEventListener,所有的DOM节点都包含这两个方法,它们都接收三个参数:
addEventListener(要处理的事件名,作为事件处理程序的函数,布尔值)
布尔值:true: 在捕获阶段调用事件处理程序;false:在冒泡阶段调用
见代码:
var btn = document.getElementById("myBtn");
btn.addEventListener("click", function() {
console.log(this.id);
}, false);
btn.addEventListener("click", function() {
console.log("hello world!");
}, false);
通过addEventListener()添加的事件处理程序只能通过removeEventListener()来移除,三个参数必须完全相同,所以添加的匿名函数无法删除
优点:
- 同一个事件可以添加多个监听函数。
- 能够指定在哪个阶段(捕获阶段还是冒泡阶段)触发监听函数。
- 除了 DOM 节点,其他对象(比如window、XMLHttpRequest等)也有这个接口,它等于是整个 JavaScript 统一的监听函数接口。
-
什么是事件代理?
ans:由于事件会在冒泡阶段从子节点传递到父节点,所以可以通过将子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。此即为事件代理。 -
什么是事件对象?
ans:在触发DOM上的某个元素时,会产生一个事件对象event,作为参数传给监听函数。里面包含着所有与事件有关的信息,包括导致事件的元素、事件的类型等。例如鼠标操作导致的事件中,会包含鼠标位置的信息。浏览器原生提供一个Event对象,所有的事件都是这个对象的实例,或者说继承了Event.prototype对象。Event对象本身就是一个构造函数,可以用来生成新的实例。
event = new Event(type, options);
第一个参数type是字符串,表示事件的名称;第二个参数options是一个对象,表示事件对象的配置。该对象主要有下面两个属性。
- bubbles:布尔值,可选,默认为false,表示事件对象是否冒泡。
- cancelable:布尔值,可选,默认为false,表示事件是否可以被取消,即能否用Event.preventDefault()取消这个事件。
- addEventListener() 和attachEvent()的区别?
ans:attachEvent()和detachEvent()为IE事件处理程序中定义的两个方法。
attachEvent()的特点:
- attachEvent()和detachEvent()接收两个参数:attachEvent(事件处理程序名称,事件处理程序函数)
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function() { // 注意第一个参数时“onclick”,和 addEventListener() 中的“click”有所区别
console.log("Clicked");
})
- 通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段
- 和addEventListener() 一样,attachEvent()可以为一个元素添加多个事件处理程序,通过detachEvent()移除,同样需要每个参数保持一致,所以匿名函数也没法移除。
- attachEvent(),事件处理程序在全局作用域中运行,即this === window;而DOM0级事件处理程序中,程序中的this指向当前元素,即事件处理程序在元素的作用域中运行。
- 与DOM方法不同,通过attachEvent()添加的事件处理程序,以跟添加顺序相反的顺序被触发。以下代码,先看到"hello world!",再看到"Clicked"
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function() {
console.log("Clicked");
})
btn.attachEvent("onclick", function() {
console.log("hello world!")
})
总结:
- addEventListener() 传递三个参数,attachEvent()传递两个参数,且attachEvent()传递的事件名前面必须加 on-
- addEventListener() 先绑定先触发,attachEvent()先绑定后触发
- addEventListener() 添加的事件处理程序在元素的作用域中运行,attachEvent()添加的事件处理程序在全局作用域中运行
- addEventListener() 添加的事件处理程序可以选择添加到捕获或冒泡阶段,attachEvent()添加的事件处理程序只被添加到冒泡阶段
- 如何定义跨浏览器的事件处理程序?
ans:要保证处理事件的代码能在大多数浏览器上一致的运行,只用关注冒泡阶段
代码如下:
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;
}
}
}
var btn = document.getElementById("myBtn");
var handler = function() {
console.log("Clicked");
}
EventUtil.addHandler(btn, "click", handler);
EventUtil.removeHandler(btn, "click", handler);
先检测传入的元素中是否存在DOM2级方法,若存在的时IE的方法,则采取第二种方案,最后一种即采用DOM0级方案。