jQuery事件處理: 別再亂用「return false」了

可能在你剛開始學習關於jQuery事件處理時,看到的第一個例子就是關於如何阻止瀏覽器執行默認行爲,好比下面這段演示click事件的代碼:javascript

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

這個函數使用toggle來顯示或者隱藏#mydiv,而後阻止瀏覽器繼續訪問href中指定的連接。java

像上面這樣的例子會讓用戶養成使用「return false」來阻止瀏覽器執行默認行爲的壞習慣,在這篇文章裏,我將會討論關於阻止瀏覽器執行默認行爲的兩個很是重要的主題:jquery

  • 選擇正確的方法: return false仍是preventDefault,stopPropagation或者stopImmediatePropagation
  • 選擇合適的位置,開始,結束,仍是中間某個地方:你應該在事件回調的哪一個部分取消瀏覽器執行默認行爲?

注意:當我在這篇文章中提到event bubbling(事件冒泡),我想表達的是大部分事件都是先在初始DOM上觸發,而後再經過DOM樹往上,在每一級父元素上觸發,事件不會在兄弟節點或是子節點上冒泡(當事件向下冒泡時,咱們叫它事件捕捉(event capturing)),你能夠在這裏瞭解更多關於事件起泡和捕捉的介紹。web

選擇正確的方法ajax

「return false」之因此被誤用的如此厲害,是由於它看起來像是完成了咱們交給它的工做,瀏覽器不會再將咱們重定向到href中的連接,表單也不會被繼續提交,但這麼作到底有什麼不對呢?瀏覽器

」return false「到底作了什麼?服務器

當你每次調用」return false「的時候,它實際上作了3件事情:app

  • event.preventDefault();
  • event.stopPropagation();
  • 中止回調函數執行並當即返回。

「等等」,你叫了起來!我只是想讓瀏覽器中止繼續執行默認行爲而已,我不須要它去作另外2件事。ide

這3件事中用來阻止瀏覽器繼續執行默認行爲的只有preventDefault,除非你想要中止事件冒泡,不然使用return false會爲你的代碼埋下很大的隱患,讓咱們經過一個真實的例子來看看這樣的誤用會形成什麼後果:函數

這是咱們用來演示的HTML:

  1. <div class="post">  
  2. <h2><a href="http://heikezhi.com/path/to/page">My Page</a></h2>  
  3. <div class="content">  
  4.     Teaser text...  
  5.   </div>  
  6. </div>  
  7. <div class="post">  
  8. <h2><a href="http://heikezhi.com/path/to/other_page">My Other Page</a></h2>  
  9. <div class="content">  
  10.     Teaser text...  
  11.   </div>  
  12. </div>  

如今假設咱們想要在用戶點擊文章標題時,將文章動態載入到div.contentd中:

  1. jQuery(document).ready(function ($) {  
  2.   $("div.post h2 a").click(function () {  
  3.     var a    = $(this),  
  4.     href = a.attr('href'), // Let jQuery normalize `href`,  
  5.     content  = a.parent().next();  
  6.     content.load(href + " #content");  
  7.     return false// "cancel" the default behavior of following the link  
  8.   });  
  9. });  

這段代碼能夠正常工做(至少目前是),但若是咱們順着這個思路繼續,若是我想要在用戶點擊了一個div.post元素(或者任何一個它的子元素)時,給它加上一個active類,我就須要給div.post增長了一個click回調:

  1. // Inside Document Ready:  
  2. var posts = $("div.post");  
  3.   posts.click(function () {  
  4.   // Remove active from all div.post  
  5.   posts.removeClass("active");  
  6.   // Add it back to this one  
  7.   $(this).addClass("active");  
  8. });  

如今,若是咱們點擊一個帖子的標題,這段代碼會工做嗎?答案是不會,由於咱們在標題的click回調裏使用了return false而不是咱們應該使用的,」return false「等於event.preventDefault();加event.stopPropagation();,因此事件冒泡就被終止了,click事件不會被冒泡到div.post上,咱們爲它添加的事件回調固然也就不會被調用了。

