首先說live與bind的不同,我的理解就是一個監聽事件在冒泡階段,一個監聽事件在執行階段(不知jQuery怎么在捕獲階段監聽事件)。
首先說個使用不當的例子:
兩個頁面,index.html用jQuery的load方法加載data.html,兩個頁面均有js,其中data.html頁面使用了live方法綁定事件。代碼見下:
index.html
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>test_jquery_live</title> <script type="text/javascript" src="jquery-1.6.2.js"></script> <script> $(function(){ $("#load").click(function(){ $("#con").load("data.html"); }); }); </script> </head> <body> <a id="load" href="javascript:void(0);">點我加載啊!</a> <div id="con" style="border:solid 1px;"> </div> </body> </html>
data.html
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>test_jquery_live</title> <script type="text/javascript" src="jquery-1.6.2.js"></script> <script> $(function(){ $("li").live("click",function(){ $("p").html("你點到了:"+$(this).text()); console.log($(this));//這里有妙用 }); }); </script> </head> <body> <ul> <li href="javascript:void(0);">1我是內容點我啊!</li> <li href="javascript:void(0);">2我是內容點我啊!</li> <li href="javascript:void(0);">3我是內容點我啊!</li> </ul> <p style="color:#f00;"></p> </body> </html>
這個頁面運行正常,模擬了index首頁使用Ajax方式加載二級頁面而a標簽相當於欄目列表的超鏈接(可能有多級/多個),點擊一個切換加載一個頁面。而data頁面模擬了實際業務需要使用live的功能,比方說單擊一個li切換p標簽的內容。看起來沒錯,兩個頁面無論是單獨運行還是ajax運行都ok。問題來了,先看“這里有妙用”的那行js,控制台輸出(不明白的請度娘)。如下操作:當用戶在上面多次切換欄目列表的時候(這里演示只有一個),再單擊下面的li標簽,會發現控制台輸出的消息越來越多,原來每切換一個頁面,li的click執行函數就會多執行一次,原因和live的實現方式有關(不明白的也請度娘)。
然后說幫人調頁面的時候遇到的問題。今天遇到的問題就是dtat頁面里有一個ul模擬的下拉菜單,切換過一次的頁面這個下拉菜單里的選項(用li標簽實現)的click事件就無效了,但明明綁定成功了,但就是單擊無效,為此還誤認為是jQ的bug。實際原因是什么呢,先看模擬的下拉菜單,這玩意不是我寫的:
<div> <label>組件類型:</label> <input type="hidden" id="compServType"> <input type="text" id="sComp" name="sComp" class="sel w135"> <ul style="display: none; "> <li style="z-index: 880; "><span class="qy">全部</span><span class="zj" style="display:none;">0</span></li> <li style="z-index: 870; "><span class="qy">標准操作</span><span class="zj" style="display:none;">1</span></li> <li style="z-index: 860; "><span class="qy">自定義操作</span><span class="zj" style="display:none;">2</span></li> <li style="z-index: 850; "><span class="qy">界面元素</span><span class="zj" style="display:none;">3</span></li> <li style="z-index: 840; "><span class="qy">自定義服務</span><span class="zj" style="display:none;">4</span></li> </ul> </div>
通過綁定li的click事件然后將值寫入hidden里面,類似的東西兩個頁面有多個,並且是用class做綁定的,暫且叫A、B頁面,A頁面綁定了.sel的focus和blur事件來顯示列表,B頁面也一樣,當選中后在手動觸發blur事件,代碼里都使用了一個叫li_mousedown的變量來控制ul的顯示和隱藏,打開頁面的順序A-B-A,這個時候在A頁面就無法選中了,再打開B也無法選中了,壓根就沒有執行click的處理函數。廢話好多啊,聽不明白拉到吧。。。
最后直接說解決方法吧:
不使用live,用bind。這個要看實際需求。對於本例適用。
自定義頁面的注銷方法。每次data頁面加載完了再window對象上添加一個gc方法,在方法里寫解除綁定的代碼,然后切換欄目調用load方法前先調用window.gc()來取消綁定。這用法比較麻煩,但適應范圍廣。
還有就是live的時候不給每個頁面的不同內容命名,然后$(".Addtab ul>li").live("click",function(){});這個只能解決我的問題而不能解決頁面上偵聽函數沒有解除綁定的問題(他們竟然選了這個最簡單的方法),實際上重復加載一個頁面還是有性能問題的。
使用delegate代替live也是個不錯的法子,不再document上監聽而是指定監聽的對象$(".Addtab").delegate("".Addtab ul>li"","click",function(){});,此處注意selector是基於:root的,而不是:scope的。至於delegate的bug,還請度娘。
附:
delegate的代碼
<script> $(function(){ $("ul").delegate("ul>li","click",function(){ $("p").html("你點到了:"+$(this).text()+"\n試試再點下邊的我是解決方案吧!"); console.log($(this));//這回控制台只會輸出一次了吧 }); }); </script> <h4>這個頁面解決了這個問題</h4> <ul> <li href="javascript:void(0);">1我是內容點我啊!</li> <li href="javascript:void(0);">2我是內容點我啊!</li> <li href="javascript:void(0);">3我是內容點我啊!</li> </ul> <p style="color:#f00;"></p>