整理DOM事件相关知识点

DOM事件相关内容

当用户与浏览器发生的一些交互时, 若是但愿去得到用户行为, 就须要借助事件来完成. 事件部份内容在 JS中重要性不言而喻. html

罗列须要了解与事件相关的知识以下: 这也是面试中遇到的问题.面试

  • DOM 事件的级别
  • DOM 事件模型
  • DOM 事件流
  • DOM 事件处理程序
  • 描述DOM事件捕获(冒泡)的具体流程
  • Event对象常见的应用
  • 自定义事件

事件级别

  • DOM0
  • DOM2
  • DOM3

DOM 事件模型

  • 事件冒泡
  • 事件捕获

什么是事件流

要想明白事件流,必须先懂的这几个知识点后端

  • 事件冒泡
  • 事件捕获

先来看一个有趣的问题, 这是 4 代浏览器(IE4)开发团队遇到一个的问题:浏览器

What part of the Webpage owns a specific event?

页面的哪一部分会拥有某个特定的事件?框架

要想明白这个问题能够想象成在一个页面上画了一组同心圆, 当手指放在中间时,它不只在一个圆圈内,并且在全部的圆圈内。

以下图所示:函数

图片描述

这就是浏览器事件的工做原理, 当你点击一个按钮时, 不只单击按钮,还单击包含的容器和整个页面。工具

事件生命周期, 分为三个阶段: capturing (捕获), target (目标), bubbling (冒泡). 而这三个阶段也是构成事件流基本部分. 开发工具

这种处理思想, 在如今流行 NodeJS 后端框架 Koa 对中间件的处理模式也是基于这种, 熟悉Koa或许对这洋葱图并不会陌生:this

图片描述

先来熟悉事件的模型:spa

Javascript Events: Event bubbing (事件冒泡)

事件冒泡: 既事件开始由最具体的元素接收,而后逐级向上传播最后到达 Document 对象 或 window 上.

图片描述

先来看一个简单的示例, 代码以下:

<!DOCTYPE HTML>
    <html>
        <head>
            <title>......</title>
        </head>
        <body>
            <div id="demo"> Press here.</div>
        </body>
    </html>
var target = document.getElementById("demo");
    window.addEventListener("click", function(){
        console.log("window bubbling");
    });
    document.addEventListener("click", function(){
        console.log("document bubbling");
    });
    document.documentElement.addEventListener("click", function(){
        console.log("html bubbling");
    });
    document.body.addEventListener("click", function(){
        console.log("body bubbling");
    });
    target.addEventListener("click", function(){
        console.log("target bubbling");
    });

控制台打印输出以下

"target bubbling"
    "body bubbling"
    "html bubbling"
    "document bubbling"
    "window bubbling"

当一个div 被点击, 这点击事件发生的顺序以下:

  • div
  • body
  • html
  • Document
  • Window (如今浏览器中, IE9+, Firfox, Chrome 等)

从执行顺序来讲, click事件首先在 div 元素上触发, 而后沿着DOM Tree 向上传播, 在路径上的每一个节点上触发,直到它到达文档对象(或Window对象)。

Javascript Events: Event Capturing (捕获)

图片描述

事件捕获 是另一种事件流模型, 最早由 Netscape Browser 引入.

根据上面的的模型, 恰好与前面冒泡相反. 根据该模型,最不特定(最外层)的节点首先接收事件,而最特定(目标元素)的节点最后接收事件.

它设计的目标就是在事件到达目标以前,事先进行拦截.

参考前面的示例, 修改下监听方式, 代码修改以下:

var target = document.getElementById("demo");
    window.addEventListener("click", function(){
        console.log("window Capturing");
    }, true);
    document.addEventListener("click", function(){
        console.log("document Capturing");
    }, true);
    document.documentElement.addEventListener("click", function(){
        console.log("html Capturing");
    }, true);
    document.body.addEventListener("click", function(){
        console.log("body Capturing");
    }, true);
    target.addEventListener("click", function(){
        console.log("target Capturing");
    }, true);

打印的结果:

"window bubbling"
    "document bubbling"
    "html bubbling"
    "body bubbling"
    "target bubbling"

当点击 div 元素, 按照以下顺序来进行广播事件.

  • Window (如今浏览器中, IE9+, Firfox, Chrome 等)
  • Document
  • Document
  • body
  • div

注意:

DOM0 级中默认就是使用冒泡的方式, 不支持捕获的. 因此在 DOM2 级中经过 addEventListener 提供的第三个参数来控制使用哪一种事件流来处理.

Javascript Events: DOM Event Flow (事件流)

根据上面两种模型, 能够总结完整事件流应该向以下:

图片描述

DOM Level 2 Events 指定的事件流模型分为三个阶段:

  • Event Capturing Phase (事件捕获阶段)
  • At the target (目标阶段)
  • Event Bubbling Phase (事件冒泡阶段)

从上面流程图中, 首先发生的是 事件捕获阶段 为截获事件提供了机会, 再到目标阶段 而后进入 事件冒泡阶段, 能够在这个阶段对事件进行响应.

引用前面的例子, 点击 DIV 元素时, 事件将按照上图顺序进行触发.

这就是完整的事件流, 内容看起来挺多的, 实际上一句话就概述事件流.

用来描述事件发生顺序(页面接收事件顺序).

事件处理方式

经过前面的知识点, 事件就是表示用户或浏览器自身执行某种动做. 例如: click, dbclick, load, unload, mouseover,mouseout, mouseenter ,mouseleave 等等, 这些都是事件的名字. 而响应并处理某个事件的函数称为 事件处理程序(或事件监听器(观察者模式)).

常见事件处理方式包括以下几种:

  • HTML 事件处理程序
  • DOM0 级处理程序
  • DOM2 级处理程序

HTML 事件处理程序

直接来看个简单示例:

<input type="button" onclick="alert('<Clicked')"/>

这种模式, 能够理解为 CSS 行内样式

<div style="color: #ccc;"></div>

直接在 HTML 元素上绑定相应事件名, 并指定对应的事件处理程序. 从前面语法来讲, 事件处理程序是一个函数, 上面传递是一个语句, 也就是说默认执行时, JS引擎会进行相应处理. 等价于这种方式:

<input type="button" onclick="(function(){alert('Clicked')})()"/>

若是把事件处理函数直接以这种内联值的方式提供, 应该注意不能值中指定未经转义的HTML语法字符 例如: 和号(&) 、双引号("") 等等, 不然会解析出错.

如上面示例,若是想使用双引号, 必须这么处理.

<input type="button" onclick="alert(&quot;Clicked&quot;)"/>

这种对于简单语句还行, 除了提供元素属性值的方式, 那么有木有其它方式咧? 答案:固然有 , HTML事件处理程序能够调用在页面其它地方定义的脚本.

示例以下:

<input type="button" onclick="handleClick()"/>
    <script>
        function handleClick(){
            alert("Clicked");
        }
    </script>

经过这种方式指定事件处理程序具备一些特别的地方.

  • 前面讲述事件处理程序引擎在执行时, 默认封装函数. 在封装的函数中包含一个局部变量 event, 也就是事件对象.
<input type="button" onclick="alert(event)"/>

    // => 等价

    <input type="button" onclick="(function(){var e = event; alert(e);})()"/>
  • 该函内部 this 指针表示是当前的目标对象
<input type="button" onclick="alert(this)"/>

    // this === [object HTMLInputElement] === input元素

这样能够经过 this来获取目标元素相关的内容了. 好比取值

<input type="button" onclick="alert(this.value)"/>
    <!--或简写成这样-->
    <input type="button" value="Click Me" onclick="alert(value)"/>

为何能够简写方式, input 对象是存在于当前函数的做用域链中(scope). 为了调试方式, 把上面方式做以下改变

<input type="button" value="Click Me" onclick="(function aa(){debugger;console.log(value)})()"/>

其实理解上面那句话, 咱们能够借助开发这工具来协助理解:

图片描述

  • 从执行栈(Call Stack)能够看出, 证实前面说的默认会建立封装函数 .
  • 从 Scope 做用链中能够看出, 前面说的 input 对象会被添加在当前匿名函数执行的做用域链上.

根据开发工具来看, 咱们是能够模拟引擎帮作的事情, 伪代码以下:

function () {
        with(document) {
            with(input){
                // dosomething
                console.log(value);    
            }
        }
    }

本质经过 with 来扩展做用域.

上面把基本内容说, 那么这种经过HTML事件处理程序有什么问题么 ?

  • 时差问题

    事件处理程序必须优先元素加载, 有可能DOM加载出来, 用户就开始操做, 此时事件处理程序可能未加载致使报错

  • 前面说起扩展程序的做用域链可能在不一样浏览器中兼容不同,致使程序出现错误
  • 视图和行为偶合在一块儿,也就是说事件处理程序修改相应JS部分、HTML部分都须要去修改.

引用前面的示例:

<input type="button" onclick="handleClick()"/>
    <script>
        function handleClick(){
            alert("Clicked");
        }
    </script>

若是用户想把函数名为: "onClick", 这时是否是须要同时去修改.

未完, 后续...

相关文章
相关标签/搜索