今天看书又看到事件,遂决定小总结一下~html
JavaScript与HTML之间的交互是经过事件实现的。事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。可使用监听器(事件处理程序)来监听事件,以便事件发生时执行相应的代码。前端
本文主要讲事件流和事件处理程序。后续会再写一篇文章,总结事件类型。浏览器
你们都举同心圆的栗子,我在这里也说说咯~想象画在一张纸上的一组同心圆,若是把手指放在圆心上,那么手指碰触的不是一个圆,而是纸上全部的圆。这和点击HTML页面上的元素原理是同样的,若是页面上有个按钮,你点击按钮的同时,也点击了按钮的容器元素,点击了容器元素的容器元素……点击了整个页面。函数
事件流描述的是从页面中接收事件的顺序。现存两种事件流:this
接收事件的顺序是:事件由最具体的元素接收,逐级向上传播到较为不具体的节点。spa
若是页面中有个div
,点击了div
以后,事件的传播顺序以下:
(1) div
(2) body
(3) html
(4) document设计
事件冒泡的过程以下图:code
全部现代浏览器都支持事件冒泡,并且IE9,Firefox、Chrome、Safari都将事件一直冒泡到window
对象。htm
接收事件的顺序是:事件由最不具体的元素接收,逐级向下传播到最具体的节点。对象
若是页面中有个div
,点击了div
以后,事件的传播顺序以下:
(1) document
(2) html
(3) body
(4) div
事件捕获的过程以下图:
IE9,Firefox、Chrome、Safari都支持这种事件流模型,并且是从window
对象开始捕获事件的。
“DOM2级事件”规定事件流包含3个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。
以下图:
在DOM事件流中,捕获阶段包括1,2,3。冒泡阶段包括4,5,6,7。其中4是处理目标阶段,在事件处理中被当作是冒泡阶段的一部分。
虽然“DOM级事件”规范明确规定捕获阶段不涉及事件目标,可是IE9,Firefox、Chrome、Safari和Opera9.5及以上版本都会在捕获阶段触发事件对象上的事件,这就意味着有两个机会在目标对象上面操做事件。
IE9,Firefox、Chrome、Safari、Opera都支持DOM事件流,IE8及更早版本不支持DOM事件流。即IE9,Firefox、Chrome、Safari、Opera及支持冒泡又支持捕获,而IE8及之前版本仅支持冒泡。
须要说明的是,在实际中,大多数都使用冒泡事件,仅在有特殊须要时才使用捕获事件。
在说事件处理程序以前,先简单介绍下事件对象。什么是事件对象呢?
在触发DOM上的某个事件时,会产生一个对象,这个对象中包含全部与事件有关的信息,如致使事件的元素、事件的类型以及其余与特定事件相关的信息,这个对象就是事件对象。全部的事件处理程序均可以访问事件对象,可是访问的方式不尽相同,一种是将事件对象做为事件处理程序的参数,另外一种是事件对象做为window
的属性,经过window.event
来访问。这里就介绍到这里,先有个概念~
回归正题,click
,load
,mouseover
都是事件的名字,而响应某个事件的函数就叫作事件处理程序,事件处理程序的名字以“on
”开头。
那么该怎样为事件指定处理程序呢?主要有两类方法:HTML事件处理程序和JavaScript指定事件处理程序。
某个HTML元素支持的每种事件,均可以使用一个与相应事件处理程序同名的HTML特性来指定。这个特性的值是可以执行的JavaScript代码。
<input type="button" value="Click" onclick="console.log('hello')" /> <!--直接是一句代码-->
或
<script> function show () { console.log('hello'); } </script> <input type="button" value="Click" onclick="show()" /> <!--调用show()函数-->
这种方式中,事件处理程序中的代码在执行时,有权访问全局做用域中的任何代码。
这样的事件处理程序在执行的时候,会建立一个封装着元素属性值的函数,而后执行这个函数。就是说,
<input type="button" value="Click" onclick="console.log('hello')" />
当单击按钮的时候,先建立一个函数function () { console.log('hello'); }
,而后当即执行该函数。
这个函数有如下3个特色:
1) 函数中会有一个局部变量
event,即事件对象。
注意:event是局部变量,能够在函数中直接访问,既不用本身定义,也不是从参数列表中读取。这点与后面其余指定事件处理程序的方式有所不一样,记得区分。
2) 函数内部,this
值等于事件的目标元素。
3)这个动态建立的函数使用with
扩展做用域:
function () { with(document) { with(this) { // 元素属性值,如 console.log('hello'); } } }
从而该函数能够访问document
及该元素自己的任何成员。这也是为何“采用这种方式指定的事件处理程序中的代码在执行时,有权访问全局做用域中的任何代码。”,由于做用域中有document
。
实质上就是让动态建立的函数return false
,因此要这样写:
<a href="http://www.baidu.com" onclick="console.log('hello'); return false;">baidu</a>
或
<script> function show () { console.log('hello'); } </script> <a href="http://www.baidu.com" onclick="show(); return false;">baidu</a>
第二种方式必定要注意,return false
是加在onclick
属性值里面的,若是放到show()
里是不起做用的哦,由于是要“让动态建立的函数return false
”
show()
函数,若是用户单击按钮的时候,function show(){}
尚未执行,就会致使错误。JavaScript指定事件处理程序主要有两种方式:DOM0 级事件处理程序、DOM2 级事件处理程序。使用JavaScript指定事件处理程序,首先必须取得对一个操做对象的引用。
每一个元素(包括window
和document
)都有本身的事件处理程序属性,这些属性一般所有小写,如onclick
,将这种属性赋值为一个函数,就是DOM0 级事件处理程序的指定方式。以下:
var btn = document.getElementById('myBtn'); // 取得对操做对象的引用 btn.onclick = function () { // 为onclick赋值一个函数,指定事件处理程序 console.log('hello'); }
btn.onclick = null; // 只需将属性置空
1)添加事件处理程序的代码只有在运行完以后才会为元素绑定事件处理程序,所以若是该段代码未执行单击按钮是没有反应的。
2)事件处理程序中的this
引用当前元素,即绑定了该事件的元素。
3)以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。
4)全部的浏览器都支持DOM0 级事件处理程序。
“DOM2 级事件处理程序”定义了两个方法,用来指定和删除事件处理程序:addEventListener()
和 removeEventListener()
。这两个方法都接受3个参数:要处理的事件名、做为事件处理程序的函数、一个布尔值,这个布尔值参数若是为true
表示在捕获阶段调用事件处理程序,为false
表示在冒泡阶段调用事件处理程序,通常设为false
。
var btn = document.getElementById('myBtn'); // 取得对操做对象的引用 btn.addEventListener('click', function(){ // 注意是 click 哦~ console.log('hello'); }, false);
或
function show() { console.log('hello'); } var btn = document.getElementById('myBtn'); // 取得对操做对象的引用 btn.addEventListener('click', show, false);
移除事件处理程序有一个要求就是:移除时传入的参数必须与添加事件处理程序时使用相同的参数。从而经过addEventListener()
添加的匿名函数将没法移除。
移除上面第二种方式绑定的事件处理程序代码:
var btn = document.getElementById('myBtn'); // 取得对操做对象的引用 btn.removeEventListener('click', show, false);
要说明的是:经过addEventListener
添加的事件处理程序只能经过removeEventListener()
来移除。
1)只有运行完addEventListener()
才为元素绑定了事件处理程序。
2)事件处理程序中的this
引用当前元素,即绑定了该事件的元素。
3)经过设置第三个参数为true
或false
能够设定在捕获阶段仍是冒泡阶段调用事件处理程序。
4)能够为同一个元素绑定多个事件处理程序,绑定的事件处理程序会按照它们添加的前后顺序依次触发。这是DOM0 级事件处理程序不支持的哦~ DOM0 级只能添加一个事件处理程序,后添加的会覆盖先添加的。
5)IE9、Firefox、Safari、Chrome和Opera都支持DOM2 级事件处理程序。
上面特意加粗了IE9,那么IE8及之前版本怎么办呢?IE实现了与DOM中相似的两个方法:attachEvent()
和detachEvent()
。这两个方法仅接受两个参数:事件处理程序名称和事件处理程序函数。IE8及以前版本仅支持事件冒泡,因此经过attachEvent()
添加的事件处理程序会在冒泡阶段调用。
注:IE10及以前版本支持attachEvent()
和detachEvent()
方法,IE11再也不支持,通常IE9及以上都使用DOM方法,这两个方法仅对IE8及如下版本使用。
var btn = document.getElementById('myBtn'); btn.attachEvent('onclick', function () { // 注意这里是 onclick 哦~ console.log('hello'); });
或
function show() { console.log('hello'); } var btn = document.getElementById('myBtn'); btn.attachEvent('onclick', show);
使用detachEvent
移除事件处理程序的条件与DOM方法相同——必须提供相同的参数,从而添加的匿名函数也没法被移除。
var btn = document.getElementById('myBtn'); btn.detachEvent('onclick', show);
1)只有运行完attachEvent()
才为元素绑定了事件处理程序。
2)事件处理程序会在全局做用域中运行,从而事件处理程序中的this
指向全局对象window
。这与以前的都不一样哦~因此不能认为this
始终等于事件目标,当使用attachEvent()
的时候,this
是window
,不过其余时候,this
是等于事件目标的~
3)能够为同一个元素绑定多个事件处理程序,绑定的事件处理程序会按照它们添加顺序的相反次序被触发,即先添加后执行,后添加的先执行。
是否是以为IE太奇葩呢,若是没有IE,前端世界会和谐不少吧~还好IE9以上都是按照DOM标准来了,再也不我行我素。
这里顺便简单说下事件对象,对比来看,会更清晰~
前面介绍过事件对象,这里再也不赘述。
event
,这个event
就是事件对象。DOM0 级
仍是DOM2 级
方法,事件对象都会做为事件处理程序的参数传入到事件处理程序中。DOM0 级
方法添加事件处理程序时,事件对象做为window
对象的属性存在,经过window.event
访问。attachEvent()
添加事件处理程序,事件对象会做为参数传入到事件处理程序中。DOM中的事件对象(两个方法)preventDefault()
方法用来取消事件默认行为,stopPropagation()
方法用来阻止事件进一步捕获或冒泡
// 假设事件对象传入给参数event event.preventDefault(); // 取消事件默认行为 event.stopPropagation(); // 阻止事件进一步捕获或冒泡
IE8及如下中的事件对象(两个属性)returnValue
属性用来设置事件的默认行为。默认值为true
,当设置为false
时,就会取消事件的默认行为。cancelBubble
属性用来取消事件冒泡。默认值为false
,当设置为true
时,就会阻止事件冒泡。
event.returnValue = false; // 取消事件的默认行为 event.cancelBubble = true; // 阻止事件冒泡
注:这两个属性仅IE8及如下版本支持,IE9+使用这两个属性会报错。
终于写完了,这篇写得时间好长……这样整理完知识脉络会清晰一点吧~
本文主要参考《js高级程序设计》事件一章。若有不妥之处,还请指正。谢谢!