文章目录

  • ​​13 事件流​​
  • ​​13.1 事件流​​
  • ​​13.1.1 事件冒泡​​
  • ​​13.1.2 事件捕获​​
  • ​​13.1.3 DOM事件流​​
  • ​​13.2 事件处理程序​​
  • ​​某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的 HTML 特性来指定。这个​​
  • ​​13.2.2 DOM0 级事件处理程序​​
  • ​​13.2.3 DOM2 级事件处理程序​​
  • ​​13.2.4 IE事件处理程序​​
  • ​​13.2.5 跨浏览器的事件处理程序​​
  • ​​13.3 事件对象​​
  • ​​13.3.1 DOM中的事件对象​​
  • ​​13.3.2 IE中的事件对象​​
  • ​​13.3.3 跨浏览器的事件对象​​

13 事件流

javaScript 与 HTML 之间的交互是通过事件实现的。事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用侦听器(或处理程序)来预订事件,以便事件发生时执行相应的代码。这种在传统软件工程中被称为观察员模式的模型,支持页面的行为( JavaScript 代码)与页面的外观( HTML 和 CSS 代码)之间的松散耦合.

13.1 事件流

IE 的事件流是事件冒泡流,而 Netscape Communicator 的事件流是事件捕获流。

13.1.1 事件冒泡

IE 的事件流叫做事件冒泡( event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深
的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。以下面的 HTML 页面为例:

<!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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ppgZsDQy-1579591369552)(/images/frontend-JavaScript-event-iecapture.jpg)]

13.1.2 事件捕获

另一种事件流叫做事件捕获( event capturing)。事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预定目标之前捕获它。

(1) document
(2) <html>
(3) <body>
(4) <div>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mt1cFz51-1579591369553)(/iamges/frontend-javascript-event-netscape-capture.jpg)]

13.1.3 DOM事件流

“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OC6ZGLqR-1579591369554)(/images/frontend-javascript-event-domevent.jpg)]

13.2 事件处理程序

事件就是用户或浏览器自身执行的某种动作。诸如 click、 load 和 mouseover,都是事件的名字。而响应某个事件的函数就叫做事件处理程序(或事件侦听器)。

某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的 HTML 特性来指定。这个

特性的值应该是能够执行的 JavaScript 代码。例如,要在按钮被单击时执行一些 JavaScript,可以像下面这样编写代码:

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

当单击这个按钮时,就会显示一个警告框。这个操作是通过指定 onclick 特性并将一些 JavaScript代码作为它的值来定义的。由于这个值是 JavaScript,因此不能在其中使用未经转义的 HTML 语法字符,例如和号( &)、双引号( “”)、小于号( <)或大于号( >)。为了避免使用 HTML 实体,这里使用了单引号。如果想要使用双引号,那么就要将代码改写成如下所示:

<input type="button" value="Click Me" onclick="alert("Clicked")" />

在 HTML 中定义的事件处理程序可以包含要执行的具体动作,也可以调用在页面其他地方定义的脚本,如下面的例子所示:

<script type="text/javascript">
function showMessage(){
alert("Hello world!");
}
</script>
<input type="button" value="Click Me" onclick="showMessage()" />

关于这个动态创建的函数,另一个有意思的地方是它扩展作用域的方式。在这个函数内部,可以像访问局部变量一样访问 document 及该元素本身的成员。这个函数使用 with 像下面这样扩展作用域:

function(){
with(document){
with(this){
//元素属性值
}
}
}

如果当前元素是一个表单输入元素,则作用域中还会包含访问表单元素(父元素)的入口,这个函数就变成了如下所示:

function(){
with(document){
with(this.form){
with(this){
//元素属性值
}
}
}
}

这样扩展事件处理程序的作用域链在不同浏览器中会导致不同结果。不同 JavaScript引擎遵循的标识符解析规则略有差异,很可能会在访问非限定对象成员时出错。
通过 HTML 指定事件处理程序的最后一个缺点是 HTML 与 JavaScript 代码紧密耦合。如果要更换事件处理程序,就要改动两个地方: HTML 代码和 JavaScript 代码。

13.2.2 DOM0 级事件处理程序

通过 JavaScript 指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。这种为事件处理程序赋值的方法是在第四代 Web 浏览器中出现的,而且至今仍然为所有现代浏览器所支持。原因一是简单,二是具有跨浏览器的优势。要使用 JavaScript 指定事件处理程序,首先必须取得一个要操作的对象的引用。

var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert("clicked");
}

13.2.3 DOM2 级事件处理程序

