DOM事件的那些事儿

作者: JokerPeng | 来源:发表于2017-03-13 13:48 被阅读0次

    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>
    

    相关文章

      网友评论

        本文标题:DOM事件的那些事儿

        本文链接:https://www.haomeiwen.com/subject/miifnttx.html