浅析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 相关文章推荐
javascript中的一些注意事项 更新中
Dec 06 Javascript
基于jQuery替换table中的内容并显示进度条的代码
Aug 02 Javascript
jquery的选择器的使用技巧之如何选择input框
Sep 22 Javascript
一个JavaScript获取元素当前高度的实例
Oct 29 Javascript
推荐5 个常用的JavaScript调试技巧
Jan 08 Javascript
JavaScript编程中的Promise使用大全
Jul 28 Javascript
js转html实体的方法
Sep 27 Javascript
JQuery.validationEngine表单验证插件(推荐)
Dec 10 Javascript
基于jQuery代码实现圆形菜单展开收缩效果
Feb 13 Javascript
javascript中BOM基础知识总结
Feb 14 Javascript
ionic2懒加载配置详解
Sep 01 Javascript
koa2使用ejs和nunjucks作为模板引擎的使用
Nov 27 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 模拟登陆MSN并获得用户信息
2009/05/16 PHP
php 代码优化之经典示例
2011/03/24 PHP
php实现获取文章内容第一张图片的方法
2014/11/04 PHP
PHP发送AT指令实例代码
2016/05/26 PHP
PHP控制前台弹出对话框的实现方法
2016/08/21 PHP
PHP文件操作实例总结【文件上传、下载、分页】
2018/12/08 PHP
载入jQuery库的最佳方法详细说明及实现代码
2012/12/28 Javascript
JS实现将人民币金额转换为大写的示例代码
2014/02/13 Javascript
node.js中的fs.lchmodSync方法使用说明
2014/12/16 Javascript
JavaScript前端图片加载管理器imagepool使用详解
2014/12/29 Javascript
jQuery实现友好的轮播图片特效
2015/01/12 Javascript
js实现禁止中文输入的方法
2015/01/14 Javascript
jquery滚动到顶部底部代码
2015/04/20 Javascript
js实现无缝循环滚动
2020/06/23 Javascript
基于JS实现回到页面顶部的五种写法(从实现到增强)
2016/09/03 Javascript
vue如何实现observer和watcher源码解析
2017/03/09 Javascript
基于JavaScript实现瀑布流布局
2018/08/15 Javascript
Koa代理Http请求的示例代码
2018/10/10 Javascript
vue中选中多个选项并且改变选中的样式的实例代码
2020/09/16 Javascript
Javascript 模拟mvc实现点餐程序案例详解
2020/12/24 Javascript
Vue实现多页签组件
2021/01/14 Vue.js
[52:52]DOTA2上海特级锦标赛C组资格赛#1 OG VS LGD第三局
2016/02/27 DOTA
python获得图片base64编码示例
2014/01/16 Python
Python遍历zip文件输出名称时出现乱码问题的解决方法
2015/04/08 Python
Python 线程池用法简单示例
2019/10/02 Python
Python全局锁中如何合理运用多线程(多进程)
2019/11/06 Python
python、Matlab求定积分的实现
2019/11/20 Python
H&M美国官网:欧洲最大的服饰零售商
2016/09/07 全球购物
vivo智能手机官方商城:vivo
2016/09/22 全球购物
Linux上比较文件的命令都有哪些
2013/09/28 面试题
关于青春的演讲稿
2014/05/05 职场文书
科长个人四风问题整改措施思想汇报
2014/10/13 职场文书
涨价通知怎么写
2015/04/23 职场文书
给朋友的道歉短信
2015/05/12 职场文书
创业计划书之DIY自助厨房
2019/09/06 职场文书
正确使用MySQL update语句
2021/05/26 MySQL