前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Web开发的基本功

Web开发的基本功

作者头像
ThoughtWorks
发布2018-04-16 10:44:56
1.4K0
发布2018-04-16 10:44:56
举报
文章被收录于专栏:ThoughtWorks

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

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

首先简单回顾下DOM的事件处理过程:
DOM0级:
dom_event_stream
dom_event_stream
  • 在图(1)所示的capture phase中, 事件向下冒泡抵达目标的父元素。
  • 在图(2)所示的TargetPhase中,事件抵达元素。
  • 在图(3)所示的bubbling phase中,事件冒泡到顶。
同时回顾浏览器事件处理流程:
代码语言:javascript
复制
var btn = document.getElementByID("myBtn");
btn.onclick = function(){
  console.log("this.id"); //myBtn
}

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

DOM0的事件流为冒泡类型。

DOM2级:
代码语言:javascript
复制
var btn = document.getElementByID("myBtn");
btn.addEventListener("click", function(){
  console.log("click");
},false);

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

代码语言:javascript
复制
btn.removeEventListener("click", function(){
  console.log("click");
},false);

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

代码语言:javascript
复制
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()方法进行事件绑定与解绑,绑定事件会被添加到冒泡阶段。

代码语言:javascript
复制
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级事件中新增的方法,在取消进一步冒泡与捕获的同时,阻止任何事件处理程序被调用。
现有如下代码:
代码语言:javascript
复制
<!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:

代码语言:javascript
复制
$(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上的一个问题

参考资料:
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2014-03-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 思特沃克 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 浏览器默认行为执行与阻止分析
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档