浅析return false的正确使用


Posted in Javascript onNovember 04, 2013

可能在你刚开始学习关于jQuery事件处理时,看到的第一个例子就是关于如何阻止浏览器执行默认行为,比如下面这段演示click事件的代码:

$("a.toggle").click(function () {  
    $("#mydiv").toggle();  
    return false; // Prevent browser from visiting `#`  
});

这个函数使用toggle来显示或者隐藏#mydiv,然后阻止浏览器继续访问href中指定的链接。

像上面这样的例子会让用户养成使用“return false”来阻止浏览器执行默认行为的坏习惯,在这篇文章里,我将会讨论关于阻止浏览器执行默认行为的两个非常重要的主题:

•选择正确的方法: return false还是preventDefault,stopPropagation或者stopImmediatePropagation
•选择合适的位置,开始,结束,还是中间某个地方:你应该在事件回调的哪个部分取消浏览器执行默认行为?

注意:当我在这篇文章中提到event bubbling(事件冒泡),我想表达的是大部分事件都是先在初始DOM上触发,然后再通过DOM树往上,在每一级父元素上触发,事件不会在兄弟节点或是子节点上冒泡(当事件向下冒泡时,我们叫它事件捕捉(event capturing)),你可以在这里了解更多关于事件起泡和捕捉的介绍。

选择正确的方法

“return false”之所以被误用的如此厉害,是因为它看起来像是完成了我们交给它的工作,浏览器不会再将我们重定向到href中的链接,表单也不会被继续提交,但这么做到底有什么不对呢?

”return false“到底做了什么?

当你每次调用”return false“的时候,它实际上做了3件事情:

•event.preventDefault();
•event.stopPropagation();
•停止回调函数执行并立即返回。
“等等”,你叫了起来!我只是想让浏览器停止继续执行默认行为而已,我不需要它去做另外2件事。

这3件事中用来阻止浏览器继续执行默认行为的只有preventDefault,除非你想要停止事件冒泡,否则使用return false会为你的代码埋下很大的隐患,让我们通过一个真实的例子来看看这样的误用会造成什么后果:

这是我们用来演示的HTML:

<div class="post">  
<h2><a href="http://3water.com">My Page</a></h2>  
<div class="content">  
    Teaser text...  
  </div>  
</div>  
<div class="post">  
<h2><a href="http://3water.com">My Other Page</a></h2>  
<div class="content">  
    Teaser text...  
  </div>  
</div> 

现在假设我们想要在用户点击文章标题时,将文章动态载入到div.contentd中:
jQuery(document).ready(function ($) {  
  $("div.post h2 a").click(function () {  
    var a    = $(this),  
    href = a.attr('href'), // Let jQuery normalize `href`,  
    content  = a.parent().next();  
    content.load(href + " #content");  
    return false; // "cancel" the default behavior of following the link  
  });  
}); 

这段代码可以正常工作(至少目前是),但如果我们顺着这个思路继续,如果我想要在用户点击了一个div.post元素(或者任何一个它的子元素)时,给它加上一个active类,我就需要给div.post增加了一个click回调:
// Inside Document Ready:  
var posts = $("div.post");  
  posts.click(function () {  
  // Remove active from all div.post  
  posts.removeClass("active");  
  // Add it back to this one  
  $(this).addClass("active");  
}); 

现在,如果我们点击一个帖子的标题,这段代码会工作吗?答案是不会,因为我们在标题的click回调里使用了return false而不是我们应该使用的,”return false“等于event.preventDefault();加event.stopPropagation();,所以事件冒泡就被终止了,click事件不会被冒泡到div.post上,我们为它添加的事件回调当然也就不会被调用了。

如果我们把它和live或者delegate事件混在一起使用时,情况就更糟了。

$("a").click(function () {  
  // do something  
  return false;  
});  $("a").live("click", function () {  
  // THIS WON'T FIRE  
}); 

那么我们真正需要的是什么呢?

preventDefault()

大多数情况下,当你使用return false时,你其实真正需要的是e.preventDefault()。要使用e.preventDefault,你需要确保你传递了event参数到你的回掉函数中(在这个例子里,就是那个e):

$("a").click(function (e) {  
  // e == our event data  
  e.preventDefault();  
}); 

它会替我们完成所有工作,但不会阻止父节点继续处理事件,要记住,你放在代码中的限制越少,你的代码就越灵活,也就越易于维护。

