DOM事件和事件委托

1 DOM 事件模型(DOM 事件机制)

HTML DOM 允许 JavaScript 对 HTML 事件作出反应。JavaScript 能够在事件发生时执行,比如当用户点击某个 HTML 元素时。JavaScript与HTML之间的交互是通过事件实现的。

事件由事件源、事件类型和事件处理程序三部分组成

事件执行步骤:获取事件源 => 注册(绑定)事件 => 采用函数赋值形式添加事件处理程序

2 事件流

事件流描述了页面接受事件的顺序,IE 和 Netscape 开发团队提出了几乎完全相反的事件流方案,IE支持事件冒泡流,Netscape 支持事件捕获流。2002年,W3C发布标准,规定浏览器应该支持两种调用方式,开发者自己选择把函数放在捕获阶段还是冒泡阶段。

2.1 事件捕获

Netscape事件流被称作事件捕获流,规定为从外向内找监听函数的过程。

过程实例:当点击div元素时,触发click事件顺序:
document => html => body => div

在事件捕获中,click事件首先由document元素捕获,随后沿着DOM树向下传播,直到到达目标元素。

由于旧版本浏览器不支持,因此通常建议使用事件冒泡,特殊情况使用时间捕获。


2.2 事件冒泡

IE事件流被称作事件冒泡流,规定为从内向外找监听函数的过程。

过程实例:当点击div元素时,触发click事件顺序:
div => body => html => document

在事件冒泡中,被点击的元素div最先触发click事件,然后,click事件沿着DOM树一路向上,在经过的每个节点上以此触发,直至到达document对象

所有现代浏览器都支持事件冒泡,但实现方式存在部分变化,如IE5.5早期版本会跳过html元素,直接从body到document,现代浏览器中的事件会一直冒到window

取消冒泡(阻止事件冒泡)

  • 捕获不可以取消,但冒泡可以
  • e.stopPropagation()可以中断(取消)冒泡,浏览器不在向上走
  • 一般用于封装某些独立组件

不可阻止默认动作

  • 阻止默认动作语法:e.preventDefault()
  • 所有冒泡皆可取消,但默认动作有的可以取消有的不能取消
  • MDN搜索scroll event,看到 Bubbles(该事件是否冒泡) 和 Cancelable(开发者是否可以阻止默认事件)
  • 取消scroll滚动事件
    不可取消默认动作,因为现有滚动才有滚动事件
    但可以阻止wheel和touchstart的默认动作,注意需要找准滚动条所在元素,同时用CSS让滚动条width:0;
    也可使用overflow:hidden;直接取消滚动条,但此时JS仍然可以修改scrollTop


2.3 addEventListener()

W3C模型

  • 先捕获(父 => 子),再冒泡(子 => 父)
  • 特例:当只有一个div被监听(不考虑父子同时被监听),fn分别在捕获阶段和冒泡阶段监听click事件,用户点击的元素就是开发者监听的,则谁先监听谁先执行(2022年已修复,变为先捕获再冒泡)
  • 注意e对象会被传给所有监听函数
  • 事件结束后,e对象就不存在了(其实是存在的,只是变成了currentTarget = null,更多了解后面再讨论,暂时认为不存在了),如果需要继续使用,可以使用变量保存const t = e.currentTarget

注意:关于targetcurrentTarget的区别:

  • e.target—用户操作的元素
  • e.currentTarget—程序员监听的元素
  • this就是e.currentTarget
  • 实例:div > span{文字},用户点击文字时,e.target就是span,e.currentTarget是div

事件绑定API

  • IE5*:
// 冒泡
father.attachEvent('onclick', fn)
  • 网景(Netscape ):
// 捕获
father.addEventListener('click', fn)
  • W3C:
// bool处为布尔值,决定采用冒泡还是捕获,
// 不填时默认为false,则使用冒泡
// 填true时为网景的捕获
father.addEventListener('click', fn,bool)


3 事件委托

事件委托就是利用冒泡的原理,但事件触发时,把要做的事件委托给父元素(或父元素的父元素)来处理,将事件加到父级上,通过判断事件来源的子集,执行相应操作,从而避免对特定每个节点添加事件监听器,减少占用内存空间,提升性能。

  • 事件委托利用事件冒泡,可以只使用一个事件处理程序来管理一种类型的事件。
    实例
    情景:需要给100个按钮添加点击事件。
    解决方法:监听这100个按钮的祖先,由于这100个按钮都是祖先的后代,所以他们的事件都会向上冒泡,最终都会由添加给祖先的函数来处理,只需要检查具体子元素(event对象)的相应属性/内容就可以确定,然后执行相应操作即可。

  • 需要监听目前不存在的元素的点击事件时,也可以通过事件委托实现。
    解决方法:监听目标元素的祖先,等点击的时候再查看是否是需要的监听的元素即可

优点

  • 减少页面所需内存,提升整体性能
  • 可以用于监听动态元素
  • 节省花在设置页面事件处理程序上的时间(只用指定一个处理函数,节省DOM引用)
  • document 对象随时可用,任何时候都可以给它添加事件处理程序,只要页面渲染出可点击的元素,就可以无延迟地起作用

适用事件

最适合使用事件委托的事件包括:clickmousedownmouseupkeydownkeypress

主要事项

  • JS支持,也不支持事件,DOM事件不属于JS的功能,属于浏览器提供的DOM的功能
  • JS只是调用了DOM提供的addEventListener而已
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容