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. LeetCode 题解 | 237. 删除链表中的节点

    题目描述: 请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点. 现有一个链表 -- head = [4,5,1,9],它可以表示为: 示例 1: 输入: hea ...

  2. 软件工程实验一 Git代码版本管理

    实验一  GIT 代码版本管理 一.实验目的: 1)了解分布式分布式版本控制系统的核心机理: 2)熟练掌握git的基本指令和分支管理指令: 二.实验内容: 1)安装git: 2)初始配置git ,gi ...

  3. 使用Vagrant部署虚拟分布式开发和测试环境

    同步更新到笔者个人博客,可以访问我的博客查看原文:https://www.rockysky.tech 创建自动化配置开发环境 最近由于最近研究和学习的关系,需要经常配置和搭建多个虚拟机组成的分布式系统 ...

  4. 配置VSCode的C/C++语言功能

    0. 前言 主要是在网上找的方法都没试成功过,在各种机缘巧合下终于成功了. 这篇文章基于个人经验,而且没有走寻常路. 1. 需要的软件和插件 软件: VSCode (https://code.visu ...

  5. p标签内不能嵌套块级标签

    今天突然发现一个问题,那就是p标签内不能嵌套块级标签 例如: <p><p></p></p> 会被浏览器解析成 我又把 div 嵌套在里面,发现还是这样 ...

  6. 2020年春招面试必备Spring系列面试题129道(附答案解析)

    前言 关于Spring的知识总结了个思维导图分享给大家   1.不同版本的 Spring Framework 有哪些主要功能?   2.什么是 Spring Framework? Spring 是一个 ...

  7. 使用 Hexo 创建项目文档网站

    当我们发布一个开源项目的时候,最重要的事情之一就是要创建项目文档.对使用项目的用户来说,文档是非常有必要的,通常我们可以使用下面这些方式来创建文档: GitHub Wiki:在 Github 上我们可 ...

  8. 用ABAP 生成二维码 QR Code

    除了使用我的这篇blogStep by step to create QRCode in ABAP Webdynpro提到的使用ABAP webdynpro生成二维码之外,也可以通过使用二维码在线生成 ...

  9. Element-UI饿了么时间组件控件按月份周日期,开始时间结束时间范围限制参数

    在日常开发中,我们会遇到一些情况,在使用Element-UI 限制用户的日期时间范围的选择控制(例如:查询消息开始和结束时间,需要限制不能选择今天之后的时间). 看了网上的一些文档,零零散散.各式各样 ...

  10. mysql的锁与事务

    1. MySQL中的事物 1.InnoDB事务原理 1. 事务(Transaction)是数据库区别于文件系统的重要特性之一,事务会把数据库从一种一致性状态转换为另一种一致性状态. 2. 在数据库提交 ...