js中的事件机制(上)

很早就想好好总结一下事件机制了,终于有动力写了。文章分为上下2部分
JS和HTML的交互是通过事件来实现的。事件就是文档中发生的一些特定的交互瞬间。

事件流

当浏览器发展到第四代时(IE4 和 netscape Communicator 4),开发团队遇到了一个有意思的问题:页面的哪一部分会拥有某个特定的事件,可以想象在一张纸上画一组同心圆,如果把手指放在圆心上,那么你的手指指向的不是一个圆,而是所有的圆。2家公司看待事件的想法是一致的。当你点击了一个按钮,他们都认为是点击不仅仅发生在按钮上。也就是说 当你点击按钮,你也点击了按钮的容器元素,甚至可以说点击了整个页面。
事件流描述的是,页面接收事件的顺序。IE和Netscape开发团队居然提出了完全相反的事件流的概念。IE的事件流是事件冒泡流,netScape是事件捕获流。

image

事件冒泡

IE的事件流流叫事件冒泡(event bubbling)
所有的现代浏览器都是支持事件冒泡,但是具体实现上有一点区别。
IE5.5及其更早的版本会跳过html直接到document

事件捕获

netscape 团队提出的另一种事件流叫事件捕获。事件捕获的思想是不太具体的节点应该更早的接收到事件,具体的节点应该最后接受到事件。它的目的是在事件到达预定目标之前捕获它。

DOM事件流

IE9、Opera、Firefox、Chrome、Safari都是支持DOM事件流的。IE8及更早的版本不支持

DOM 2级事件规定事件流包括3个阶段,事件捕获阶段、处于目标阶段、事件冒泡阶段。
首先发生的事件捕获,为截取事件提供了机会。然后是实际的目标接受事件,最后一个阶段是冒泡阶段。

DOM事件级别

Dom事件主要分为3个级别,DOM 0 级、DOM2级、DOM3级,但是还有一个HTML事件,直接内嵌在HTML里面的。

HTML事件

<button onclick = "handleClick()">click</button>
<script>
  function handleClick(){
    console.log('do something')
  }
<script>

这种强耦合的的事件方法存在2个弊端,首先是时差问题,用户可能在HTML元素一渲染在页面的时候就点击按钮,但是当时的事件处理程序组可能还没有执行条件,比如这个demo中handleClick是在页面的最底部定义的,如果用户在页面解析handleClick函数之前就点击了按钮,会引发错误。
其次这种强耦合也是不被提倡的。

DOM 0 级事件

<button id = ‘myBtn’>click</button>
<script>
  var btn = document.getElementById('myBtn')
  brn.onclick = function (){
    console.log('do something', this.id)
  }
<script>

DOM 0 级事件是将一个函数赋给一个dom对象的处理函数属性。
可以通过给事件处理属性赋值null来解绑事件。
这个demo中就是讲函数赋给了btn的onclick属性
通过以下的demo可以看出来,DOM 0 级事件的处理程序会在捕获阶段被处理。

<!DOCTYPE HTML>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <body>
        <div id="box" style="height:100px;width:300px;background-color:pink;"></div>
        <button id="reset">还原</button>
        <script>
            //IE8-浏览器返回div body html document
            //其他浏览器返回div body html document window
            reset.onclick = function () {
                history.go();
            }
            box.onclick = function () {
                box.innerHTML += 'div\n';
            }
            document.body.onclick = function () {
                box.innerHTML += 'body\n';
            }
            document.documentElement.onclick = function () {
                box.innerHTML += 'html\n';
            }
            document.onclick = function () {
                box.innerHTML += 'document\n';
            }
            window.onclick = function () {
                box.innerHTML += 'window\n';
            }
        </script
    </body>
</html>

DOM 2 级事件

IE9、Firefix、Safari、Chrome、Opera支持DOM 2 级事件处理程序

Dom 2 级事件定义了2个方法,用于指定和删除事件处理程序:addEventListener 和 removeEventListener。
他们都接受3个参数

  • 需要处理的事件名:click,scroll,focuse // 注意没有 on,不是onclick
  • 事件的处理函数: 可以是函数名字
  • useCapture: true表示在捕获阶段调用处理程序,false表示冒泡阶段调用。默认为false

