JavaScript DOM 事件模型

JavaScript 是基于面向对象和事件驱动的一门语言,事件模型是 DOM 中至关重要的内容,理解事件驱动机制、事件反馈、事件冒泡、事件捕获以及事件委托能帮助我们更好的处理事件,写出更优的代码

事件驱动机制

  1. 当事件发生时,我们收到事件的反馈,在 JavaScript 中,事件反馈是我们自行定义的事件处理函数
  2. 事件,如点击事件、鼠标移入事件等,是每一个元素与生俱来的能力
  3. 通常说的绑定事件,实际上是绑定事件的反馈,即事件处理函数
  4. 例如点击一个按钮,按钮元素对象是事件发送器或事件源,事件是鼠标点击事件,事件处理函数是侦听器
  5. 元素对象发出事件,事件处理函数做出反应,这就是 JS 的事件驱动机制

在观察者模式中,事件发送器就是主题,事件处理函数即侦听器就是观察者

绑定事件反馈

  1. 内联属性

    <button onclick="test()">按钮</button>

    介于结构和逻辑要相分离,不建议使用内联方式绑定

  2. 事件句柄

    var oBtn = document.getElementsByTagName('button')[0];
    oBtn.onclick = function() {
    // this -> oBtn
    }

    兼容性好,但是重复绑定会覆盖

  3. 事件监听器

    var oBtn = document.getElementsByTagName('button')[0];
    oBtn.addEventListener("click", funtion(){
    // this -> oBtn
    }, false);
    oBtn.addEventListener("click", test, false);
    funtion test(){
    // 事件处理函数
    }

    重复添加,不会覆盖之前添加的监听器,但是如果事件类型、事件处理函数和最后一个布尔参数都相同,则不会重复执行

    IE8 及以下不支持 addEventListener,可用 attachEvent 代替

    var oBtn = document.getElementsByTagName('button')[0];
    oBtn.attachEvent("onclick", funtion(){
    // this -> window
    });
    // 区别于 addEventListener,第一个参数使用 'onclick',而不是 'click'
    // 并且内部 this 指向 window
    // 对于 attachEvent,如果事件类型、事件处理函数都相同,还是会重复执行

    兼容性封装

    function addEvent(elem, type, fn) {
    if (elem.addEventListener) {
    elem.addEventListener(type, fn, false);
    } else if (elem.attachEvent) {
    elem.attachEvent('on' + type, function(ev) {
    fn.call(elem, ev); // call 兼容性比 bind 好
    });
    } else {
    elem['on' + type] = fn;
    }
    }
  4. 解除绑定

    oBtn.onclik = null;
    oBtn.removeEventListener("click", test, false); // 解除 addEventListener
    oBtn.detachEvent('onclick', test); // 解除 attachEvent

    示例:点击一次后清除事件反馈

    oBtn.onclik = function() {
    // ...
    this.onclick = null;
    }
    // 非严格模式
    oBtn.addEventListener("click", funtion() {
    // ...
    this.removEventListener('cilck', arguments.callee, false);
    }, false);
    // 严格模式
    oBtn.addEventListener("click", funtion temp() {
    // ...
    this.removeEventListener('click', temp, false);
    }, false);

