vapour's blog

17Mar/112

label导致冒泡事件的诡异问题

今天遇到一个很怪异的问题,给label标签绑定了click事件,但是每次单击时,click事件都会执行两次。开始以为自己的代码有问题,反复痛苦的检查了N久以后,才发现自己代码没问题。根本原因在于事件的冒泡机制和label标签共同作用引起的。

<ul id="sdfsde" class="multi-list">
    <li><label for="q1"><input id="q1" type="text" value="" />工商</label></li>
    <li><label for="q2"><input id="q2" type="text" value="" />农业</label></li>
    <li><label for="q3"><input id="q3" type="text" value="" />招商</label></li>
    <li><label for="q4"><input id="q4" type="text" value="" />建设</label></li>
    <li><label for="q5"><input id="q5" type="text" value="" />浦发</label></li>
</ul>

执行两次的过程如下:

  1. 单击label标签,第一次执行click事件
  2. 由于label标签的特殊性,事件向下传递到input标签
  3. 事件开始冒泡几上传递
  4. 冒泡到label标签时,第二次执行click事件

为了更便于说明上面的过程,请看下面的示例:

<label id="lbUser">
    <input id="txtUser" type="text" value="单击这里alert二次" />
    单击这里alert三次
</label>
<script type="text/javascript">
$('lbUser').click(function(){
    alert('单击label');
});
$('txtUser').click(function(){
    alert('单击input');
});
</script>

当点击label中的文字时,依次弹出:

  1. alert('单击label')
  2. alert('单击input');
  3. alert('单击label');

当直接点击input输入框时,依次弹出:

  1. alert('单击input');
  2. alert('单击label');

总结:这个问题让自己纠结了,很长时间。主要原因时没有考虑到冒泡,需要注意的这里我们注册的事件都是绑定在冒泡期间,但是因为label(会自动将焦点转到和标签相关的表单控件上),所以导致了这个意外。

Tagged as: , 2 Comments
27May/100

事件监听兼容处理

在事件监听处理方面,IE6-IE8支持attachEvent和detachEvent,而Firefox/Chrome等标准浏览器则支持addEventListener和removeEventListener。并且利用attachEvent方法绑定事件后this指向window,并不是我们期望的被监听元素。我们可以利用prototype的bind函数来解决this指向问题,使用了bind后,就无法再移除事件了。

jRaiser揭秘——事件监听兼容处理中通过增加delegate和getDelegate方法来达到目的,代码如下:

var eventId = 0;
function delegate(elem, eventName, handler) {
   var events = elem.events = elem.events || {}, // 创建事件集合
      id = handler.eventId = handler.eventId || ++eventId; // 生成事件标识
      events[eventName] = events[eventName] || {}; // 创建某种事件的集合

   var trueHandler = function(e) { // 真正被添加的事件处理函数
      handler.call(elem, e);
   };

   events[eventName][id] = trueHandler; // 记下函数的引用
}

function getDelegate(elem, eventName, handler) {
   try { return elem.events[eventName][handler.eventId]; } catch (e) {}
}

function addEvent(elem, eventName, handler) {
   handler = delegate(elem, eventName, handler);
   if (elem.attachEvent) {
      elem.attachEvent(”on” + eventName, handler);
   } else if (elem.addEventListener) {
      elem.addEventListener(eventName, handler, false);
   }
}

function removeEvent(elem, eventName, handler) {
   handler = getDelegate(elem, eventName, handler);
   if (elem.detachEvent) {
      elem.detachEvent(”on” + eventName, handler);
   } else if (elem.removeEventListener) {
      elem.removeEventListener(eventName, handler, false);
   }
}

John Resig给出了另一种解决方法:

function addEvent( obj, type, fn ) {
   if ( obj.attachEvent ) {
      obj['e'+type+fn] = fn;
      obj[type+fn] = function(){obj['e'+type+fn]( window.event );}
      obj.attachEvent( 'on'+type, obj[type+fn] );
   } else
      obj.addEventListener( type, fn, false );
}

function removeEvent( obj, type, fn ) {
   if ( obj.detachEvent ) {
      obj.detachEvent( 'on'+type, obj[type+fn] );
      obj[type+fn] = null;
   } else
      obj.removeEventListener( type, fn, false );
}

John Resig的这种方法比较简单,用到的代码比较少,更多的事件代理方法请参考PPK的addEvent() recoding contest

Tagged as: No Comments