美文网首页
DOM事件(一)

DOM事件(一)

作者: zhaochengqi | 来源:发表于2018-09-12 08:47 被阅读8次

    JavaScript使用侦听器来订阅事件,实现与HTML之间的交互。

    DOM2级规范开始尝试标准化DOM事件。主流浏览器已全部实现了DOM2级事件模块的核心部分。IE8是最后一个仍使用其专有事件系统的主要浏览器。

    一、 DOM事件流

    1.1 事件流

    事件流描述的是从页面接收事件的顺序。

    • 事件冒泡(event bubbling)
      事件开始时由最具体的元素(嵌套层次最深的那个节点)接收,然后逐层向上传播。

      the Element > Elements outer > body > html > document

    • 事件捕获(event capturing)
      事件由外层节点接收事件传递到具体节点

      document > html > body > Elements outer > the Element

    尽管“DOM2级事件”规范要求事件应该从document对象开始传播,但IE9、Safari、Chrome、Opera和Firefox都是从window对象开始捕获事件的。在冒泡模式中也将事件一直冒泡到window对象


    “DOM2级事件”规定的事件流包括三个阶段:

    1. 事件捕获阶段

      事件从 documenthtml 再到 body。这个阶段目标节点不会接收到事件

    2. 处于目标阶段

      目标节点接收到事件,在事件处理中被看做冒泡阶段的一部分

    3. 事件冒泡阶段

      事件又从目标节点传播回文档

    即使“DOM2级事件”规范明确要求捕获阶段不会涉及事件目标,但主流浏览器高版本都会在捕获阶段触发事件对象上的事件。结果就有两个机会在目标对象上面操作事件。

    IE8及更早版本不支持DOM事件流(不支持捕获)

    二、 事件处理程序

    click、load、mouseover等由用户或浏览器自身执行的某种动作就是事件。
    而响应某个事件的函数就是事件处理程序(或事件侦听器)

    事件处理程序的名字以“on”开头:onclick、onload;为事件制定处理程序有好几种方式。

    【事件类型传送门】

    2.1 HTML事件处理程序

    使用一个与相应事件处理程序同名的HTML特性来指定,指定的值应该是 能够执行的JavaScript代码,

    所以调用页面其他地方定义的函数时不能只填一个函数名哦 。

    <a onclick="alert('clicked!')">click me</a>
    <a onclick="foo()">click me</a>
    
    • 事件处理程序中的代码在执行时有权访问全局作用域中的任何代码

    • 可以访问局部变量event(事件对象,你不必自己定义它,也不用从函数的参数列表中读取)。

    • 在函数内部this值等于事件的目标元素。

    <a onclick="console.log(event)">click me</a>
    
    > MouseEvent {isTrusted: true, screenX: 39, screenY: 120, ...}
    

    事件处理程序作用域使用with像下面这样扩展

    function(){
        with(document){
            with(this){
                
            }
        }
    }
    

    SO,事件处理程序访问自己的属性就肥肠简单了

    <a onclick="console.log(href)">click me</a>
    

    如果当前元素是一个表单输入元素,则作用域中还会包含表单元素的入口

    function(){
        with(document){
            with(this.form){
                with(this){
                    
                }
            }
        }
    }
    

    这样不需要引用表单元素就能访问其他表单字段

    <form action="#">
        <input type="text" name="username">
        <input type="button" value="submit" onclick="console.log(username.value)">
    </form>
    
    

    BUT,如果不是在HTML特性中直接写的执行代码,而是调用的其他地方定义的函数,那么在指定函数中是无法直接访问上述扩展的作用域的。而且this等于window(除非你用箭头函数)

    <button id="btn1" onclick="console.log(id)">HTML</button> // btn1
    <button id="btn2" onclick="handler()">HTML2</button>
    <script>
        function handler() {
          console.log(id)  //Uncaught ReferenceError: id is not defined
        }
    </script>
    

    通过HTML指定事件处理程序最大的缺点就是HTML与JavaScript代码紧密耦合


    2.2 DOM0级事件处理程序

    通过JavaScript指定事件处理程序的传统方式: 将一个函数赋值给一个事件处理程序属性

    每个元素(包括window和document)都有自己的事件处理程序属性,这些属性通常全部小写。
    这个方法会替换这个元素上所有已存在的相应事件处理程序

    //首先取得一个要操作的对象的引用
    var btn = document.getElementById('btn')
    //将属性值设为一个函数
    btn.onclick = function(){console.log('clicked:'+this.id)}
    

    tips:

    1. 使用DOM0级方法指定的事件处理程序被认为是元素的方法。因此事件处理程序是在元素的作用域内执行(函数中this引用当前元素,但没有像HTML那样为我们扩展作用域)
    2. 以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理
    3. 删除通过DOM0级方法指定的事件处理程序,只需将相应属性值设为null: btn.onclick=null(同样可以清除通过HTML特性设置的相应事件处理程序)
    4. 通过HTML指定事件处理程序,则节点对象相应的事件处理属性的值就是一个包含着在HTML特性中指定代码的函数。eg.
      <button id="btn1" onclick="console.log(id)">HTML</button>
      <button id="btn2" onclick="handler()">HTML2</button>
      <button id="btn3">DOM0</button>
      <script>
        function handler() {
          console.log(this.id)
        }
        //首先取得一个要操作的对象的引用
        var btn1 = document.getElementById('btn1')
        var btn2 = document.getElementById('btn2')
        var btn3 = document.getElementById('btn3')
        //将属性值设为一个函数
        btn3.onclick = function(){console.log(this.id)}
    
        console.log(btn1.onclick) //ƒ onclick(event) {console.log('clicked:'+this.id, event, this, id)}
        console.log(btn2.onclick) //ƒ onclick(event) {handler()}
        console.log(btn3.onclick) //ƒ (){console.log(this.id)}
      </script>
    

    由此也可以看出为什么在HTML特性中指定外部处理函数时,在函数内无法访问扩展的作用域(直接访问节点对象属性)的原因(函数作用域)。

    为我们扩展作用域的应该是 onclick(event)函数:)


    2.3 DOM2级事件处理程序

    “DOM2级事件”定义了两个方法来指定和删除事件处理程序:

    ```js
    //那些不支持参数options的浏览器,会把第三个参数默认为useCapture,即设置useCapture为true
    target.addEventListener('click', listener ,{
        capture: Boolean, 
        //表示 listener 会在该类型的事件捕获阶段传播到该 EventTarget 时触发。
       
        once: Boolean, 
        //表示 listener 在添加之后最多只调用一次。如果是 true, 
        //listener 会在其被调用之后自动移除。
        
        passive: Boolean, 
        //表示 listener 永远不会调用 preventDefault()。
        //如果listener仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告
        //添加passive参数后,touchmove事件不会阻塞页面的滚动(同样适用于鼠标的滚轮事件)
    });
    
    /**
    true 是指在DOM树中,注册了该listener的元素,是否会先于它下方的任何事件
    目标,接收到该事件。沿着DOM树向上冒泡的事件不会触发被指定为use capture
    (也就是设为true)的listener。当一个元素嵌套了另一个元素,两个元素都对
    同一个事件注册了一个处理函数时,所发生的事件冒泡和事件捕获是两种不同的
    事件传播方式。事件传播模式决定了元素以哪个顺序接收事件。进一步的解释可
    以查看 事件流 及 JavaScript Event order 文档。 如果没有指定, useCapture
    默认为 false 。 
    **/
    target.addEventListener('click', listener, true)
    ```
    

    tips:

    1. DOM2级方法添加的事件处理程序也是在其依附的元素的作用域中执行的(没有扩展作用域)
    2. 可以添加多个事件处理程序,按顺序触发
    3. 通过addEventListener添加的事件处理程序只能通过使用removeEventListener来移除,移除时传入的参数与添加时使用的参数要相同。这也意味着通过addEventListener添加的匿名函数将无法移除
    4. IE8不支持

    2.4 IE事件处理程序

    IE实现了与DOM2级中类似的两个方法:attachEventdetachEvent

    tips:

    1. IE8及更早版本只支持事件冒泡,所以通过attachEvent添加的事件处理程序都会被添加到冒泡阶段
    2. attachEvent第一个参数是onclick而不是DOM中addEventListenerclick
    3. 事件处理程序在全局作用域中运行,因此this等于window
    4. 可以为一个元素添加多个事件处理程序,但不同于DOM方法,他们不是按添加他们的顺序执行,而是以相反的顺序被触发。
    5. 只有IE和Opera支持IE事件处理程序

    FINAL

    作用域 HTML/函数 DOM0 DOM2 IE
    event Y Y Y Y
    this Y/N Y Y N
    this.xxx Y/N N N N

    var div1 = document.getElementById('div1')
    var div2 = document.getElementById('div2')
    function handler(){
      var ep = ''
      switch(event.eventPhase){
        case 1: ep = '捕获阶段'; break;
        case 2: ep = '处于目标'; break;
        case 3: ep = '冒泡阶段'; break;
        default: ep = 'error';
      }
      console.log(this.id, ep)
    }
    div1.addEventListener('click', handler, true)
    div1.addEventListener('click', handler, false)
    div2.addEventListener('click', handler, true)
    div2.addEventListener('click', handler, false)
    
    • 点击inner:


      image
    • 点击outer


      image

    <= to be continued

    相关文章

      网友评论

          本文标题:DOM事件(一)

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