事件
Js与HTML之间的交互通过事件完成,事件,就是文档或浏览器窗口中发生的一些 特定的交互瞬间。事件类型有很多分类,例如:DOM事件类型,焦点事件,滚轮事件等等,说到这里就不得不说一下js的事件流
事件流
事件冒泡
事件冒泡,事件开始从具体的节点出发,逐步向上冒泡到外层节点。例如:点击id为a的div时,事件会一步步向上传递 a=> b => c,当我们点击click的时候,会依次输出123
<div id='c' onclick='console.log(3)'>
<div id='b' onclick='console.log(2)'>
<div id='a' onclick='console.log(1)'>
click
</div>
</div> </div> 复制代码
事件捕获
虽然事件捕获是 Netscape 唯一支持的事件流模型,但 IE9、Safari、Chrome、Opera 和 Firefox 目前也都支持这种事件流模型。尽管“DOM2 级事件”规范要求事件应该从 document 对象开始传播,但这些浏览器都是从 window 对象开始捕获事件的。 由于老版本的浏览器不支持,因此很少有人使用事件捕获。
DOM事件流
DOM事件流分三个阶段,捕获阶段,目标阶段和冒泡阶段,DOM事件流又分为DOM0级和DOM2级。
DOM0级事件处理程序
比如我们常见的click,load等等,DOM0级别事件被认定是元素的方法,所以其中this为元素本身,其作用域也是当前目标元素。
<div id='a' onclick='console.log(this.id)'>
click
</div>
复制代码
打印出来的this 就是元素本身。清除DOM0级事件可以直接赋值为null就会清除掉。
target.onclick = null
复制代码
DOM2级事件处理程序
DOM2级事件提供了两个方法,即大名鼎鼎的 addEventListener 和 removeEventListener. 他们都提供了三个参数 1.事件名称 2.事件方法 3.事件是在捕获阶段执行,还是在冒泡阶段执行,类型为布尔值,true在捕获阶段,false在冒泡阶段,默认为false
addEventListener
var target = document.getElementById('a')
target.addEventListener('click', function () {
console.log(this.id)
}, false)
复制代码
DOM2级事件,不会和DOM0级的冲突,所以在click的时候。会先打印this,再打印id,同时可以添加多个click事件,此时会先打印id,在打印 hello world,执行顺序和添加顺序保持一致
var target = document.getElementById('a')
target.addEventListener('click', function () {
console.log(this.id)
}, false)
target.addEventListener('click', function () { console.log('hello world') }, false) 复制代码
事件优先级:DOM0级事件 > DOM2 一道简单的面试题,点击c依次输出abc和依次输出cba
<div id="a">
a
<div id="b">
b
<div id="c">
c </div> </div> </div> 复制代码
removeEventListener
用于移除事件,移除事件的时候,必须的有两个参数,事件名称和处理函数。函数必须为是同一个函数,否则无法移除。
var target = document.getElementById('a')
function sayHello () {
console.log('hello world')
}
target.addEventListener('click', sayHello, false)
target.removeEventListener('click', function () { console.log('hello world') }, false) // 无效不能移除 target.removeEventListener('click', sayHello, false) // 有效 复制代码
IE 实现了与 DOM 中类似的两个方法:attachEvent()和 detachEvent()。这两个方法接受相同 的两个参数:事件处理程序名称与事件处理程序函数。由于 IE8 及更早版本只支持事件冒泡,所以通过 attachEvent()添加的事件处理程序都会被添加到冒泡阶段。
鉴于此封装一个小型的事件处理方法,用来兼用跨平台:
var EventUtil = {
addHandler: function(element, type, handler, isBubble = false) {
if (element.addEventListener){
element.addEventListener(type, handler, isBubble);
} else if (element.attachEvent){
element.attachEvent("on" + type, handler); } else { element["on" + type] = handler; } }, removeHandler: function(element, type, handler, isBubble = false){ if (element.removeEventListener){ element.removeEventListener(type, handler, isBubble); } else if (element.detachEvent){ element.detachEvent("on" + type, handler); } else { element["on" + type] = null; } } }; 复制代码
事件对象
无论是DOM0 还是 DOM2 在执行事件的时候,都会返回一个Event对象给我们。其中包含很多的属性,重点说下下面几个。
1.preventDefault,取消默认行为,例如a标签的跳转;相关的属性为cacelable,可以通过preventDefault取消默认行为,cacelable为true,否则为false
2.type 为出发该行为的事件是什么类型,例如click等
3.eventPhase 当前触发事件的阶段,是冒泡,捕获,还是目标阶段
4.stopPropagation 可以停止冒泡和捕获的继续传递
5.srcElement 和 target一样,事件的目标对象
自定义事件
除了平时使用的原生事件,有时候需要自定义事件。创建自定义事件的方法有两种,Event() 构造函数 和 CustomEvent() 构造函数,还有个createEvent()已经废弃,但是仍然有浏览器支持,所以我们暂且不说,有兴趣的可以自己看看。
Event()
支持两个参数: 1.typeArg 事件名称 2.eventInitOption 事件初始化配置,配置内容包含三个字段
参数 | 类型 | 说明 |
---|---|---|
bubbles | 布尔 | 表示该事件是否冒泡 |
cancelable | 布尔 | 表示该事件能否被取消 |
composed | 布尔 | 指示事件是否会在影子DOM根节点之外触发侦听器。 |
ex:
// 创建事件
const ev = new Event('testEvent')
// 监听事件
document.addEventListener('testEvent', function () {
console.log('testEvent执行了')
}) // 触发事件 document.dispatchEvent(ev) 复制代码
CustomEvent()
支持两个参数: 1.typeArg 事件名称 2.eventInitOption 事件初始化配置,配置内容包含事件部分字段,详情可以查看CustomEvent,额外还有一个detail字段可用于传递额外的参数
// 创建事件
const ev = new CustomEvent('testEvent', {
detail: {
extraParams: 1
}
}) // 监听事件 document.addEventListener('testEvent', function (e) { console.log('testEvent执行了', e.detail) }) // 触发事件 document.dispatchEvent(ev) 复制代码
事件代理
事件代理:就是将子元素的事件通过冒泡的形式交由父元素来执行。 常规应用:
<div id="idd">
<div>
<section>
<div>1</div>
<div>1</div>
<div>1</div> <div>1</div> <div>1</div> <div>1</div> <div>1</div> <div>1</div> <div>1</div> <div>1</div> <div>1</div> <div>1</div> </section> </div> </div> 复制代码
function findParent (child, parent) {
if (child.parentNode === parent) {
return true
} else {
if (child.parentNode.nodeName.toLowerCase !== 'html') {
findParent(child.parentNode, parent) } else { return false } } } window.onload = function () { const parent = document.getElementById('idd') parent.onclick = function (e) { let flag = findParent(e.target, parent) if (e.target.nodeName.toLowerCase() === 'div' && flag) { console.log('div') } } } 复制代码
以上的栗子,能够让我们减少对DOM的操作,提升性能,发现这段代码的事前消耗更低,因为只取得了一个 DOM 元素,只添加了一个事件处理程序。虽然对用户来说最终的结果相同,但这种技术需要占用的内存更少。所有用到按钮的事件(多数鼠标事件和键盘事件)都适合采用事件委托技术。
最后,事件种类太多,使用的难易程度也不尽相同;在使用事件时,需要考虑如下一些内存与性能方面的问题。
1.有必要限制一个页面中事件处理程序的数量,数量太多会导致占用大量内存,而且也会让用户感觉页面
反应不够灵敏。
2. 建立在事件冒泡机制之上的事件委托技术,可以有效地减少事件处理程序的数量。
3. 建议在浏览器卸载页面之前移除页面中的所有事件处理程序。</p>
复制代码
有错误的地方或者不足的地方欢迎指正。
来源:oschina
链接:https://my.oschina.net/u/4381645/blog/4282320