首先说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>