事件冒泡和捕获

  1. 事件冒泡:当一个元素发生事件时,该事件会向父级元素传递,按由子到父的顺序触发一连串的事件反馈,称之为事件冒泡

    DOM 上的嵌套关系会产生事件冒泡,例如两个 div 嵌套,点击内部的 div,触发内部 div 的点击事件,内部 div 的点击事件处理函数进行响应,这个事件向其父级即外部 div 传递,外部 div 也有点击事件,外部 div 所绑定的点击事件反馈也会响应

    <div class="outer">
    <div class="inner"></div>
    </div>
    var outer = document.getElementsByClassName('outer')[0],
    inner = outer.getElementsByClassName('inner')[0];
    outer.addEventListener('click', function () {
    console.log('bubble outer');
    }, false);
    inner.addEventListener('click', function () {
    console.log('bubble inner');
    }, false);
    // addEventListener 最后一个参数默认值为 false,表示事件冒泡
    // 点击 inner,打印出
    // bubble inner
    // bubble outer
  2. 事件捕获:当一个元素发生事件时,该事件会向父级元素传递,按由父到子的顺序触发一连串的事件反馈,称之为事件捕获

    事件捕获与事件冒泡的触发顺序相反,同样需要 DOM 上的嵌套关系

    outer.addEventListener('click', function () {
    console.log('outer');
    }, true);
    inner.addEventListener('click', function () {
    console.log('inner');
    }, true);
    // addEventListener 最后一个参数使用 true,表示事件捕获
    // 点击 inner,打印出
    // outer
    // in
  3. 捕获和冒泡的执行顺序

    outer.addEventListener('click', function () {
    console.log('bubble outer');
    }, false); // 冒泡
    inner.addEventListener('click', function () {
    console.log('bubble inner');
    }, false); // 冒泡
    outer.addEventListener('click', function () {
    console.log('outer');
    }, true); // 捕获
    inner.addEventListener('click', function () {
    console.log('inner');
    }, true); // 捕获
    // 点击 inner,打印出
    // outer
    // bubble inner
    // inner
    // bubble outer

    点击一个元素,元素即事件源,若事件源绑定了事件处理函数,且设定了事件捕获,则先执行捕获,捕获执行完毕后,按照绑定顺序执行该事件源绑定的事件,如果设定了事件冒泡,再执行冒泡

  4. focus blur change submit reset select 事件没有冒泡和捕获,IE 浏览器没有事件捕获

阻止事件冒泡

  1. 阻止冒泡的方法

    Event 的原型上有 stopPropagation 方法,可以阻止冒泡,是 w3c 的规范

    Event 的原型上有 cancleBubble 属性,赋值为 true,可以阻止冒泡

  2. addEventListener 绑定事件处理函数,拿到事件对象

    var outer = document.getElementsByClassName('outer')[0],
    inner = outer.getElementsByClassName('inner')[0];
    inner.addEventListener('click', function (ev) {
    console.log(ev); // 事件对象 ev
    ev.stopPropagation(); // 阻止事件冒泡
    }, false);
  3. IE 浏览器没有 stopPropagation 方法,可以使用 cancelBubble 属性

    注意:IE 浏览器中事件对象存放在 window.event 中。IE8 不支持 addEventListener 方法

    // 封装阻止冒泡的方法
    function cancelBubble(ev) {
    if (ev.stopPropagation) {
    ev.stopPropagation();
    } else ev.cancelBubble = true; // 兼容 IE8 及以下
    }
    // 使用上文中封装好的 addEvent 方法
    function addEvent(elem, type, fn) {
    if (elem.addEventListener) {
    elem.addEventListener(type, fn);
    } else if (elem.attachEvent) {
    elem.attachEvent('on' + type, function (ev) {
    fn.call(elem, ev);
    });
    } else {
    elem['on' + type] = fn;
    }
    }
    // 绑定事件处理函数
    var outer = document.getElementsByClassName('outer')[0],
    inner = outer.getElementsByClassName('inner')[0];
    addEvent(inner, 'click', function (ev) {
    var ev = ev || window.event; // IE 兼容性写法
    cancelBubble(ev); // 阻止冒泡
    });

阻止默认事件

  1. 三种方法

    • 事件对象 preventDefault() 方法,兼容 IE9 及以上
    • 事件对象 returnValue = false,兼容 IE8 及以下
    • 事件处理函数 return false
  2. 兼容性写法

    function preventDefaultEvent(ev) {
    if (ev.preventDefault) {
    ev.preventDefault();
    } else ev.returnValue = false; // 兼容 IE8 及以下
    }
  3. 右键菜单事件

    document.oncontextmenu = function (ev) {
    var ev = ev || window.event;
    // 1. ev.preventDefault(); // IE9 及以上
    // 2. ev.returnValue = false; // IE8 及以下
    // 3. return false;
    }
  4. a 标签跳转事件

    href 使用伪协议

    <a href="javascript:void(0);">a 标签</a>
    <a href="javascript:;">a 标签</a>
    <a href="#">a 标签</a> <!--跳转到当前页面顶部-->

    onclick 事件 return false

    <a href="http://www.baidu.com" onclick="return false">a 标签</a>
    <a href="http://www.baidu.com" onclick="return test(),false">a 标签</a>
    <!--第二个是利用了 “,” 分隔符会返回最后一个的特点,与 test 方法无关-->

    绑定事件处理函数

    <!--内联绑定-->
    <a id='taga' href="http://www.baidu.com" onclick="return test()">a 标签</a>
    <!--句柄绑定-->
    <script>
    document.getElementById('taga').onclick = test;
    function test(ev) {
    var ev = ev || window.event;
    // 1. ev.preventDefault(); // IE9 及以上
    // 2. ev.returnValue = false; // IE8 及以下
    // 3. return false;
    }
    // 前两种方式在使用内联属性绑定时,不需要在属性上加 return,第三种则需要
    </script>

    表单的 action 属性支持 javascript: 伪协议,onsubmit 或者提交按钮点击事件都可以绑定处理函数,阻止提交的方法和阻止 a 标签跳转的方法类似

