2014-02-25 30 views
12

考慮此示例代碼:爲什麼在調用event.preventDefault()之後用JavaScript切換複選框/單選按鈕失敗?

<span> 
    <input type="checkbox"> 
</span> 

$('span').click(function(e) { 
    e.preventDefault(); 
    $(':checkbox')[0].checked = true; 
}); 

Fiddle

從我所知,這應該發生:

  • preventDefault()應防止複選框從由被檢查瀏覽器的默認b ehavior,即使事件處理程序在上面的DOM層次結構中。這部分工作正常。
  • 設置.checked = true應該作爲,我相信它應該獨立於我已取消的事件的瀏覽器的默認操作。這部分似乎是越野車,好像preventDefault()正在影響它 - 刪除preventDefault(),它按預期工作。

複選框保持未選中狀態的原因是什麼?

我在Chrome 33和Firefox 27上測試過,所以這似乎並不是一個瀏覽器錯誤。

這個問題主要是由於好奇擴展我的DOM /事件模型知識。我不想要解決方法,但我想知道爲什麼這個例子失敗。

+0

單擊文本時,請參見[Fiddle](http://jsfiddle.net/R2Awe/10/)e.preventDefault()不與默認行爲接口 – Satpal

+0

@Satpal click事件被綁定到跨度而不是孩子,那麼究竟是怎麼回事。 – Jai

+0

我真的很困惑什麼發生,好問題! –

回答

4

鑑於你的HTML:

<span> 
    <input type="checkbox"> 
</span> 

和你的代碼:

$('span').click(function(e) { 
    e.preventDefault(); 
    $(':checkbox')[0].checked = true; 
}); 

事件冒泡是你的問題在這裏。

單擊checkbox時,單擊事件將傳播到spane.preventDefault()是從checkbox傳播的事件對象上調用的。

因此preventDefault()是針對checkbox本身執行的,從而阻止它被檢查。

其中checkbox的點擊事件被preventDefault「取消」,它將保持取消狀態,直到事件流完成爲止。 (請參見有關詳細信息,回答的底部)

如果應用一些樣式到您注意到點擊周圍的checkbox你的代碼工作正常,但點擊checkbox自身複製您的問題,由於上面提到的跨度。


DEMO - 原始小提琴+風格。點擊span是好的,checkbox


按照MDN documentation on preventDefault()

在事件流的任何階段調用preventDefault取消 事件,這意味着任何默認操作通常採取 實施由於事件不會發生。

DOM Level 2 Spec還指出:

如果事件可取消,則preventDefault方法用於 表示該事件被取消,這意味着通常由該實現採取的任何默認操作 事件的結果將不會發生 。 如果在事件流的任何階段中,調用preventDefault方法 ,則事件將被取消

DOM Level 3 Events Spec的筆記實施例5下:

click事件上<input type="checkbox">元素相關聯的默認操作切換的checked IDL屬性該元素的值。如果click事件的默認操作被取消,則該值將恢復到其以前的狀態。

+0

這樣''click'事件處理' span'永遠不會被調用。 – kamilkp

+0

「因此,preventDefault()是針對複選框本身執行的,以防止它被檢查。」 - 是的,那是我的預期行爲。這個問題不是針對輸入應用preventDefault,問題是爲什麼設置了checked狀態的代碼行在'preventDefault()'之後無法按預期工作。 –

+0

@FabrícioMatté:'設置檢查狀態的代碼行不起作用「,它不起作用,因爲跨度捕獲的第一個事件是來自複選框的冒泡點擊事件,並且它被告知不做它的默認行爲。在您的代碼行執行時,事件鏈仍處於活動狀態,並且防止默認行爲的指令也是如此。 – Nope

1

你將不得不preventDefaultclickmouseup並設置mouseup

嘗試檢查:

$('span').click(function(e) { 
    e.preventDefault(); 
}).mouseup(function(e){ 
    e.preventDefault(); 
    $(':checkbox')[0].checked = true; 
}); 
+0

雖然有其他幾個更好的解決方法.....問題是**爲什麼複選框保持始終未選中狀態的原因是什麼?**並且您還沒有回答。 – Jai

+0

您正在調用'preventDefault'。當你點擊輸入時,點擊事件會使DOM膨脹起來。而在'span's'事件監聽器中,它已經發生在'input'上。複選框會被檢查,然後取消選中,所以你甚至不會看到它。 – kamilkp

+0

@kamilkp爲什麼沒有選中? –

1

這是我期望的行爲,因爲當您說event.preventDefault()時,您指示瀏覽器在此事件中不對其執行任何操作。因此,即使在您明確檢查複選框後的聲明之後,瀏覽器也不會對其執行任何操作。 我們可以看到其中的差別,當我改變事件如下:

$('span').click(function(e) { 
    e.preventDefault(); 
    //$(':checkbox')[0].checked = true; 
    setTimeout(function(){$(':checkbox')[0].checked = true;}); 
}); 

使用超時,我們正在改變事件外的checked屬性,因此它得到遏制。

3

setTimeout結束語部分代碼是卓有成效的,因爲你可以在這裏看到http://jsfiddle.net/y7C77/

$('span').click(function(e) { 
    e.preventDefault(); 
    setTimeout(function() { 
     $('input').prop('checked', true); 
    }, 1); 
}); 

preventDefault()取消默認的動作,我認爲這是可以取消它,即使你手動做什麼瀏覽器會做(在這種情況下:更改checked屬性)。

+0

'setTimeout'的工作原理是內部函數在事件流完全完成後執行,該事件流在傳播的checkbox事件對象上調用preventDefault。很好的工作:) – Nope

+0

我在想同樣的事情,但是,這是一個理論問題。 +1,但是如果有人在將來需要它,那麼這個方法很好。 –

相關問題