“DOM2 级事件” 定义了两个方法,用于处理指定和删除事件处理程序的操作: addEventListener()和 removeEventListener()。所有 DOM 节点中都包含这两个方法,并且它们都接受 3 个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是 true,表示在捕获阶段调用事件处理程序;如果是 false,表示在冒泡阶段调用事件处理程序。

var btn = document.getElementById("myBtn");
btn.addEventListener("click",function(){
alert(this.id);
},false);

通过 addEventListener()添加的事件处理程序只能使用 removeEventListener()来移除;移除时传入的参数与添加处理程序时使用的参数相同。这也意味着通过 addEventListener()添加的匿名函数将无法移除,如下面的例子所示。

var btn = document.getElementById("myBtn");
btn.addEventListener("click",function(){
alert(this.id);
},false);
//省略了其他代码
btn.removeEventListener("click",function(){//程序没有用
//
},false)

传入 removeEventListener()中的事件处理程序函数必须与传入addEventListener()中的相同

var btn = document.getElementById("myBtn");
var handler = function(){
alert(this.id);
};
btn.addEventListener("click",handler,false);
btn.removeEventListener("click",handler,false);

13.2.4 IE事件处理程序

IE 实现了与 DOM 中类似的两个方法: attachEvent()和 detachEvent()。这两个方法接受相同的两个参数:事件处理程序名称与事件处理程序函数。由于 IE8 及更早版本只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick",function(){
alert(this === window); //true});
};
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
alert("Clicked");
});
btn.attachEvent("onclick", function(){
alert("Hello world!");
});

这里调用了两次 attachEvent(),为同一个按钮添加了两个不同的事件处理程序。不过,与 DOM方法不同的是,这些事件处理程序不是以添加它们的顺序执行,而是以相反的顺序被触发。单击这个例子中的按钮,首先看到的是"Hello world!",然后才是"Clicked"。
使用 attachEvent()添加的事件可以通过 detachEvent()来移除,条件是必须提供相同的参数。与 DOM 方法一样,这也意味着添加的匿名函数将不能被移除。

var btn = document.getElementById("myBtn");
var handler = function(){
alert("Clicked");
};
btn.attachEvent("onclick", handler);
//这里省略了其他代码
btn.detachEvent("onclick", handler);

13.2.5 跨浏览器的事件处理程序

第一个要创建的方法是 addHandler(),它的职责是视情况分别使用 DOM0 级方法、 DOM2 级方法或 IE 方法来添加事件。
与 addHandler()对应的方法是 removeHandler(),它也接受相同的参数。这个方法的职责是移除之前添加的事件处理程序

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;
}
};
removeHnadler: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] = handler;
}
}
}

这两个方法首先都会检测传入的元素中是否存在 DOM2 级方法。如果存在 DOM2 级方法,则使用该方法:传入事件类型、事件处理程序函数和第三个参数 false(表示冒泡阶段)。如果存在的是 IE 的方法,则采取第二种方案。

var btn = document.getElementById("myBtn");
var handler = function(){
alert("Clicked");
};
EventUtil.addHandler(btn, "click", handler);
//这里省略了其他代码
EventUtil.removeHandler(btn, "click", handler);

13.3 事件对象

在触发 DOM 上的某个事件时,会产生一个事件对象 event,这个对象中包含着所有与事件有关的信息。

13.3.1 DOM中的事件对象

兼容 DOM 的浏览器会将一个 event 对象传入到事件处理程序中。无论指定事件处理程序时使用什么方法( DOM0 级或 DOM2 级),都会传入 event 对象。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert(event.type); //"click"
};
btn.addEventListener("click", function(event){
alert(event.type); //"click"
}, false);

event 对象包含与创建它的特定事件有关的属性和方法。触发的事件类型不一样,可用的属性和方法也不一样。

属性/方法

类 型

读/写

说 明

bubbles

Boolean

只读

表明事件是否冒泡

cancelable

Boolean

只读

表明是否可以取消事件的默认行为

currentTarget

Element

只读

其事件处理程序当前正在处理事件的那个元素

defaultPrevented

Boolean

只读

为true表示已经调用了preventDefault()(DOM3级事件中新增)

detail

Integer

只读

与事件相关的细节信息

eventPhase

Integer

只读

调用事件处理程序的阶段: 1表示捕获阶段, 2表示“处于目标”, 3表示冒泡阶段

preventDefault()

Function

只读

取消事件的默认行为 。 如果cancelable是true,则可以使用这个方法

stopImmediatePropagation()

Function

只读

取消事件的进一步捕获或冒泡,同时阻止任何事件处理程序被调用( DOM3级事件中新增)