stopPropagation()

但有些情况下,你有可能需要停止事件冒泡,让我们看看下面的例子:

<div class="post">  
    Normal text and then a <a href="http://3water.com">link</a> and then more text.  
</div> 

现在,让我们假设如果你点了div上除了a链接之外的地方,我们希望能发生点什么事情(比如改变下背景什么的),但是不能影响用户点击a链接的行为(从可用性的角度,这个例子不怎么好,你可能不希望用户点击别的地方时发生任何事情)。
$("div.post").click(function () {  
  // Do the first thing;  
});  $("div.post a").click(function (e) {  
  // Don't cancel the browser's default action  
  // and don't bubble this event!  
  e.stopPropagation();  
}); 

在这种情况下,如果我们使用return false,div的click事件不会被触发,但是用户也不会到达他们点的那个链接。

stopImmediatePropagation()

这个方法会停止一个事件继续执行,即使当前的对象上还绑定了其它处理函数,所有绑定在一个对象上的事件会按绑定顺序执行,看看下面的例子:

$("div a").click(function () {  
  // Do something  
});  $("div a").click(function (e) {  
  // Do something else  
  e.stopImmediatePropagation();  
});  
$("div a").click(function () {  
  // THIS NEVER FIRES  
});  
$("div").click(function () {  
  // THIS NEVER FIRES  
}); 

你可能会觉得这个例子看起来很别扭,没错,尽管如此,但有时这的确会发生,如果你的代码非常复杂,那么不同的widgets和plugin就有可能在同一个对象上添加事件,如果遇到这种情况,那你就很有必要理解和使用stopImmediatePropagation。

return false

只有当你同时需要preventDefault和stopPropagation,并且你的代码可以接受直到你的回调执行完成才停止执行浏览器的默认行为,那你就可以使用”return false“。但我强烈建议你别在写给其它jQuery开发者的演示代码中使用这个方法,因为这会造成更多误用,只有在你确信非用不可的情况下再去使用”return false“。

选择适当的位置

如果你使用了”return false“,它只会在你的回调函数执行结束才去取消浏览器的默认行为,但是使用e.preventDefault,我们有更多的选择,它可以随时停止浏览器执行默认动作,而不管你将它放在函数的哪个部分。

1. 开发阶段,你应该总是将它放在第一行。你最不想做的事情可能就是你正在调试将一个form改成ajax提交的时候,它却已经被按照老方法提交了。

2. 产品阶段,如果你采用了渐进增强(progressive enhancement),那就把它放到回调的结束位置,或者是逻辑终点,如果在一个普通页面采用渐进增强,那你就需要在服务器端考虑如果浏览器不支持JS时(或者被禁用时),对链接的click事件和表单的提交事件的处理。这里的好处是,我们不考虑关闭JS的情况,只考虑支持js时的强狂,如果你的回调代码出错抛出了异常,让我们看看下面的代码:

var data = {};  
$("a").click(function (e) {  
  e.preventDefault(); // cancel default behavior  
  // Throws an error because `my` is undefined  
  $("body").append(data.my.link);  
  // The original link doesn't work AND the "cool"  
  // JavaScript has broken. The user is left with NOTHING!  
}); 

现在,让我们看看同样的事件,把preventDefault调用放在底部的效果:
>  
var data = {};  
$("a").click(function (e) {  
  // Throws an error because `my` is undefined  
  $("body").append(data.my.link);    // This line is never reached, and your website  
  // falls back to using the `href` instead of this  
  // "cool" broken JavaScript!  
  e.preventDefault(); // cancel default behavior  
}); 

这对表单提交也同样有效,你可以更好的应对出错的情况,别指望你的代码一直正常工作,在发生错误时有正确的应对总胜过假设代码不会出错。

3.在产品阶段,如果功能这设计JS,那就还应该放在第一行。

记住,不必非得是函数的第一行,但是越早越好,这里的原则是:如果函数的功能是通过JS实现的(不涉及服务端交互),那就没必要考虑兼容,在这种情况下,添加在第一行可以防止URL中出现#字符,但显然,你还是应该尽可能多的增加些错误处理代码,以防止用户在出错时变得不知所措。

结论

