浅析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 相关文章推荐
Extjs 继承Ext.data.Store不起作用原因分析及解决
Apr 15 Javascript
Jquery动态更改一张位图的src与Attr的使用
Jul 31 Javascript
jquery 判断滚动条到达了底部和顶端的方法
Apr 02 Javascript
javascript正则表达式使用replace()替换手机号的方法
Jan 19 Javascript
Node.js操作mysql数据库增删改查
Mar 30 Javascript
jQuery视差滚动效果网页实现方法经验总结
Sep 29 Javascript
jQuery插件echarts去掉垂直网格线用法示例
Mar 03 Javascript
基于React实现表单数据的添加和删除详解
Mar 14 Javascript
Vue2.0如何发布项目实战
Jul 27 Javascript
Vue项目服务器部署之子目录部署方法
May 12 Javascript
express框架下使用session的方法
Jul 31 Javascript
javascript中Set、Map、WeakSet、WeakMap区别
Dec 24 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
制作美丽的拉花
2021/03/03 冲泡冲煮
php中存储用户ID和密码到mysql数据库的方法
2013/02/06 PHP
关于PHP递归算法和应用方法介绍
2013/04/15 PHP
php中spl_autoload详解
2014/10/17 PHP
仿dedecms下拉分页样式修改的thinkphp分页类实例
2014/10/30 PHP
cakephp打印sql语句的方法
2015/02/13 PHP
PHP实现的增强性mhash函数
2015/05/27 PHP
详细解读php的命名空间(一)
2018/02/21 PHP
Alliance vs Liquid BO3 第二场2.13
2021/03/10 DOTA
js 目录列举函数
2008/11/06 Javascript
基于jquery的代码显示区域自动拉长效果
2011/12/07 Javascript
防止浏览器记住用户名及密码的简单实用方法
2013/04/22 Javascript
JQuery之focus函数使用介绍
2013/08/20 Javascript
JavaScript 学习笔记之操作符(续)
2015/01/14 Javascript
比例尺、缩略图、平移缩放之百度地图添加控件方法
2015/08/03 Javascript
jQuery插件开发精品教程(让你的jQuery更上一个台阶)
2015/11/07 Javascript
Vue.js计算属性computed与watch(5)
2016/12/09 Javascript
jQuery实现拼图小游戏(实例讲解)
2017/07/24 jQuery
基于Express框架使用POST传递Form数据
2019/08/10 Javascript
Python 文件和输入输出小结
2013/10/09 Python
python如何修改装饰器中参数
2018/03/20 Python
django框架模型层功能、组成与用法分析
2019/07/30 Python
pytorch绘制并显示loss曲线和acc曲线,LeNet5识别图像准确率
2020/01/02 Python
python数字类型math库原理解析
2020/03/02 Python
pytorch 多分类问题,计算百分比操作
2020/07/09 Python
美国彩妆品牌:Coastal Scents
2017/04/01 全球购物
意大利消费电子产品购物网站:SLG Store
2019/12/26 全球购物
男女钓鱼靴和甲板鞋:XTRATUF
2021/01/09 全球购物
大学生四年生活自我鉴定
2013/11/21 职场文书
校庆活动方案
2014/03/31 职场文书
学习教师敬业奉献模范事迹材料思想汇报
2014/09/19 职场文书
判缓刑人员个人思想汇报
2014/10/10 职场文书
个人思想政治总结
2015/03/05 职场文书
2016年3月份红领巾广播稿
2015/12/21 职场文书
爱岗敬业先进典型事迹材料(2016推荐版)
2016/02/26 职场文书
Pandas 稀疏数据结构的实现
2021/07/25 Python