JavaScript高级程序设计学习笔记第十三章--事件

时间:2024-08-15 23:38:02

事件冒泡:

IE 的事件流,事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。例如:

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

事件捕获:

事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预定目标之前捕获它。

前面的例子如果是事件捕获的话,那么单击<div>元素就会以下列顺序触发 click 事件:
(1) document

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

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

以前面的例子为例,单击div后事件流过程是这样的:

JavaScript高级程序设计学习笔记第十三章--事件

HTML事件处理程序:

如果单击一个按钮执行事件的话,可以采用以下两种方式:

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

第二种:

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

第二种方法创建了一个封装着元素属性值的函数,这个函数中有一个局部变量 event,也就是事件对象。通过 event 变量,可以直接访问事件对象,你不用自己定义它,也不用从函数的参数列表中读取。
例如查看事件的类型,可以通过event.type获得,单击事件输出类型为click。

在 HTML 中指定事件处理程序的缺点如下:

  • 首先,存在一个时差问题。因为用户可能会在HTML 元素一出现在页面上就触发相应的事件,但当时的事件处理程序有可能尚不具备执行条件。
  • 这样扩展事件处理程序的作用域链在不同浏览器中会导致不同结果。
  • HTML 与 JavaScript 代码紧密耦合。

DOM0 级事件处理程序:

通过 JavaScript 指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。

要使用 JavaScript 指定事件处理程序,首先必须取得一个要操作的对象的引用。找到引用之后,将这个引用的属性的值设置为一个函数。

使用 DOM0 级方法指定的事件处理程序被认为是元素的方法。因此,这时候的事件处理程序是在元素的作用域中运行;换句话说,程序中的 this 引用当前元素。例如:

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

如果想撤销事件,就将事件处理的属性设置为null就可以了,例如: btn.onclick = null; //删除事件处理程序

DOM2 级事件处理程序:

定义了两个方法,用于处理指定和删除事件处理程序的操作: addEventListener()和 removeEventListener()。

所有 DOM 节点中都包含这两个方法,并且它们都接受 3 个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是 true,表示在捕获
阶段调用事件处理程序;如果是 false,表示在冒泡阶段调用事件处理程序。

要在按钮上为 click 事件添加事件处理程序,可以使用下列代码::

 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(){ //没有用!
alert(this.id);
}, false);

但是下列代码就可以移除事件处理程序:

 var btn = document.getElementById("myBtn");
var handler = function(){
alert(this.id);
};
btn.addEventListener("click", handler, false);
//这里省略了其他代码
btn.removeEventListener("click", handler, false); //有效!

因为两个事件处理程序使用了相同的函数。

IE事件处理程序:

IE 实现了与 DOM 中类似的两个方法: attachEvent()和 detachEvent()。这两个方法接受相同的两个参数:事件处理程序名称与事件处理程序函数。例如:

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

在 IE 中使用 attachEvent()与使用 DOM0 级方法的主要区别在于事件处理程序的作用域。在使用 DOM0 级方法的情况下,事件处理程序会在其所属元素的作用域内运行;在使用 attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,因此 this 等于 window。例如:

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

用attachEvent()方法也可以用来为一个元素添加多个事件处理程序,这些事件处理程序不是以添加它们的顺序执行,而是以相反的顺序被触发,先添加后执行。

使用 attachEvent()添加的事件可以通过 detachEvent()来移除,条件是必须提供相同的参数。同样匿名函数不能被删除,只能传入同一个引用。

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

addHandler():视情况分别使用 DOM0 级方法、 DOM2 级方法或 IE 方法来添加事件。这个方法属于一个名叫 EventUtil 的对象,addHandler()方法接受 3 个参数:要操作的元素、事件名称和事件处理程序函数。

removeHandler():它也接受相同的参数。这个方法的职责是移除之前添加的事件处理程序——无论该事件处理程序是采取什么方式添加到元素中的,如果其他方法无效,默认采用 DOM0 级方法。

EventUtil类定义如下:

 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 对象:

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

事件对象:在触发 DOM 上的某个事件时,会产生一个事件对象 event,这个对象中包含着所有与事件有关的信息。所有浏览器都支持 event 对象,但支持方式不同。

DOM中的事件对象:

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

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

以上代码检测了 currentTarget 和 target 与 this 的值。由于 click 事件的目标是按钮,因此这三个值是相等的。如果事件处理程序存在于按钮的父节点中(例如 document.body),那么这些值是不相同的。例如:

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

当单击这个例子中的按钮时, this 和 currentTarget 都等于 document.body,因为事件处理程序是注册到这个元素上的。然而, target 元素却等于按钮元素,因为它是 click 事件真正的目标。由于按钮上并没有注册事件处理程序,结果 click 事件就冒泡到了 document.body,在那里事件才得到了处理。

