DOM事件和事件委托

【前言】

在网页端、移动端H5、小程序等各个终端环境的前端开发中随处可见事件的运用,可见事件机制的是前端这一块的重中之重。

【目录】

  一、从实例看事件传递

  二、事件传递

  三、事件添加

  四、取消冒泡

  五、事件委托

    

  六、封装事件委托

  七、自定义事件

  

【正文】

一、从实例看事件传递

以下面这个普通的html文件为例

<!DOCTYPE html>

<html lang="en" onclick="handleClickHtml()">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Document</title>

</head>

<body onclick="handleBodyClick()">

<div id="div1" onclick="handleClick2()">

<button id="button1" onclick="handleClick1()">handleClick1</button>

</div>

<script>

function handleClick1(e) {

console.log('click1')

}

function handleClick2(e) {

console.log('click2')

}

function handleBodyClick(e) {

console.log('body clicked')

}

function handleClickHtml(e) {

console.log('html clicked')

}

document.addEventListener('click', e => {

console.log('document clicked')

})

</script>

</body>

</html>

例子中给document、html、body、div、button都添加了点击事件,其执行结果如下:

二、事件传递

事件产生后,从window对象自上而下向目标节点传递,抵达目标节点后会沿着相反方向传递

事件捕获:由内向外找监听函数

事件冒泡:由外向内找监听函数

W3C为了同时让IE和景文满意,只能规定浏览器应该同时支持两种调用方式

三、事件添加

  • 事件绑定API

    • IE5:X.attachEvent('onclick',fn)//冒泡
    • 网景:X.addEventListener('cilck',fn)//捕获
    • W3C:X.addEventListener('cilck',fn,bool)

  • 如果bool不传或为falsy,

    就让fn走冒泡

  • 如果booltrue,

    就让fn走捕获

四、取消冒泡

看下面这个例子,可以通过stopPropagation来阻止事件继续往上冒泡,window、document、html上添加的点击事件均不会生效

<!-- 省略了部分代码 -->

<div id="div1">

<button id="button1">button</button>

</div>

<script>

var div1 = document.getElementById('div1')

var button1 = document.getElementById('button1')

div1.addEventListener('click', (e) => {

console.log(e.currentTarget)

}, false)

button1.addEventListener('click', (e) => {

console.log(e.currentTarget)

e.stopPropagation() // 关键代码:阻止了click事件继续往上冒泡

}, false)

// 以下是监听html、document的点击事件

function handleClickHtml(e) {

console.log('html clicked')

}

document.addEventListener('click', e => {

console.log('document clicked')

})

</script>

点击button的打印结果如下:

五、事件委托

大批量事件监听性能问题考虑以下场景:

有个一个长消息列表实时接受新的消息,滚动到底部时加载更多消息,点击消息可进入回话窗口页面,消息左滑消息此条消息

问题:

如何给此消息列表添加左滑事件?

抽象来看这个问题就是如何高效的给大批量(甚至是无限量)节点添加事件,这里暂时不考虑vue、react等框架

性能问题原因

每注册一个事件监听监听都需要使用一定内存,同时在dom节点多了之后事件经过的捕获、冒泡阶段要处理事件传递也就慢了

事件委托

父节点嵌套的子节点的触发的事件会通过事件冒泡到达父节点,事件处理程序不直接绑定到子节点,统一由公共父节点进行事件监听。

e.target表示触发事件的元素,通过e.target可以判断具体响应那个元素的事件

e.currentTarget表示事件绑定的元素,在事件委托情况下指向同一个元素

采用事件委托改造本文例子

<!-- 省略了部分代码 -->

<div id="div1">

<button id="button1">button</button>

</div>

<script>

document.addEventListener('click', e => {

// e.target:触发事件的元素

// e.currentTarget:事件监听器绑定的元素

// console.log(e.target, e.currentTarget)

if (e.target.id === 'button1') {

return console.log('button1 clicked')

}

if (e.target.id === 'div1') {

return console.log('div1 clicked')

}

if (e.target.tagName === 'HTML') {

return console.log('html clicked')

}

if (e.target.tagName === 'BODY') {

return cosnole.log('body clicked')

}

})

</script>

依次点击html、div1、button1会依次打印结果:

六、封装事件委托

//部分代码

on('click', '#div', 'button',() => {

console.log('button 被点击了')

})

functionon(eventType, element, selector, fn) {

if(!(element instanceof Elemnet) {

element = document.querySelector(element)

}

element.addEventListener(eventType, e => {

let el = e.target

while (!el.matches(selector)){

if (element === el) {

el = null

break

}

el = el.parentNode

}

el && fn.call(el, e, el)

})

return element

}

自定义事件

以上是 DOM事件和事件委托 的全部内容, 来源链接: utcz.com/a/32704.html

回到顶部