最近開發的時候同事遇見了一個問題,點擊label的時候改變checkbox的屬性,或許大家覺得這是一個很簡單的問題,然而這里面卻蘊藏着一個大坑!
舉例說明:
頁面簡單,就是一個
<input id="input" type="checkbox">
和一個
<label id="label" for="t">點我</label>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div> <input id="input" type="checkbox"> <label id="label" for="input">點我</label> </div> </body>
操作的思路大致是點擊給label添加一個事件,通過判斷checkbox當前的狀況來改變checkbox的狀態,代碼如下:
<script> var inputELe = document.getElementById('input'); //input元素 var labelEle = document.getElementById('label'); //label元素 labelEle.addEventListener('click',function(e){ //給label添加事件
alert('我被點了'); if(inputELe.checked){ //如果當前是選中狀態 inputELe.checked = false //置為false }else{ inputELe.checked = true //否則置為true } }); </script>
乍一看代碼很完美,完全能夠滿足要求,但是實際上呢?看gif圖:
可以看到,無論我怎么點擊,checkbox都沒有變成選中狀態。這是一個很奇怪的現象。要解釋這個現象要知道label和這個input之間的通信問題。
點擊label觸發checkbox狀態改變和直接點擊checkbox觸發改變的過程是不一樣的,
直接點擊checkbox會立即改變checkbox的狀態,如下:
假如現在代碼如下:
inputELe.addEventListener('click',function(e){ //只給input元素添加事件 alert(inputELe.checked); });
可以看到,當input的狀態在視圖上還沒改變的時候,他的checked的值已經發生了改變了,而如果在label上加事件呢?
labelEle.addEventListener('click',function(e){ alert(inputELe.checked); });
這樣則在點擊label的時候,並沒有立刻改變checkedbox的狀態,而是當注冊在label上的事件alert完了之后,才改變的checkbox的狀態。造成這種現象的原因在於:
點擊label的時候,label有限處理自己對應的事件,然后再通知checkbox改變狀態,而且,只會通知checkbox改變狀態,並不告訴他要改變成true還是false,checkbox當前是true就變成false,是false就改成true.
這樣便可以理解一開始的程序為什么沒有改變成功狀態了。
再把代碼拷過來,一步一步看,假設一開始checkbox沒有選中:
labelEle.addEventListener('click',function(e){ //給label添加事件
alert('我被點了'); if(inputELe.checked){ //由於點擊label的時候,還沒有告訴checkbo改變狀態,所以當前值是false,執行else語句 inputELe.checked = false //置為false }else{ inputELe.checked = true //所以這個時候checkbox應該變成了true,狀態改變成功! } });
上面的方法確實改變了checkbox的狀態但是,執行完這個方法后,label還有事要做,那就是通知checkbox改變狀態,由於這時候執行的方法里面已經把checkbox的狀態改成了true,所以當checkbox接到label的消息的時候,又會把自己變成false,
所以改變狀態並沒有成功,一來一回又被變成了原樣了。checkbox一開始為false的時候也一樣。
所以當大家使用label和checkbox(radio也一樣)組合的時候,一定注意:不要把事件加給label,卻在點擊時間里去改變checkbox的狀態!正確的做法是始終給checkbox添加事件並且改變checkbox的狀態。
最后,告訴大家這個組合的使用場景:
就是組合成一個這樣的開關,通過隱藏input並給label添加樣式:after 和 :before 偽元素即可,當然注意操作的時候不要陷入上面的坑里面。