JavaScript与HTML交互系列--事件流(冒泡、捕获、应用)和事件处理程序

栏目: Html · 发布时间: 5年前

内容简介:当浏览器发展到第四代时(IE4及Netscape Communicator4),浏览器开发团队遇到了一个很有意思的问题:页面的哪一部分会拥有某个特定的事件?要明白这个问题问的是什么,可以想象画在一张纸上的一组同心圆。如果你把手指放在圆心上,那么你的手指指向的不是一个圆,而是纸上的所有圆。两家公司的浏览器开发团队在看待浏览器事件方面还是一致的。如果你单击了某个按钮,他们都认为单击事件不仅仅发生在按钮上。换句话说,在单击按钮的同时,你也单击了按钮的容器元素,甚至也单击了整个页面。事件流描述的是从页面中接收事件的

当浏览器发展到第四代时(IE4及Netscape Communicator4),浏览器开发团队遇到了一个很有意思的问题:页面的哪一部分会拥有某个特定的事件?要明白这个问题问的是什么,可以想象画在一张纸上的一组同心圆。如果你把手指放在圆心上,那么你的手指指向的不是一个圆,而是纸上的所有圆。两家公司的浏览器开发团队在看待浏览器事件方面还是一致的。如果你单击了某个按钮,他们都认为单击事件不仅仅发生在按钮上。换句话说,在单击按钮的同时,你也单击了按钮的容器元素,甚至也单击了整个页面。

事件流描述的是从页面中接收事件的顺序。但有意思的是,IE和Netscape开发团队居然提出了差不多是完全相反的事件流的概念。IE的事件流是事件冒泡流,而Netscape Communicator的事件流是事件捕获流。

思考:点击页面元素,什么样的元素能感应到这样一个事件?

点击页面元素的同时,也点击了元素的容器元素甚至整个页面。

事件冒泡

