浅析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 相关文章推荐
js 字符串转化成数字的代码
Jun 29 Javascript
Fastest way to build an HTML string(拼装html字符串的最快方法)
Aug 20 Javascript
深入浅析JavaScript面向对象和原型函数
Feb 06 Javascript
jquery对象和DOM对象的任意相互转换
Feb 21 Javascript
Javascript实现代码折叠功能
Aug 25 Javascript
各种选择框jQuery的选中方法(实例讲解)
Jun 27 jQuery
JS实现unicode和UTF-8之间的互相转换互转
Jul 05 Javascript
使用vue-router为每个路由配置各自的title
Jul 30 Javascript
vue-cli3.0 环境变量与模式配置方法
Nov 08 Javascript
vue实现搜索功能
May 28 Javascript
vue实现跳转接口push 转场动画示例
Nov 01 Javascript
javascript实现图片轮换动作方法
Aug 07 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程序员基本要求和必备技能
2014/05/09 PHP
在WordPress中使用PHP脚本来判断访客来自什么国家
2015/12/10 PHP
PHP读取大文件的多种方法介绍
2016/04/04 PHP
strpos() 函数判断字符串中是否包含某字符串的方法
2019/01/16 PHP
PhpStorm配置Xdebug调试的方法步骤
2019/02/02 PHP
Laravel jwt 多表(多用户端)验证隔离的实现
2019/12/18 PHP
输入框的字数时时统计—关于 onpropertychange 和 oninput 使用
2011/10/21 Javascript
前端开发过程中浏览器版本的两种判定方法
2013/10/30 Javascript
javascript运行机制之this详细介绍
2014/02/07 Javascript
tuzhu_req.js 实现仿百度图片首页效果
2015/08/11 Javascript
基于JavaScript实现网页倒计时自动跳转代码
2015/12/28 Javascript
浅谈angularJS中的事件
2016/07/12 Javascript
Bootstrap显示与隐藏简单实现代码
2017/03/06 Javascript
jQuery插件Echarts实现的渐变色柱状图
2017/03/23 jQuery
ES6中Array.copyWithin()函数的用法实例详解
2017/09/16 Javascript
JavaScript实现计算圆周率到小数点后100位的方法示例
2018/05/08 Javascript
微信小程序车牌号码模拟键盘输入功能的实现代码
2018/11/11 Javascript
微信小程序自定义导航教程(兼容各种手机)
2018/12/12 Javascript
jquery获取img的src值实例介绍
2019/01/16 jQuery
详解express使用vue-router的history踩坑
2019/06/05 Javascript
Vue 中可以定义组件模版的几种方式
2019/08/06 Javascript
Vue中通过属性绑定为元素绑定style行内样式的实例代码
2020/04/30 Javascript
Vue+ElementUI 中级联选择器Bug问题的解决
2020/07/31 Javascript
Vue两种组件类型:递归组件和动态组件的用法
2020/08/06 Javascript
Javascript表单序列化原理及实现代码详解
2020/10/30 Javascript
python通过索引遍历列表的方法
2015/05/04 Python
python中yaml配置文件模块的使用详解
2018/04/27 Python
Python实现合并两个有序链表的方法示例
2019/01/31 Python
Python语言检测模块langid和langdetect的使用实例
2019/02/19 Python
对Python生成器、装饰器、递归的使用详解
2019/07/19 Python
pytorch1.0中torch.nn.Conv2d用法详解
2020/01/10 Python
Python class的继承方法代码实例
2020/02/14 Python
css3绘制百度的小度熊
2018/10/29 HTML / CSS
HTML5添加禁止缩放功能
2017/11/03 HTML / CSS
学校门卫工作职责
2013/12/07 职场文书
见义勇为事迹材料
2014/12/24 职场文书