JavaScript是一套使用事件机制较多的语言,特别是与DOM交互的时候。所以了解并理解事件机制就变得很必要了。
类似<button onclick="alert('你点击了这个按钮');">点击这个按钮</button>
的方式,这种方式会使JS与HTML高度耦合,不利于开发和维护,不推荐使用。
使用DOM元素的onXXX
属性设置,简单易懂,兼容性好。确定是只能绑定一个处理函数。
使用事件监听函数element.addEventListener(<event-name>, <callback>, <use-capture>);
,在 element 这个对象上面添加一个事件监听器,当监听到有
使用事件解除绑定方法:element.removeEventListener(<event-name>, <callback>, <use-capture>);
需要注意的是,绑定事件时的回调函数不能是匿名函数,必须是一个声明的函数,因为解除事件绑定时需要传递这个回调函数的引用,才可以断开绑定。
内置的时间也可以被JavaScript模拟触发,使用dispatchEvent
方法。
与自定义事件的函数有 Event、CustomEvent 和 dispatchEvent。
直接自定义事件,使用 Event 构造函数:
var event = new Event('build');
// Listen for the event.
elem.addEventListener('build', function (e) { ... }, false);
// Dispatch the event.
elem.dispatchEvent(event);
CustomEvent 可以创建一个更高度自定义事件,还可以附带一些数据,具体用法如下:
var myEvent = new CustomEvent(eventname, options);
其中options可以是:
{
detail: {
...
},
bubbles: true,
cancelable: false
}
其中 detail 可以存放一些初始化的信息,可以在触发的时候调用。其他属性就是定义该事件是否具有冒泡等等功能。
这个用于触发自定义的事件。
当我们给父子关系的元素都绑定了事件的时候,触发子元素的时候,这两个事件发生的前后顺序是如何的?这引开了我们关于事件顺序的讨论,其实一共有两种方式:
这两种方式确定了事件执行的前后顺序,只不过后来W3C对DOM2的事件模型给出了一个规范:首先进入事件捕获阶段->达到元素后->进入事件冒泡阶段。
开发者可以通过addEventListener
函数的第三个参数设置事件触发的阶段,默认为false,冒泡阶段。而DOM1级别的事件绑定则只能在冒泡阶段触发。
事件绑定后,检测顺序就会从被绑定的DOM下滑到触发的元素,再冒泡会绑定的DOM上。也就是说,如果你监听了一个DOM节点,那也就等于你监听了其所有的后代节点。
代理的意思就是只监听父节点的事件触发,以来代理对其后代节点的监听,而你需要做的只是通过currentTarget
属性得到触发元素并作出回应。
使用事件代理意味着你可以节省大量重复的事件监听,以减少浏览器资源消耗。还有一个好处就是让HTML独立起来,比如之后还有要加子元素的需求,也不需要再为其单独加事件监听了。
当一个事件被触发的时候,会创建一个事件对象(Event Object),这个对象里面包含了一些有用的属性或者方法。事件对象会作为第一个参数,传递给我们的毁掉函数。我们可以使用下面代码,在浏览器中打印出这个事件对象:
var btn = document.getElementsByTagName('button');
btn[0].addEventListener('click', function(event) {
console.log(event);
}, false);
比较常用的几个属性和方法:
event.currentTarget
。event.stopPropagation()
.事件绑定函数时,该函数会以当前元素为作用域执行,所以回调函数中的this
是当前的DOM元素。如果我们需要指定作用域,可以选择:
可以通过MDN查询,也可以在浏览器中输入:
for (i in window) {
if ( /^on/.test(i)) { console.log(i); }
}
查看,你会发现提供的事件超过你想象的多!
load
资源加载完成时触发。这个资源可以是图片、CSS 文件、JS 文件、视频、document 和 window 等等。DOMContentLoaded
DOM构建完毕的时候触发, jQuery的ready方法包裹的就是这个事件。beforeunload
当浏览者在页面上的输入框输入一些内容时,未保存、误操作关掉网页可能会导致输入信息丢失。当浏览者输入信息但未保存时关掉网页,我们就可以开始监听这个事件,这时候试图关闭网页的时候,会弹窗阻止操作,点击确认之后才会关闭。当然,如果没有必要,就不要监听,不要以为使用它可以为你留住浏览者。resize
当节点尺寸发生变化时,触发这个事件。通常用在 window 上,这样可以监听浏览器窗口的变化。通常用在复杂布局和响应式上。出于对性能的考虑,你可以使用函数 throttle 或者 debounce 技巧来进行优化,throttle 方法大体思路就是在某一段时间内无论多次调用,只执行一次函数,到达时间就执行;debounce 方法大体思路就是在某一段时间内等待是否还会重复调用,如果不会再调用,就执行函数,如果还有重复调用,则不执行继续等待。error
当我们加载资源失败或者加载成功但是只加载一部分而无法使用时,就会触发 error 事件,我们可以通过监听该事件来提示一个友好的报错或者进行其他处理。比如 JS 资源加载失败,则提示尝试刷新;图片资源加载失败,在图片下面提示图片加载失败等。该事件不会冒泡。因为子节点加载失败,并不意味着父节点加载失败,所以你的处理函数必须精确绑定到目标节点。在 IE 下面绑定一个事件监听,在 IE9之前的版本中无法使用标准的 addEventListener
函数,而是使用自家的 attachEvent
,具体用法:element.attachEvent(<event-name>, <callback>);
它只支持监听在冒泡阶段触发的事件,所以为了统一,在使用标准事件监听函数的时候,第三参数传递 false。
IE 中往回调函数中传递的事件对象与标准也有一些差异,你需要使用 window.event 来获取事件对象。所以你通常会写出下面代码来获取事件对象:
event = event || window.event
此外还有一些事件属性有差别,比如比较常用的 event.target 属性,IE 中没有,而是使用 event.srcElement 来代替。如果你的回调函数需要处理触发事件的节点,那么需要写:
node = event.srcElement || event.target;