一步,一步前進の一步
事件是文档或者浏览器窗口中发生的一些交互瞬间。JS注册事件处理程序来预订事件,当事件发生的瞬间来执行相应的代码,进而实现 JS 和 HTML(即文档或者浏览器窗口) 的交互。javascript
事件流描述的是从页面中接收事件的顺序。html
用手指戳一下屏幕上的同心圆的中心,先点到的是最外围的大圆,仍是最核心的小圆呢?这个就是事件流要处理的本质问题。早起的 IE 和 Netscape 对此有不一样的观点,IE认为先点到的是最小的圆,而后在一层层的传递到最外面的大圆(事件冒泡),Netscape 正好相反,最早碰到的是最外围的大圆,而后在一层层的追踪到最精准的小圆(事件捕获)。前端
事件冒泡:事件开始由最具体的元素接收,而后逐级向上传播到较为不具体的节点。java
事件捕获:事件是从不太具体的节点开始产生接收,而最具体的节点应该是最后接收事件的。编程
事件流规范出来讲,事件传递有三个阶段: 捕获阶段、目标阶段、冒泡阶段。小程序
浏览器的事件处理大概就是注册、监听。程序开始就对将来会发生的某些事情,作出预期,对预期作出正确的反应。事件处理程序就是被注册
的正确反应
,监听
这个操做由浏览器本身完成。
浏览器提供了三种方法,为事件绑定监听函数。浏览器
<div onclick="doSomething()">
须要注意的是:此处的事件处理程序是须要带小括号的,大概的过程是当 div 接收到事件时,会将onclick
后面的代码原封不动的传入JavaScript引擎执行,不加小括号就不会触发处理程序。性能优化
此方式会让 js 的代码和 html 代码杂糅在一块儿,不易代码的变动和维护,所以不推荐使用。dom
div.onclick = function (event) { console.log('触发事件'); };
以元素节点对象的事件属性的方式进行注册,与第一种方式相似。
该事件只会在冒泡阶段
触发。编程语言
target.addEventListener(type, listener[, useCapture])
type
事件名称,大小写敏感;listener
处理函数;useCapture
是否在捕获阶段触发。
推荐使用该方式进行事件的注册,能够对同一节点注册多个事件处理函数。当前冒泡流是被大多浏览器支持,所以useCapture大多赋为false。
document.addEventListener('click', hello, false); document.addEventListener('click', hello2, false);
早期的 IE 浏览器只支持冒泡流,有本身的事件注册和移除的方法:attachEvent()
、detachEvent()
btn.attachEvent('onclick', function () { alert('ie browser'); });
须要注意的是处理函数的 this 是指向 window 的,而不像前两种事件处理函数的 this 会指向事件所在的 dom 节点对象。
红宝书方案代码以下:
var EventUtil = { addHandler: function(elm, type, handler) { if (elm.addEventListener) { elm.addEventListener(type, handler, false); } else if (elm.attachEvent) { elm.attachEvent('on' + type, handler); } else { elm['on' + type] = handler; } } };
该方法是比较好的,可是处理函数执行时的 this 指向仍是有一点点问题,attachEvent
方式仍是指向 window 的,若是想更加完善,请参考 js 忍者秘籍上面的方案。
在处理事件时,咱们须要考虑一些性能的问题,有必要限制事件处理函数的数量,适当的时候将已有的事件处理程序移除掉或者采用事件委托机制减小注册的个数。下面咱们简单谈谈如何移除事件处理程序。
var EventUtil = { removeHandler: function (elm, type, handler) { if (elm.removeEventListener) { elm.removeEventListener(type, handler, false); } else if (elm.detachEvent) { elm.detachEvent('on' + type, handle); } else { elm['on' + type] = handler; } } }
事件的移除有个原则,怎么注册的就要原封不动的 copy 参数调用移除。此处须要注意的是当你的事件处理函数是匿名函数时,那将会永远也清理不掉了。
处理函数执行时,this 指向现代的浏览器指向的是 事件所在dom 节点
,老 IE 指向window。
事件发生后,会产生一个事件对象,做为参数传给监听函数。事件有若干的实例属性和实例方法。只讲下笔者认为重要的currentTarget
、target
、preventDefault()
、.stopPropagation()
、stopImmediatePropagation()
。
currentTarget
事件处理函数注册在什么节点上,那么 currentTarget 就永远的指向了该节点。target
,咱们回顾一下事件流的概念,事件会经历捕获阶段、目标阶段、冒泡阶段,能够感受到事件的传递画了个对称的钩,target 表示事件当前所处的节点位置。
咱们不只能够读取事件的状态,还能够人为的改变它的内部状态。
preventDefault()
阻止事件的默认行为,如 a 标签被点击时就会跳转到新的 url,咱们可使用event.preventDefault()来阻止跳转。
stopPropagation()
阻断事件的冒泡流或者事件捕获流。但若是同一节点上注册了多个事件处理程序,那么该节点上的事件处理程序会继续处理,它只会阻断事件向上或向下的传播。
stopImmediatePropagation()
更为狠毒,事件会中止在该事件处理程序上,同节点的其余事件处理程序也不会被触发了。
因为事件会在冒泡阶段
向上传播到父节点,所以能够把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫作事件的代理(delegation)。
<ul> <li>test</li> <li>test</li> <li>test</li> <li>test</li> <li>test</li> ... </ul>
假设业务须要给 li 添加 click 监听事件,那么初学者可能会采用直接获取 li 节点的方式进行事件程序的绑定,那么有几个 li,就须要注册多少个事件处理程序。能够采用事件代理机制实现性能优化,代码以下:
var ul = document.querySelector('ul'); ul.addEventListener('click', function (event) { if (event.target.tagName.toLowerCase() === 'li') { // some code } });
事件代理和事件委托是同一个东西,主要是利用事件冒泡和事件的实例属性 target,目的是减小事件处理程序的数量,提升性能。
红宝书
阮一峰 事件模型
🌚 前端学习QQ群: 538631558 🌚
【开发环境推荐】 Cloud Studio 是基于浏览器的集成式开发环境,支持绝大部分编程语言,包括 HTML五、PHP、Python、Java、Ruby、C/C++、.NET 小程序等等,无需下载安装程序,一键切换开发环境。 Cloud Studio提供了完整的 Linux 环境,而且支持自定义域名指向,动态计算资源调整,能够完成各类应用的开发编译与部署。