DOM事件的那些事儿

DOM(Document Object Model) 即文档对象模型,是针对HTML和XML文档的一个API,DOM描绘了一个层次化的节点树,允许开发人员进行添加、移除和修改页面的某一部分。

一、DOM事件的级别

  • DOM0 点击事件的JS中写法是: element.onclick=function(){ },如果在HTML中就是onclick属性上加一个JS语句。
    删除DOM0事件处理程序,只要将对应事件属性置为null即可,即element.onclick=null
  • DOM2 新增的点击事件JS中写法是:element.addEventListener('click', function(){}, false)。最后一个参数为true的时候表示在捕获阶段调用程序,如果是false,表示在冒泡阶段调用事件处理程序,不填则默认为false
    删除DOM2事件处理程序,用removeEventListener实现。
    IE中的DOM2级事件处理使用了attachEvent来实现,IE9以下版本只支持冒泡事件,所以attachEvent添加的事件都是冒泡阶段。attachEvent添加的事件第一个参数是onclick而非标准事件中的click。使用detachEvent实现删除事件。
  • DOM3 定义方式没变,知识新增了很多事件类型,包括UI事件,鼠标事件,焦点事件,滚轮事件等等。如:element.addEventListener('keyup', function(){}, false)

因为DOM1主要专注于HTML文档和XML文档,没有涉及事件处理,所以事件处理直接从DOM0跳到DOM2。

二、DOM事件模型

DOM事件模型分为两类:一类是IE所使用的冒泡型事件(Bubbling);另一类是DOM标准定义的冒泡型与捕获型(Capture)的事件。除IE外的其他浏览器都支持标准的DOM事件处理模型。

DOM事件模型

  • 冒泡型事件处理模型(Bubbling)
    如上图所示,冒泡型事件处理模型在事件发生时,首先在最精确的元素上触发,然后向上传播,直到根节点。反映到DOM树上就是事件从叶子节点传播到根节点。

  • 捕获型事件处理模型(Captrue)
    相反地,捕获型在事件发生时首先在最顶级的元素上触发,传播到最低级的元素上。在DOM树上的表现就是由根节点传播到叶子节点。

  • 标准的DOM事件处理模型
    标准的事件处理模型分为三个阶段:
    (1) 父元素中所有的捕获型事件(如果有)自上而下地执行
    (2) 目标元素的冒泡型事件(如果有)
    (3) 父元素中所有的冒泡型事件(如果有)自下而上地执行

三、DOM事件流

DOM标准采用捕获+冒泡。两种事件流都会触发DOM的所有对象,从document对象开始,也在document对象结束。


DOM事件流

标准DOM事件流包括三个阶段:事件捕获阶段,处于目标阶段和事件冒泡阶段

  • 事件捕获阶段:实际目标(<div>)在捕获阶段不会接收事件。也就是在捕获阶段,事件从document到<html>再到<body>就停止了。上图中为1~3。
  • 处于目标阶段:事件在<div>上发生并处理。但是事件处理会被看成是冒泡阶段的一部分。
  • 冒泡阶段:事件又传播回文档。

四、DOM事件捕获的具体流程

DOM事件捕获具体流程

首先接收的是window,然后是ducument,再是html标签(js获取html节点用document.documentElement),然后是body,最后随着节点父子关系一级一级往下传,直到目标元素。
冒泡则相反,从目标元素到window一级一级往上。
通过代码来描述事件捕获过程:

<div id="ev"  style='width:100px;height: 100px;background: blue'></div>
// 把打印顺序搅乱,以免误以为是因为执行顺序影响
<script>
        var ev = document.getElementById('ev');

        ev.addEventListener('click', function (e) {
            console.log('ev captrue');
        }, true);

        window.addEventListener('click', function (e) {
            console.log('window captrue');
        }, true);

        document.addEventListener('click', function (e) {
            console.log('document captrue');
        }, true);

        document.body.addEventListener('click', function (e) {
            console.log('body captrue');
        }, true);

        document.documentElement.addEventListener('click', function (e) {
            console.log('html captrue');
        }, true);

        // 打印结果如下:
       // window captrue
       // document captrue
      // html captrue
      // body captrue
      // ev captrue

</script>

通过代码来描述事件冒泡过程:

<div id="ev"  style='width:100px;height: 100px;background: blue'></div>
// 把打印顺序搅乱,以免误以为是因为执行顺序影响
<script>
        var ev = document.getElementById('ev');

        ev.addEventListener('click', function (e) {
            console.log('ev captrue');
        }, false);

        window.addEventListener('click', function (e) {
            console.log('window captrue');
        }, false);

        document.addEventListener('click', function (e) {
            console.log('document captrue');
        }, false);

        document.body.addEventListener('click', function (e) {
            console.log('body captrue');
        }, false);

        document.documentElement.addEventListener('click', function (e) {
            console.log('html captrue');
        }, false);

        // 打印结果如下:
        // ev captrue
        // body captrue
        // html captrue
        // document captrue
       // window captrue
</script>

五、Event对象的常见应用

  • event.preventDefault():阻止默认行为。常用的情况就是给一个<a>标签绑定了click事件,响应函数中设置了 event.preventDefault(),就阻止了链接跳转的行为。
  • event.stopPropagation():阻止冒泡行为。比如:一个父级元素绑定了一个事件,子元素绑定了另一个事件,如果想父级元素做一件事,子元素做一件事,两件事是分开的,互不影响,就需要给子元素事件中设置event.stopPropagation(),否则子元素事件执行时,按照冒泡的原则,父级元素事件也会响应。
  • event.stopImmediatePropagation:除了该事件的冒泡行为被阻止之外(event.stopPropagation方法的作用),该元素绑定的后序相同类型事件的监听函数的执行也将被阻止。如一个元素上绑定了2个click事件a和b,如果给a函数中添加event.stopImmediatePropagation后,则阻止click事件冒泡,并且阻止了b函数的执行。
  • event.currentTarget:当事件遍历DOM时,标识事件的当前目标(类似于this)。如下例子:
    <p>1</p>
    <p>2</p>
    <p>3</p>
    <script>
        var ps = document.getElementsByTagName('p');
        for (var i = 0; i < ps.length; i++) {
            ps[i].addEventListener('click', func, false);
        }

        function func(e) {
            console.log(e.currentTarget);  // 打印所点击对应的<p>节点
            // 该函数用作事件处理器时: this === e.currentTarget
        }
    </script>
  • event.target:表示一个触发事件的对象的引用,常用来实现事件委托。如:
<ul id="ul">
   <li>1</li>
   <li>2</li>
   <li>3</li>
</ul>

    <script>
        document.getElementById("ul").addEventListener('click', function(){
              console.log(event.target);   // 当点击1时,打印<li>1</li>
              console.log(event.currentTarget);  // 当点击1或2或3时,都打印整个<ul>
        });
    </script>

六、自定义事件

以上所讲都是DOM一些自带的事件,当然也可以自己定义一些事件。

  • ① 自定义一个事件:new Event()
  • ② 给一个dom节点绑定自定义的事件
  • ③ 使用dispatchEvent() 分派事件
<div id="ev"></div>

<script>
var ev = document.getElementById('ev');
var evt = new Event('test');
ev.addEventListener('test', function () {
    console.log('test dispatch');
});
setTimeout(function () {
    ev.dispatchEvent(evt);
}, 3000);

// 打开页面3面后打印 'test dispatch'
</script>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容