event.preventDefault():阻止特定事件的默认行为。只有 cancelable 属性设置为 true 的事件,才可以使用 preventDefault()来取消其默认行为。例如,链接的默认行为就是在被单击时会导航到其 href 特性指定的 URL。如果你想阻止链接导航这一默认行为,那么通过链接的onclick 事件处理程序可以取消它。例如:

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

event.stopPropagation():用于立即停止事件在 DOM 层次中的传播,即取消进一步的事件捕获或冒泡。例如:

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

对于这个例子而言,如果不调用 stopPropagation(),就会在单击按钮时出现两个警告框。可是,由于 click 事件根本不会传播到 document.body,因此就不会触发注册在这个元素上的 onclick 事件处理程序。

event.eventPhase属性:可以用来确定事件当前正位于事件流的哪个阶段。如果是在捕获阶段调用的事件处理程序,那么 eventPhase 等于 1;如果事件处理程序处于目标对象上,则 eventPhase 等于 2;如果是在冒泡阶段调用的事件处理程序, eventPhase 等于 3。这里要注意的是,尽管“处于目标”发生在冒泡阶段,但 eventPhase 仍然一直等于 2。

IE中的事件对象:

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

  • 在使用 DOM0 级方法添加事件处理程序时, event 对象作为 window 对象的一个属性存在,可以通过 window.event 取得了 event 对象。例如:
  •  var btn = document.getElementById("myBtn");
    btn.onclick = function(){
    var event = window.event;
    alert(event.type); //"click"
    };
  • 如果事件处理程序是使用 attachEvent()添加的,那么就会有一个 event 对象作为参数被传入事件处理程序函数中。也可以通过 window 对象来访问 event 对象,就像使用DOM0 级方法时一样。例如:
  •  var btn = document.getElementById("myBtn");
    btn.attachEvent("onclick", function(event){
    alert(event.type); //"click"
    });
  • 如果是通过 HTML 特性指定的事件处理程序,那么还可以通过一个名叫 event 的变量来访问 event对象(与 DOM 中的事件模型相同)。例如:
  • <input type="button" value="Click Me" onclick="alert(event.type)">

event.srcElement属性 :事件的目标(与DOM中的target属性相同)。

event.returnValue属性:相当于 DOM 中的 preventDefault()方法。只要将 returnValue 设置为 false,就可以阻止默认行为。

event.cancelBubble属性:与 DOM 中的 stopPropagation()方法作用相同,都是用来停止事件冒泡的。由于 IE 不支持事件捕获,因而只能取消事件冒泡;但stopPropagatioin()可以同时取消事件捕获和冒泡。 将 cancelBubble 设置为 true,就可阻止事件通过冒泡而触发document.body 中注册的事件处理程序。