我希望这篇文章传达的信息足够你在需要阻止浏览器执行默认行为时做出正确的选择。记住,只有当你真的明白你在做什么时,才使用”return false“,并确保你是在函数的正确位置调用了相应的代码。最后,尽可能保持代码的灵活性,尽量不要再用“return false”了!

Javascript 相关文章推荐
JSON 入门指南 想了解json的朋友可以看下
Aug 26 Javascript
js输出列表实现代码
Sep 12 Javascript
jQuery学习笔记之jQuery的动画
Dec 22 Javascript
jQuery-Tools-overlay 使用介绍
Jul 14 Javascript
JQuery给网页更换皮肤的方法
May 30 Javascript
微信公众号-获取用户信息(网页授权获取)实现步骤
Oct 21 Javascript
node.js到底要不要加分号浅析
Jul 11 Javascript
vue 移动端适配方案详解
Nov 15 Javascript
Angular7创建项目、组件、服务以及服务的使用
Feb 19 Javascript
如何在微信小程序里面退出小程序的方法
Apr 28 Javascript
javscript 数组扁平化的实现
Feb 03 Javascript
Vue js with语句原理及用法解析
Sep 03 Javascript
javascript与jquery中跳出循环的区别总结
Nov 04 #Javascript
js中apply方法的使用详细解析
Nov 04 #Javascript
js数组操作学习总结
Nov 04 #Javascript
Javascript执行效率全面总结
Nov 04 #Javascript
jquery的map与get方法详解
Nov 04 #Javascript
ajax请求get与post的区别总结
Nov 04 #Javascript
jquery text(),val(),html()方法区别总结
Nov 04 #Javascript
You might like
解析php获取字符串的编码格式的方法(函数)
2013/06/21 PHP
php使用指定字符列表生成随机字符串的方法
2015/04/18 PHP
PHP使用Redis实现Session共享的实现示例
2019/05/12 PHP
用Jquery实现多级下拉框无刷新的联动
2010/12/22 Javascript
JS构建页面的DOM节点结构的实现代码
2011/12/09 Javascript
js时间戳格式化成日期格式的多种方法
2013/11/11 Javascript
Json序列化和反序列化方法解析
2013/12/19 Javascript
JavaScript模拟鼠标右键菜单效果
2020/12/08 Javascript
dedecms页面如何获取会员状态的实例代码
2016/03/15 Javascript
jQuery插件echarts实现的多折线图效果示例【附demo源码下载】
2017/03/04 Javascript
jQuery插件FusionCharts绘制的2D条状图效果【附demo源码】
2017/05/13 jQuery
基于AngularJS实现表单验证功能
2017/07/28 Javascript
JS实现图片上传多次上传同一张不生效的处理方法
2018/08/06 Javascript
js实现贪吃蛇游戏(简易版)
2020/09/29 Javascript
Nuxt的动态路由和参数校验操作
2020/11/09 Javascript
vue切换菜单取消未完成接口请求的案例
2020/11/13 Javascript
Python中集合类型(set)学习小结
2015/01/28 Python
Python实现将SQLite中的数据直接输出为CVS的方法示例
2017/07/13 Python
对numpy中轴与维度的理解
2018/04/18 Python
对python实时得到鼠标位置的示例讲解
2018/10/14 Python
python 将json数据提取转化为txt的方法
2018/10/26 Python
python面向对象法实现图书管理系统
2019/04/19 Python
python3 tkinter实现点击一个按钮跳出另一个窗口的方法
2019/06/13 Python
Django 过滤器汇总及自定义过滤器使用详解
2019/07/19 Python
keras使用Sequence类调用大规模数据集进行训练的实现
2020/06/22 Python
Python模拟登录和登录跳转的参考示例
2020/10/30 Python
解决pytorch 模型复制的一些问题
2021/03/03 Python
html5的自定义data-*属性与jquery的data()方法的使用
2014/07/02 HTML / CSS
优衣库澳大利亚官网:UNIQLO澳大利亚
2017/01/18 全球购物
毕业生的自我评价分享
2013/12/18 职场文书
大四自我鉴定
2014/02/08 职场文书
校庆活动策划方案
2014/06/05 职场文书
大学生万能检讨书范例
2014/10/04 职场文书
事业单位岗位说明书
2015/10/08 职场文书
营销策划分析:怎么策划才能更好销量产品?
2019/09/04 职场文书
JavaScript组合继承详解
2021/11/07 Javascript