removeEventListener的事件处理程序函数必须与addEventListener的相同。匿名函数没办法移除

btn.addEventListener("click", handleClick, true)
btn.removeEventListener("click", handleClick, true)

大多数情况下,都是将是将事件处理程序添加到时间流的冒泡阶段,这样可以最大限度的兼容各种浏览器,但是也不排除有些场合需要在到达目标事件之前捕获它。

<!DOCTYPE HTML>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <body>
        <div id="box" style="height:100px;width:300px;background-color:pink;"></div>
        <button id="reset">还原</button>
        <script>
            //IE8-浏览器不支持
            //其他浏览器返回window document html body div
            reset.onclick = function () {
                history.go();
            }
            box.addEventListener('click', function () {
                box.innerHTML += 'div\n'
            }, true)
            document.body.addEventListener('click', function () {
                box.innerHTML += 'body\n';
            }, true);
            document.documentElement.addEventListener('click', function () {
                box.innerHTML += 'html\n';
            }, true);
            document.addEventListener('click', function () {
                box.innerHTML += 'document\n';
            }, true);
            window.addEventListener('click', function () {
                box.innerHTML += 'window\n';
            }, true);
        </script>
    </body>
</html>

DOM 3 级事件

DOM3级事件在DOM2级事件的基础上添加了更多的事件类型,全部类型如下:

  • UI事件,当用户与页面上的元素交互时触发,如:load、scroll
  • 焦点事件,当元素获得或失去焦点时触发,如:blur、focus
  • 鼠标事件,当用户通过鼠标在页面执行操作时触发如:dbclick、mouseup
  • 滚轮事件,当使用鼠标滚轮或类似设备时触发,如:mousewheel
  • 文本事件,当在文档中输入文本时触发,如:textInput
  • 键盘事件,当用户通过键盘在页面上执行操作时触发,如:keydown、keypress
  • 合成事件,当为IME(输入法编辑器)输入字符时触发,如:compositionstart
  • 变动事件,当底层DOM结构发生变化时触发,如:DOMsubtreeModified
    同时DOM3级事件也允许使用者自定义一些事件。

IE事件处理程序

支持IE事件处理程序的浏览器只有IE和Opera

上文说到IE9以后才支持addEventListener和removeEventListener,IE9以前浏览器有类似的方法,attachEvent和detachEvent,但是这2个方法只接受2个参数,事件名称和事件处理程序函数。由于IE8及其之前的版本并不支持DOM事件流,只有事件冒泡,所以没有第三个参数也是意料之中的。
那这个和DOM 0 级事件有什么区别呢?
DOM 0 级事件处理函数中的this指代的是当前DOM元素,attachEvent中this指代的是window

let btn = document.getElementById("myBtn")
btn.attachEventListener("onclick",function(){
  console.log("clicked")
})
btn.attachEventListener("onclick",function(){
  console.log("hello world")
})

顺序是先hello world 再clicked,这是和addEventListener不一样的地方。还有就是"click"&&"onclick"

小结

本文介绍了事件流和几种不同级别的DOM事件,不同浏览器的处理方式需要认真对待。

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

推荐阅读更多精彩内容

  •   JavaScript 与 HTML 之间的交互是通过事件实现的。   事件,就是文档或浏览器窗口中发生的一些特...
    霜天晓阅读 3,559评论 1 11
  • js之事件机制 1、事件初探 1.1 js事件的概述 JavaScript事件:JavaScript是基于事件驱动...
    道无虚阅读 2,437评论 0 2
  • 以下文章为转载,对理解JavaScript中的事件处理机制很有帮助,浅显易懂,特分享于此。 什么是事件? 事件(E...
    jxyjxy阅读 3,080评论 1 10
  • JavaScript 程序采用了异步事件驱动编程模型。在这种程序设计风格下,当文档、浏览器、元素或与之相关的对象发...
    劼哥stone阅读 1,297评论 3 11
  • 半夜给我发来去深圳的感受,你受的苦来的太晚,少年你早该经历,你说陌生的城市没有一个人等你回家吃饭,我想说你的一切感...
    治愈系小妖飞儿阅读 206评论 0 0