事件类型:

  • UI事件:
    • load事件:JavaScript 中最常用的一个事件就是 load。当页面完全加载后(包括所有图像、 JavaScript 文件、CSS 文件等外部资源),就会触发 window 上面的 load 事件。
      定义load事件有两种方式:第一种方式如下:
    •  EventUtil.addHandler(window, "load", function(event){
      alert("Loaded!");
      });

      第二种方式:为<body>元素添加一个 onload 特性,但这种方式只能添加一个事件处理程序。也可以用上述两种方式为图像等元素增加onload事件。

    • unload事件:这个事件在文档被完全卸载后触发。只要用户从一个页面切换到另一个页面,就会发生 unload 事件。添加事件的方式与load方式一样。
    • resize事件:浏览器窗口被调整到一个新的高度或宽度时,就会触发 resize 事件。浏览器窗口最小化或最大化时也会触发 resize事件。关于何时会触发 resize 事件,不同浏览器有不同的机制。
    • scroll 事件
  • 焦点事件:焦点事件会在页面元素获得或失去焦点时触发。
    • blur:在元素失去焦点时触发。这个事件不会冒泡;所有浏览器都支持它。
    • focus:在元素获得焦点时触发。这个事件不会冒泡;所有浏览器都支持它。
    • focusin:在元素获得焦点时触发。这个事件与 HTML 事件 focus 等价,但它冒泡。
    • focusout:在元素失去焦点时触发。这个事件是 HTML 事件 blur 的通用版本。
    • 当焦点从页面中的一个元素移动到另一个元素,会依次触发下列事件:
      • focusout 在失去焦点的元素上触发;
      • focusin 在获得焦点的元素上触发;
      • blur 在失去焦点的元素上触发;
      • focus 在获得焦点的元素上触发;
  • 鼠标与滚轮事件:
    • click:在用户单击主鼠标按钮(一般是左边的按钮)或者按下回车键时触发。这一点对确保易访问性很重要,意味着 onclick 事件处理程序既可以通过键盘也可以通过鼠标执行。
    • dblclick:在用户双击主鼠标按钮(一般是左边的按钮)时触发。
    • mousedown:在用户按下了任意鼠标按钮时触发。不能通过键盘触发这个事件。
    • mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。
    • mouseleave:在位于元素上方的鼠标光标移动到元素范围之外时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。
    • mousemove:当鼠标指针在元素内部移动时重复地触发。不能通过键盘触发这个事件。
    • mouseout:在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。又移入的另一个元素可能位于前一个元素的外部,也可能是这个元素的子元素。不能通过键盘触发这个事件。
    • mouseover:在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发。不能通过键盘触发这个事件。
    • mouseup:在用户释放鼠标按钮时触发。不能通过键盘触发这个事件。
    • mousewheel:除包含鼠标事件的所有标准信息外,还包含一个特殊的 wheelDelta 属性。。当用户向前滚动鼠标滚轮时, wheelDelta 是 120 的倍数;当用
      户向后滚动鼠标滚轮时, wheelDelta 是120 的倍数。这个事件可以在任何元素上面触发,最终会冒泡到 document(IE8)或 window(IE9、 Opera、
      Chrome 及 Safari)对象。
  • 键盘与文本事件:

    • keydown:当用户按下键盘上的任意键时触发,而且如果按住不放的话,会重复触发此事件。
    • keypress:当用户按下键盘上的字符键时触发,而且如果按住不放的话,会重复触发此事件。按下 Esc 键也会触发这个事件。Safari 3.1 之前的版本也会在用户按下非字符键时触发 keypress事件。
    • keyup:当用户释放键盘上的键时触发。
    • 在用户按了一下键盘上的字符键时,首先会触发 keydown 事件,然后紧跟着是 keypress 事件,最后会触发 keyup 事件。 其中, keydown 和 keypress 都是在文本框发生变化之前被触发的;而 keyup事件则是在文本框已经发生变化之后被触发的。如果用户按下了一个字符键不放,就会重复触发keydown 和 keypress 事件,直到用户松开该键为止。如果用户按下的是一个非字符键,那么首先会触发 keydown 事件,然后就是 keyup 事件。如果按住这个非字符键不放,那么就会一直重复触发 keydown 事件,直到用户松开这个键,此时会触发 keyup事件。
    • textInput:文本事件。
  • 复合事件:
    • compositionstart:在 IME 的文本复合系统打开时触发,表示要开始输入了。
    • compositionupdate:在向输入字段中插入新字符时触发。
    • compositionend:在 IME 的文本复合系统关闭时触发,表示返回正常键盘输入状态。
  • 变动事件:
    • DOMNodeRemoved:使用removeChild()或replaceChild()从 DOM中删除节点时触发事件。
    • DOMNodeInserted :使用 appendChild()、 replaceChild()或 insertBefore()向 DOM 中插入节点时触发事件。
  • HTML5 事件:
    • contextmenu :表示何时应该显示上下文菜单,以便开发人员取消默认的上下文菜单而提供自定义的菜单。
    • beforeunload:这个事件会在浏览器卸载页面之前触发,可以通过它来取消卸载并继续使用原有页面。
    • DOMContentLoaded :DOMContentLoaded 事件则在形成完整的 DOM 树之后就会触发,不理会图像、 JavaScript 文件、 CSS 文件或其他资源是否已经下载完毕。
    • readystatechange 事件:提供与文档或元素的加载状态有关的信息。支持 readystatechange 事件的每个对象都有一个 readyState 属性,可能包含下列 5 个值中的一个:uninitialized(未初始化):对象存在但尚未初始化、loading(正在加载):对象正在加载数据、loaded(加载完毕):对象加载数据完成、interactive(交互):可以操作对象了,但还没有完全加载、complete(完成):对象已经加载完毕。
    • hashchange:在 URL 的参数列表(及 URL 中“ #”号后面的所有字符串)发生变化时通知开发人员。把 hashchange 事件处理程序添加给 window 对象,然后 URL 参数列表只要变化就会调用它。此时的 event 对象应该额外包含两个属性: oldURL 和 newURL。这两个属性分别保存着参数列表变化前后的完整 URL。

事件委托:

利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。例如, click 事件会一直冒泡到 document 层次。也就是说,我们可以为整个页面指定一个 onclick 事件处理程序,而不必给每个可单击的元素分别添加事件处理程序。

事件模拟: