Web开发的基本功

#ThoughtWorkers好声音#第十五期 (图片:网络) 有些东西称为基本功,对于 Web 开发而言,事件处理模型便是其中的一个,我们经常会在代码里遇到阻止浏览器默认行为的做法。 成都办公室的陈致豪花了时间,把事件处理模型整理清楚,做了一次《浏览器默认行为执行与阻止分析》,帮我们更好地理解发生的一切。

浏览器默认行为执行与阻止分析

首先简单回顾下DOM的事件处理过程:

DOM0级:

  • 在图(1)所示的capture phase中, 事件向下冒泡抵达目标的父元素。
  • 在图(2)所示的TargetPhase中,事件抵达元素。
  • 在图(3)所示的bubbling phase中,事件冒泡到顶。

同时回顾浏览器事件处理流程:

var btn = document.getElementByID("myBtn");
btn.onclick = function(){
  console.log("this.id"); //myBtn
}

btn.onclick = null; //解绑事件

DOM0的事件流为冒泡类型。

DOM2级:

var btn = document.getElementByID("myBtn");
btn.addEventListener("click", function(){
  console.log("click");
},false);

unbind中需要注意的一点,如果我们采用:

btn.removeEventListener("click", function(){
  console.log("click");
},false);

这样是无法解绑的。原因很简单,传入匿名函数与bind时的匿名函数并非同一函数。为了正确解绑,需要在绑定时不使用匿名函数:

var handler = function(){console.log("click");};
btn.addEventListener("click", handler, false);
btn.removeEventListener("click",handler,false);

相较DOM0级方法, DOM2级方法的优势为:

  • 可以添加多个事件先后执行。
  • 可以指定事件流类型为冒泡或捕获(第三个boolean参数,为false为冒泡类型)。

需要注意的一点是IE仅从IE9开始支持DOM2级事件处理方式。

IE事件处理程序:

IE(以及Opera)实现了attachEvent()和detachEvent()方法进行事件绑定与解绑,绑定事件会被添加到冒泡阶段。

var handler = function()
  console.log(this === window); //will output "true"
};
btn.attachEvent("onclick",handler); //bind
btn.detachEvent("onclick",handler); //unbind

需要注意的几点:

  • attachEvent的参数为onclick而非dom2中的click。
  • attachEvent的作用域为全局作用域,this == windows, 而DOM0中,this为被绑定元素。
  • attachEvent可以绑定多个事件,与dom2类似。

好了,回到本文主题,关于阻止浏览器默认行为。

相信大家对于event对象的三个方法已经非常熟悉: preventDefault(), stopPropagation(), stopImmediatePropagation()。

  • preventDefault(): 如果event对象的cancelable属性为true,可以取消浏览器的默认行为。
  • stopPropagation():如果event对象的bubbles属性为true,可以取消时间的进一步冒泡或捕获(取决于事件流类型)。
  • stopImmediatePropagation(): 这是dom3级事件中新增的方法,在取消进一步冒泡与捕获的同时,阻止任何事件处理程序被调用。

现有如下代码:

<!DOCTYPE html>
<html>
<head>
    <title>test</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script type="text/javascript" src="js/script.js"></script>

</head>
<body>
<ul id="parent_element">
    <ul>parent</ul>
    <ul>
        <li><a id="google" href="www.google.com">1</a></li>
        <li><a id="douban" href="www.douban.com">2</a></li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
    </ul>

</ul>
</body>
</html>

js/script.js:

$(function () {

    $('#parent_element').click(function (e) {
        e.preventDefault();
        console.log("parent element has been clicked," + e.target);
    })

    $('#parent_element #douban').click(function (e) {
        console.log("#douban is clicked");
        e.stopPropagation();
    })

    $('#parent_element #google').click(function (e) {
        console.log("google is clicked");
    })
})

如上的代码,第一个a是无法进行页面跳转的。第二个a元素可以完成浏览器的默认行为,进行页面跳转。 这里有一个很容易被大家忽视的问题:子节点的浏览器默认行为,被父节点的event.preventDefault() 阻止了。通过对子节点的stopPropagation才可以防止父节点阻止子节点的浏览器默认行为。

But Why?

首先我们需要明确的一点是event对像的生存周期为:

当每一段事件处理程序执行完后,检测事件是否能继续冒泡,如无法继续冒泡,则进行销毁。如果能继续冒泡,则继续传递事件至顶,完成用户绑定事件处理后,进行事件销毁。

而在event对象被销毁之前,会检测event对象是否执行了event.preventDefault()。在JavaScript中这个boolean值为event.defaultPrevented,jQuery中event对象经过包装,此属性被包装为event.isDefaultPrevented()。如果答案为否,没有执行过event.preventDefault(), 那么此event的浏览器默认行为将被执行。

这个解答对应StackOverflow上的一个问题

参考资料:

原文发布于微信公众号 - 思特沃克(ThoughtWorks)

原文发表时间:2014-03-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏hightopo

原 基于 HTML5 WebGL 的 3D

33560
来自专栏拂晓风起

Cocos2d-js 3.0 颜色变换(调整sprite/图片的色调)

15720
来自专栏MasiMaro 的技术博文

windows编程学习笔记(三)ListBox的使用方法

ListBox是Windows中的一种控件,一般被当做子窗口使用,Windows中所有子窗口都是通过发送一个通知码到父窗口父窗口通过WM_COMMAND消息接收...

12020
来自专栏施炯的IoT开发专栏

《101 Windows Phone 7 Apps》读书笔记-BOOK READER

课程内容 Ø编页 ØList Picker Ø拉伸List Box 控件中的条目     本章的Book Reader应用程序为Jane Austen的经典小...

19560
来自专栏HT

基于HTML5 Canvas 点击添加 2D 3D 机柜模型

今天又返回好好地消化了一下我们的数据容器 DataModel,这里给新手做一个典型的数据模型事件处理的例子作为参考。这个例子看起来很简单,实际上结合了数据模型中...

27680
来自专栏用户2442861的专栏

Markdown,你只需要掌握这几个

http://www.cnblogs.com/crazyant007/p/4220066.html

13610
来自专栏前端说吧

JS案例 - 基于vue的移动端长按手势

当时首先想到要做长按事件的时候,我想到的是vue内部的自定义指令,毕竟官网里边有这么一句描述:

22820
来自专栏cnblogs

DOM事件第一弹

     近期温习了部分w3c上关于DOM事件的规范,发现以前有些模糊的概念更加清晰,以及受到罗胖(罗辑思维)的影响,很是想分享自己的了解的东西,希望大家给予指...

20660
来自专栏前端知识分享

Vue---父子组件之间的通信

  在vue组件通信中其中最常见通信方式就是父子组件之中的通信,而父子组件的设定方式在不同情况下又各有不同。最常见的就是父组件为控制组件子组件为视图组件。父组件...

9920
来自专栏Python小屋

Python使用tkinter打造自定义对话框完整代码

问题来源:前一阵发过一个技术文章Python编写抽奖式随机提问程序,其中有个弹出式对话框,好像上海科技大学宋老师在群里当时问了一句对话框中中奖姓名是否能显示的大...

57040

扫码关注云+社区

领取腾讯云代金券