stopPropagation()

Function

只读

取消事件的进一步捕获或冒泡。如果bubbles为true,则可以使用这个方法

target

Element

只读

事件的目标

trusted

Boolean

只读

为true表示事件是浏览器生成的。为false表示事件是由开发人员通过JavaScript创建的(DOM3级事件中新增)

type

String

只读

被触发的事件的类型

view

AbstractView

只读

与事件关联的抽象视图。等同于发生事件的window对象

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

在需要通过一个函数处理多个事件时,可以使用 type 属性。例如:

var btn = document.getElementById("myBtn");
var handler = function(event){
switch(event.type){
case "click":
alert("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;

要阻止特定事件的默认行为,可以使用 preventDefault()方法。

var link = document.getElementById("myLink");
link.onclick = function(event){
event.preventDefault();
};

只有 cancelable 属性设置为 true 的事件,才可以使用 preventDefault()来取消其默认行为。
另外, stopPropagation()方法用于立即停止事件在 DOM 层次中的传播,即取消进一步的事件捕获或冒泡。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert("clicked");
event.stopPropagation();
};
document.body.onclick = function(event){
alert("Body clicked")
};

13.3.2 IE中的事件对象

与访问 DOM 中的 event 对象不同,要访问 IE 中的 event 对象有几种不同的方式,取决于指定事件处理程序的方法

var btn = document.getElementById("myBtn");
btn.onclick = function(){
var event = window.event;
alert(event.type); //""click
};

通过 window.event 取得了 event 对象,并检测了被触发事件的类型( IE 中的 type属性与 DOM 中的 type 属性是相同的)。事件处理程序是使用 attachEvent()添加的,那么就会有一个 event 对象作为参数被传入事件处理程序函数中.

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick",function(event){
alert(event.type);//click
})

属性/方法

类 型

读/写

说 明

cancelBubble

Boolean

读/写

默认值为false,但将其设置为true就可以取消事件冒泡(与DOM中的stopPropagation()方法的作用相同)

returnValue

Boolean

读/写

默认值为true,但将其设置为false就可以取消事件的默认行为(与DOM中的preventDefault()方法的作用相同)

srcElement

Element

只读

事件的目标(与DOM中的target属性相同)

type

String

只读

被触发的事件的类型

使用 event.srcElement 比较保险

var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert(window.event.srcElement === this); //true
};
btn.attachEvent("onclick", function(event){
alert(event.srcElement === this); //false
});

returnValue 属性相当于 DOM 中的 preventDefault()方法,它们的作用都是取消给定事件的默认行为。只要将 returnValue 设置为 false,就可以阻止默认行为。

var link = document.getElementById("myLink");
link.onclick = function(){
window.event.returnValue = false;
};
document.body.onclick = function(){
alert("body clicked")
}

13.3.3 跨浏览器的事件对象

虽然 DOM 和 IE 中的 event 对象不同,但基于它们之间的相似性依旧可以拿出跨浏览器的方案来。IE 中 event 对象的全部信息和方法 DOM 对象中都有,只不过实现方式不一样。

var EventUtil = {
addHandler:function(element,type,handler){
//省略代码
},
getHandler:function(event){
return event?event:window.event;
},
getTarget:function(event){
return event.target||event.srcElement;
},
preventDefault:function(event){
if(event.preventDefault){
event.prevenntDefault();
}else{
event.returnValue = false;
}
},
removeHandler:function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble = true;
}
}
}

第一个是 getEvent(),它返回对 event对象的引用。

btn.onclick = function(event){
event = EventUtil.getEvent(event);
};

第二个方法是 getTarget(),它返回事件的目标。在这个方法内部,会检测 event 对象的 target属性,如果存在则返回该属性的值;否则,返回 srcElement 属性的值。

btn.onclick = function(event){
event = EventUtil(event);
var target = EventUtil.getTarget(event);
}

第三个方法是 preventDefault(),用于取消事件的默认行为。在传入 event 对象后,这个方法会检查是否存在 preventDefault()方法,如果存在则调用该方法。如果 preventDefault()方法不存在,则将 returnValue 设置为 false。

var link = document.getElementById("myLink");
link.onclick = function(event){
event = EventUtil.getEvent(event);
EventUtil.preventDefault(event);
};

第四个方法是 stopPropagation(),其实现方式类似。首先尝试使用 DOM 方法阻止事件流,否则就使用 cancelBubble 属性。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert("Clicked");
event = EventUtil.getEvent(event);
EventUtil.stopPropagation(event);
};
document.body.onclick = function(event){
alert("Body clicked");
};