事件
在网页中,如果想与使用者进行“互动”,必须要通过某种办法晓得他都做了什么。当然,浏览器开发者们早已依据 W3C 事件标准 实现好了底层的逻辑,咱们只须要通过 Web API 中的 DOM Event,通过注册想监听的 DOM 元素和事件的事件监听器(Event Listener)就能够轻松把握使用者在网页上的一举一动。
事件监听
咱们能够在想要监听事件的 DOM 元素上通过 addEventListener 注册监听器。例如:
document.querySelector('#id').addEventListener('click', clickHandler)
当点击 #id
元素时会触发 clickHandler
并传入一个事件,其内容蕴含事件传递过程中必要的数据,例如指标元素、以后元素、传递阶段等等。这时咱们便能够从中获取所须要的数据,并针对这些数据做你想做的事。
当初的网站有大量的互动,如果通过事件监听一个一个去写,除了效力很差,写起来也很麻烦;这时就体现出“事件代理”的重要性了!
不过在说到事件代理之前,现须要了解 DOM Tree 上的工夫传递机制是怎么的
工夫传递
能够参考 W3C 所定义的 Event Flow 图:
标准中定义了工夫传递的三个阶段:
- 捕捉阶段:由 DOM Tree 的根节点顺次向内传递,过程中触发各别元素的捕捉阶段事件监听。
- 指标阶段:达到事件指标(Event Target),依照注册程序触发事件监听。
- 冒泡阶段:由事件指标依序向外传递,过程中触发各别元素的冒泡阶段事件监听。
如图所示,当使用者触发一个 DOM 元素的事件时,首先会进入 捕捉阶段(Capture Phase),从根结点逐渐向事件指标传递;达到指标后则进入 指标阶段(Target Phase),接着就开始折返,进入向根结点传递的 冒泡阶段(Bubbling Phase)。
在应用 addEventListener
注册事件监听器时,能够通过传递第三个参数,指定此事件监听要在什么阶段触发:
elem.addEventListener('click', eventHandler) // 未指定,预设为冒泡
elem.addEventListener('click', eventHandler, false) // 冒泡
elem.addEventListener('click', eventHandler, true) // 捕捉
elem.addEventListener('click', eventHandler, {capture: true // 是否为捕捉。IE、Edge 不反对。其余属性请参考 MDN})
通过简略的来回传递,这样就能更精准的管制触发的机会了!
事件代理
当初终于聊到了事件代理。因为事件传递的机制,子元素的事件在传递过程中势必会通过它的父元素;而事件代理,顾名思义就是将子元素事件监听器交由父元素代理。
什么意思呢?咱们间接看个简略的对照例子:
首先是 HTML 骨架:
<button id="push">push</button>
<button id="pop">pop</button>
<ul id="list"></ul>
没有事件代理
(function() {document.querySelector('#push').addEventListener('click', pushHandler)
document.querySelector('#pop').addEventListener('click', popHandler)
const list = document.querySelector('#list')
function pushHandler() {list.appendChild(getNewElem(list.childNodes.length))
}
function popHandler() {document.querySelectorAll('#list>li')[list.childNodes.length - 1].remove()}
function getNewElem(text) {const elem = document.createElement('li')
elem.innerText = text
elem.addEventListener('click', eventHandler)
return elem
}
function eventHandler(e) {alert(e.target.innerText)
}
})()
有事件代理
(function() {document.querySelector('#push').addEventListener('click', pushHandler)
document.querySelector('#pop').addEventListener('click', popHandler)
const list = document.querySelector('#list')
list.addEventListener('click', listClickHandler)
function pushHandler() {list.appendChild(getNewElem(list.childNodes.length))
}
function popHandler() {document.querySelectorAll('#list>li')[list.childNodes.length - 1].remove()}
function getNewElem(text) {const elem = document.createElement('li')
elem.innerText = text
return elem
}
function listClickHandler(e){if (e.target.tagName === 'LI') alert(e.target.innerText)
}
})()
差别在于事件监听的指标元素
在没有事件代理的版本中每一个 li
上都注册了事件监听器,当数量越来越多时浏览器也就建设了越来越多的监听器,无形中对性能有很大的影响;反之在有事件代理的版本中,将事件监听器注册在了外层的 ul
上,无论内容有多少,浏览器都只须要承当一组事件监听器的耗费。
库和框架中的事件处理
在 DOM 事件处理的这部分,jQuery 和 Vue 都将原生的事件监听器做了封装,不便咱们疾速设定、应用,甚至会主动帮你移除无用的事件监听。
然而在 React 中,React DOM 上间接注册的事件监听器,其实监听的是 React 额定封装过的 React DOM Event,并将全副事件代理到 document 上,这与原生事件有很大不同;特地是如果混用 React DOM Even tListener 及原生的 addEventListener
,事件监听器之间的执行程序很有可能会和预期不统一,在写 React 的时候要特地留神。
有趣味深入研究的话能够在React 源码 中查找对于事件处理的代码局部。
本文首发微信公众号:前端先锋
欢送扫描二维码关注公众号,每天都给你推送陈腐的前端技术文章
欢送持续浏览本专栏其它高赞文章:
- 深刻了解 Shadow DOM v1
- 一步步教你用 WebVR 实现虚拟现实游戏
- 13 个帮你进步开发效率的古代 CSS 框架
- 疾速上手 BootstrapVue
- JavaScript 引擎是如何工作的?从调用栈到 Promise 你须要晓得的所有
- WebSocket 实战:在 Node 和 React 之间进行实时通信
- 对于 Git 的 20 个面试题
- 深刻解析 Node.js 的 console.log
- Node.js 到底是什么?
- 30 分钟用 Node.js 构建一个 API 服务器
- Javascript 的对象拷贝
- 程序员 30 岁前月薪达不到 30K,该何去何从
- 14 个最好的 JavaScript 数据可视化库
- 8 个给前端的顶级 VS Code 扩大插件
- Node.js 多线程齐全指南
- 把 HTML 转成 PDF 的 4 个计划及实现
- 更多文章 …