冒泡捕获流

  1. 事件流:描述从页面中接收事件的顺序

  2. 事件冒泡流:微软 IE 提出,Event Bubbling

  3. 事件捕获流:网景 Netscape 提出,Event Capturing

  4. 事件流三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段

    元素触发事件时,首先事件捕获阶段,由父到子的执行事件处理函数,然后处于目标阶段,该元素的事件处理函数按绑定顺序执行,最后事件冒泡阶段,由子到父的执行事件处理函数

事件和事件源

  1. 事件即事件对象,可以由事件处理函数的参数拿到

    IE8 及以下中事件对象存放在 window.event 中

    // btn 按钮元素
    btn.onclick = function(ev) {
    var ev = ev || window.event; // IE8 兼容性写法
    }
  2. 事件源即事件源对象,是发生事件的元素,即事件发送器,可以从事件对象中获取

    IE8 及以下只有 srcElement,firefox 低版本只有 target,chrome 两者都有

    // btn 按钮元素
    btn.onclick = function(ev) {
    var ev = ev || window.event; // IE8 兼容性写法
    var tar = ev.target || ev.srcElement; // 获取事件源的兼容性写法
    }

事件委托

  1. 事件委托也叫事件代理,指对父级元素绑定事件处理函数,通过获取事件源来处理子元素

  2. 示例:点击按钮使列表 ul 增加 li 元素,点击每个 li 元素打印出其中的内容(innerHTML)

    如果不使用事件委托,需要循环对每个 li 进行绑定,点击按钮添加新的 li 元素后也要进行绑定,效率低下

    使用事件委托,直接对 ul 绑定点击事件处理函数,获取事件对象、事件源对象,再对源对象进行处理

    <body>
    <button>btn</button>
    <ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    </ul>
    <script>
    var oBtn = document.getElementsByTagName('button')[0],
    oList = document.getElementsByTagName('ul')[0],
    oLi = oList.getElementsByTagName('li');
    oBtn.onclick = function () {
    var li = document.createElement('li');
    li.innerText = oLi.length + 1;
    oList.appendChild(li);
    }
    oList.onclick = function (ev) {
    var ev = ev || window.event,
    tar = ev.target || ev.srcElement;
    // tar 即为被点击的 li 元素
    console.log(tar.innerHTML);
    // 返回在所有兄弟元素中的索引,借用数组 indexOf 方法
    console.log(Array.prototype.indexOf.call(oLi, tar));
    }
    </script>
    </body>