IE的事件流叫做 事件冒泡 (event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。

事件捕获

Netscape Communicator 团队提出的另一种事件流叫做 事件捕获 (event capturing)。其思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于事件到达预订目标之前捕获它。

DOM 事件流

“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,( 一般情况下,程序 )可以在冒泡阶段对事件作出响应。

JavaScript与HTML交互系列--事件流(冒泡、捕获、应用)和事件处理程序

事实上,DOM事件流在捕获阶段和冒泡阶段均可触发事件处理程序。捕获阶段先收到事件流

JavaScript与HTML交互系列--事件流(冒泡、捕获、应用)和事件处理程序

对上面元素添加事件绑定程序。

var outBox = document.getElementById("outBox");
var middleBox = document.getElementById("middleBox");
var innerBox = document.getElementById("innerBox");

outBox.addEventListener('click', function() {
    console.log('outBox捕获');
}, true);
middleBox.addEventListener('click', function() {
    console.log('middleBox捕获');
}, true);
innerBox.addEventListener('click', function() {
    console.log('innerBox捕获');
}, true);

outBox.addEventListener('click', function() {
    console.log('outBox冒泡');
}, false);
middleBox.addEventListener('click', function() {
    console.log('middleBox冒泡');
}, false);
innerBox.addEventListener('click', function() {
    console.log('innerBox冒泡');
}, false);

// 输出顺序
// 1 outBox捕获
// 2 middleBox捕获
// 3 innerBox捕获
// 4 innerBox冒泡
// 5 middleBox冒泡
// 6 outBox冒泡
复制代码

IE9、Opera、Firefox、Chrome和Safari( 也就是说当下主流浏览器 )都支持DOM事件流;IE8及更早版本不支持DOM事件流。

思考一下:事件冒泡和事件捕获在日常开发中有哪些应用场景?

我们下面先看一下事件处理程序。

事件处理程序

1. HTML 事件处理程序

为元素添加(事件)属性,值为事件处理程序。

注:事件属性全部为小写。

<div class="outBox" onclick="alert('outBox')">
    <div class="innerBox" onclick="showInnerBox()"></div>
</div>

<script>
    function showInnerBox() {
        alert("innerBox");
    }
</script>
复制代码

特点:传统方式,简单,跨浏览器

2. DOM0 级事件处理程序

使用JavaScript指定事件处理程序,首先必须取得一个要操作的对象的引用。

<div class="outBox" id="outBox">
    <div class="innerBox" id="innerBox"></div>
</div>

<script>
    var outBoxElement = document.getElementById('outBox');
    outBoxElement.onclick = function() {
        alert(this.id)
    }
    // 删除事件处理程序
    outBoxElement.onclick = null
</script>
复制代码

特点:可通过 this 访问元素的任何属性和方法(HTML事件处理程序也可以),可以删除事件处理程序

3. DOM2 级事件处理程序

“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作: addEventListener()removeEventLintener() 。所有DOM结点中都包含这两个方法,并且他们都接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值如果是 true ,表示在捕获阶段调用事件处理程序;如果是 false ,表示在冒泡阶段调用事件处理程序。

// 添加事件绑定
outBoxElement.addEventListener('click', showOutBox, false);
// 移除事件绑定
outBoxElement.removeEventListener('click', showOutBox);
// 下面移除事件绑定方式无效,无法移除绑定的匿名函数
outBoxElement.removeEventListener('click', function() {
    alert(this.id);
});
复制代码

4. IE 事件处理程序

IE实现了与DOM中类似的两个方法:attachEvent() 和 detachEvent()。这两个方法接受相同的两个参数:事件名称和事件处理函数。由于IE8及更早版本只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。

var btn = document.getElementById('MyBtn');
btn.attachEvent('onclick', function() {
    console.log('clicked');
});

var handler = function() {
    console.log('clicked');
}
// 第二个参数必须是具名函数,不能是匿名函数
btn.detachEvent('onclick', handler);
复制代码

注:attachEvent() 的第一个参数是'onclick',而非DOM的addEventListener()方法中的'click'。

在IE中使用attachEvent()与使用DOM0级方法的主要区别在于时间处理程序的作用域。 在使用DOM0级方法的情况下,事件处理程序会在其所属元素的作用域内运行;在使用attachEvent()方法的情况下事件处理程序会在全局作用域中运行,因此this等于window

btn.attachEvent('onclick', function() {
    console.log(this === window); // true
})
复制代码

此外,attachEvent() 与addEventListener() 类似均可以为一个元素添加多个事件处理程序。

// 为同一个元素添加多个事件处理程序
btn.addEventListener('click', function() {
    console.log('clicked');
}, false);
btn.addEventListener('click', function() {
    console.log('hello world');
}, false);

btn.attachEvent('onclick', function() {
    console.log('clicked');
});
btn.attachEvent('onclick', function() {
    console.log('hello world');
});
复制代码

5. 跨浏览器事件处理程序

跨浏览器事件处理程序即一套可以同时运行在IE以及非IE浏览器的程序,重点是浏览器能力检测也就是浏览器兼容性检查。

var EventUtil = {
    // 添加事件绑定(元素,事件类型,事件处理函数)
    addHandler: function(element, type, handler) {
        if(element.addEventListenter) {
            // DOM2级绑定事件
            element.addEventListenter(type, handler, false);
        }else if(element.attachEvent) {
            // IE绑定事件
            element.attachEvent('on' + type, handler)
        }else {
            // DOM0级绑定事件,兼容IE8及更早版本
            element['on' + type] = handler;
        }
    },
    // 删除事件事件绑定(元素,事件类型,事件处理函数)
    removeHandler: function() {
        if(element.addEventListenter) {
            // DOM2级删除绑定事件
            element.removeEventListenter(type, handler, false);
        }else if(element.attachEvent) {
            // IE删除绑定事件
            element.detachEvent('on' + type, handler)
        }else {
            // DOM0级删除绑定事件,兼容IE8及更早版本
            element['on' + type] = null;
        }
    }
}

var btn = document.getElementById('MyBtn');
var handler = function() {
    console.log('clicked');
}
// 添加绑定事件
EventUtil.addHandler(btn, 'click', handler);
复制代码

事件冒泡应用-事件委托/代理

对“事件处理程序过多”问题的解决方案就是 事件委托 。事件委托利用了 事件冒泡 ,只指定一个事件处理程序,就可以管理一类型的所有事件。例如,click事件会一直冒泡到document层次。也就是说我们可以为整过页面(一般是某几个元素的父元素)指定一个onclick事件处理程序,而不必给每个可单击的元素分别添加事件处理程序。

<ul id="items">
    <li id="item1">item1</li>
    <li id="item2">item2</li>
    <li id="item3">item3</li>
</ul>
复制代码
var itemList = document.getElementById('items');
EventUtil.addHandler(itemList, 'click', function(e) {
    var id = e.target.id;
    switch(id) {
        case "item1":
            alert('item1 is clicked!');
            break;
        case "item2":
            alert('item2 is clicked!');
            break;
        case "item3":
            alert('item3 is clicked!');
            break;
        case "items":
            alert('items is clicked!');
            break;
    }
});
复制代码

以上所述就是小编给大家介绍的《JavaScript与HTML交互系列--事件流(冒泡、捕获、应用)和事件处理程序》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

React开发实战

React开发实战

[美] Cássio de Sousa Antonio / 杜伟、柴晓伟、涂曙光 / 清华大学出版社 / 2017-3-1 / 58.00 元

介绍如何成功构建日益复杂的前端应用程序与接口,深入分析 React库,并详述React生态系统中的其他工具与库,从而指导你创建完整的复杂应用程序。 你将全面学习React的用法以及React生态系统中的其他工具和库(如React Router和Flux 架构),并了解采用组合方式创建接口的佳实践。本书简明扼要地讲解每个主题,并呈现助你高效完成工作的细节。书中严谨深刻地讲述React中重要的功......一起来看看 《React开发实战》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具