若是咱們把它和live或者delegate事件混在一塊兒使用時,狀況就更糟了。

  1. $("a").click(function () {  
  2.   // do something  
  3.   return false;  
  4. });  
  5.   
  6. $("a").live("click"function () {  
  7.   // THIS WON'T FIRE  
  8. });  

那麼咱們真正須要的是什麼呢?

preventDefault()

大多數狀況下,當你使用return false時,你其實真正須要的是e.preventDefault()。要使用e.preventDefault,你須要確保你傳遞了event參數到你的回掉函數中(在這個例子裏,就是那個e):

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

它會替咱們完成全部工做,但不會阻止父節點繼續處理事件,要記住,你放在代碼中的限制越少,你的代碼就越靈活,也就越易於維護。

stopPropagation()

但有些狀況下,你有可能須要中止事件冒泡,讓咱們看看下面的例子:

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

如今,讓咱們假設若是你點了div上除了a連接以外的地方,咱們但願能發生點什麼事情(好比改變下背景什麼的),可是不能影響用戶點擊a連接的行爲(從可用性的角度,這個例子不怎麼好,你可能不但願用戶點擊別的地方時發生任何事情)。

  1. $("div.post").click(function () {  
  2.   // Do the first thing;  
  3. });  
  4.   
  5. $("div.post a").click(function (e) {  
  6.   // Don't cancel the browser's default action  
  7.   // and don't bubble this event!  
  8.   e.stopPropagation();  
  9. });  

在這種狀況下,若是咱們使用return false,div的click事件不會被觸發,可是用戶也不會到達他們點的那個連接。

stopImmediatePropagation()

這個方法會中止一個事件繼續執行,即便當前的對象上還綁定了其它處理函數,全部綁定在一個對象上的事件會按綁定順序執行,看看下面的例子:

  1. $("div a").click(function () {  
  2.   // Do something  
  3. });  
  4.   
  5. $("div a").click(function (e) {  
  6.   // Do something else  
  7.   e.stopImmediatePropagation();  
  8. });  
  9.   
  10. $("div a").click(function () {  
  11.   // THIS NEVER FIRES  
  12. });  
  13.   
  14. $("div").click(function () {  
  15.   // THIS NEVER FIRES  
  16. });  

你可能會以爲這個例子看起來很彆扭,沒錯,儘管如此,但有時這的確會發生,若是你的代碼很是複雜,那麼不一樣的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時的強狂,若是你的回調代碼出錯拋出了異常,讓咱們看看下面的代碼:

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

如今,讓咱們看看一樣的事件,把preventDefault調用放在底部的效果:

  1. >  
  2. var data = {};  
  3. $("a").click(function (e) {  
  4.   // Throws an error because `my` is undefined  
  5.   $("body").append(data.my.link);  
  6.   
  7.   // This line is never reached, and your website  
  8.   // falls back to using the `href` instead of this  
  9.   // "cool" broken JavaScript!  
  10.   e.preventDefault(); // cancel default behavior  
  11. });  

這對錶單提交也一樣有效,你能夠更好的應對出錯的狀況,別期望你的代碼一直正常工做,在發生錯誤時有正確的應對總賽過假設代碼不會出錯。

3. 在產品階段,若是功能這設計JS,那就還應該放在第一行。

記住,沒必要非得是函數的第一行,可是越早越好,這裏的原則是:若是函數的功能是經過JS實現的(不涉及服務端交互),那就不必考慮兼容,在這種狀況下,添加在第一行能夠防止URL中出現#字符,但顯然,你仍是應該儘量多的增長些錯誤處理代碼,以防止用戶在出錯時變得不知所措。

結論

我但願這篇文章傳達的信息足夠你在須要阻止瀏覽器執行默認行爲時作出正確的選擇。記住,只有當你真的明白你在作什麼時,才使用」return false「,並確保你是在函數的正確位置調用了相應的代碼。最後,儘量保持代碼的靈活性,儘可能不要再用「return false」了!

相關文章
相關標籤/搜索