JavaScript DOM 事件模型的更多相关文章

  1. JavaScript DOM事件模型

    早期由于浏览器厂商对于浏览器市场的争夺,各家浏览器厂商对同一功能的JavaScript的实现都不进相同,本节内容介绍JavaScript的DOM事件模型及事件处理程序的分类. 1.DOM事件模型.DO ...

  2. 走进javascript——DOM事件

    DOM事件模型 在0级DOM事件模型中,它只是简单的执行你为它绑定的事件,比如你为某个元素添加了一个onclick事件,当事件触发时,它只是去调用我们绑定的那个方法,不再做其他的操作. 在2级DOM事 ...

  3. javascript中0级DOM和2级DOM事件模型浅析

    Javascript程序使用的是事件驱动的设计模式,为一个元素添加事件监听函数,当这个元素的相应事件被触发那么其添加的事件监听函数就被调用: <input type="button&q ...

  4. javascript中0级DOM和2级DOM事件模型浅析 分类: C1_HTML/JS/JQUERY 2014-08-06 15:22 253人阅读 评论(0) 收藏

    Javascript程序使用的是事件驱动的设计模式,为一个元素添加事件监听函数,当这个元素的相应事件被触发那么其添加的事件监听函数就被调用: <input type="button&q ...

  5. DOM事件模型浅析

    1.何为DOM DOM是"Document Object Model"的缩写,中文译为"文档对象模型".它是一种跨平台.跨语言的编程接口,将HTML,XHTML ...

  6. W3C DOM 事件模型(简述)

    1.事件模型 由于事件捕获与冒泡模型都有其长处和解释,DOM标准支持捕获型与冒泡型,能够说是它们两者的结合体.它能够在一个DOM元素上绑定多个事件处理器,而且在处理函数内部,thiskeyword仍然 ...

  7. JavaScript Dom 事件

    JavaScript  Dom 事件 对于事件需要注意的要点: // this标签当前正在操作的标签. this // event封装了当前事件的内容. even 常用事件 // 鼠标单击.触发事件 ...

  8. DOM事件: DOM事件级别、DOM事件流、DOM事件模型、DOM事件捕获过程、自定义事件

    前端面试中只要问到事件,就肯定会有DOM事件:如果回答出来了,就会一直向下延申,其实这些东西都很简单,但我第一次被问到的时候,也是懵的: DOM事件级别: DOM0 element.onclick = ...

  9. javascript之事件模型

    事件模型 冒泡型事件(Bubbling):事件由叶子节点沿祖先节点一直向上传递到根节点 捕获型事件(Capturing):由DOM树最顶元素一直到最精确的元素,与冒泡型事件相反 DOM标准事件模型:D ...

随机推荐

  1. 一篇文章带您读懂List集合(源码分析)

    今天要分享的Java集合是List,主要是针对它的常见实现类ArrayList进行讲解 内容目录 什么是List核心方法源码剖析1.文档注释2.构造方法3.add()3.remove()如何提升Arr ...

  2. sys.argv的意义[转]

    sys.argv的意义 原文地址:https://www.cnblogs.com/zzliu/p/10775049.html 简单来说,sys.argv是一个参数列表,这个列表存放着从外界获取到的参数 ...

  3. 一位资深程序员大牛推荐的Java技术学习路线图

    Web应用,最常见的研发语言是Java和PHP. 后端服务,最常见的研发语言是Java和C/C++. 大数据,最常见的研发语言是Java和Python. 可以说,Java是现阶段中国互联网公司中,覆盖 ...

  4. Java框架之SpringBoot-Web构建-yml-模块-注解

    SpringBoot Spring Boot是一站式整合所有应用框架的框架,简化Spring应用开发,约定大于配置,去繁从简,开箱即用,准生产环境的运行时应用监控框架 快速构建 SpringBoot ...

  5. 全网最详细的一篇Flutter 尺寸限制类容器总结

    Flutter中尺寸限制类容器组件包括ConstrainedBox.UnconstrainedBox.SizedBox.AspectRatio.FractionallySizedBox.Limited ...

  6. PySide2的This application failed to start because no Qt platform plugin could be initialized解决方式

    解决PySide2的This application failed to start because no Qt platform plugin could be initialized问题 今天在装 ...

  7. 报错: raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__version__)

    Django2.0同步Mysql数据库时出现的问题 执行 python manage.py makemigrations 报错 # 报错位置 File "G:\python\lib\site ...

  8. 后台管理遇到的坑一、style中css样式怎么传入变量值

    第一.给标签定义style变量 第二.在data中定义 第三.在methods中的方法中给样式赋值

  9. java中的对象 方法 引用 等一些抽象的概念是什么意思呢?

    2020-03-14 最近这一段时间有点忙,好久都没有更新博客了,之后我会一直坚持下去的,和大家一同进步的. 这段时间一直在学java,相信刚开始学习java的小白,刚开始接触那么些抽象的概念一定和我 ...

  10. Notepad++远程连接Linux

    为方便编辑Linux上的文件,我们可以用Notepad++的NppFTP插件 工具:Notepad++.CentOS 1.通过ifconfig命令找到主机ip 2.打开Notepad++插件NppFT ...