javascript事件代理


在编程中,如果我们不想或不能够直接操纵目标对象,我们可以利用delegate创建一个代理对象来调用目标对象的方法,从而达到操纵目标对象的目的。毋庸置疑,代理对象要拥有目标对象的引用。我们来看一下javascript的一个最简单实现:

vardelegate =function(client,clientMethod ){
  returnfunction() {returnclientMethod.apply(client,arguments); }
}
varagentMethod = delegate(client, client.method )
agentMethod();
<!doctype html>
<html dir="ltr" lang="zh-CN">
  <head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    <script type="text/javascript">
      var ClassA = function(){
        var _color = "red";
        return {
          getColor : function(){
            document.write("<p>ClassA的实例的私有属性_color目前是<span style='color:"+_color+"' >"+_color+"</span></p>");
          },
          setColor:function(color){
            _color = color;
          }
        };
      };
      var delegate = function (client,clientMethod ){
        return function() { return clientMethod.apply(client,arguments); }
      }
      window.onload = function(){
        var a = new  ClassA();
        a.getColor();
        a.setColor("green");
        a.getColor();
        //alert(a._color);
        document.write("<p>执行代理!</p>")
        var d = delegate(a,a.setColor);
        d("blue");
        document.write("<p>执行完毕!</p>")
        a.getColor();
      };
    </script>
    <title>delegate</title>
  </head>
  <body>
  </body>
</html>



或者我们改变一下第二个参数,传个字符串进去:

      vardelegate =function(client,clientMethod ){
        returnfunction() {returnclient[clientMethod].apply(client,arguments); }
      }
/****************略*******************/
     vard = delegate(a,"setColor");

我们还可以搞一些很好玩的东西,下面的例子取自一个日本博客

<script type="text/javascript">
     Function.prototype.delegate = function(delegateFor){
    return delegateFor.apply(null, arguments);
};

var Hoge = function(){
    this.open = function (){
        return 'this is Hoge#open';
    };
    this.close = function (){
        return 'this is Hoge#close';
    };
};
var Foo = function (){
    this.name = 'foo';
    this.open = function (){
        return 'this is Foo#open';
    };
    this.close = function (){
        return 'this is Foo#close';
    };
};

var hoge = new Hoge;
var foo = new Foo;

alert(hoge.open()); // this is Hoge#open
alert(hoge.open.delegate(foo.open)); // this is Foo#open
alert(foo.open.delegate(hoge.open)); // this is Hoge#open
</script>

由于delegate实现目标对象的隐藏,这对于我们保护一些核心对象是非常有用的。不过,说实在javascript的delegate基本算是call与apply的傀儡,因为js中只有这2个方法提供了改变当前函数内部this作用域的功能。不过,要实现对类的委托而不是实例的委托,这就复杂得多。Prototype.js,YUI与JQuery都有相应的实现。具体自己可去参考它们的源码。接着下来,我将讲述delegate在事件上的应用,这可是个无以伦比的东东,就是改变侦听器的位置(改变事件的绑定对象)。或者说,它得益于javascript独特的事件传播机制,因此实现起来非常容易,大家要好好运行它,我以前也在富文本编辑器中用过。

$.addEvent(colorPicker,'click',function(){
  vare = arguments[0] || window.event,
  td = e.srcElement ? e.srcElement : e.target,
  nn = td.nodeName.toLowerCase();
  if(nn =='td'){
    varcmd = colorPicker.getAttribute("title");
    varval = td.bgColor;
    _format(cmd,val);
    e.cancelBubble =true;
    colorPicker.style.display ='none';
  }
});

上面就是我在富文本编辑器撷取前景色与背景色的代码,注意,我是把事件绑定在颜色面板上,而不是面板上的那一个个格子上。为了直观起见,便于大家操作,我改用下面这个例子:

<ulid="nav">
      <li><ahref="http://www.cnblogs.com/">博客园</a></li>
      <li><ahref="http://www.w3school.com.cn/">W3School</a></li>
      <li><ahref="http://study.163.com/">网易云课堂</a></li>
      <li><ahref="http://www.willspace.cn/">WILLSpace</a></li>
      <li><ahref="http://blog.csdn.net/willspace/">CSDN_WILLSpace</a></li>
</ul>

现在我们要点击列表中链接,取出里面的内容,传统的方法,我们需要遍历添加所有a元素:

window.onload =function(){
   varnav = document.getElementById("nav");
   varlinks = nav.getElementsByTagName("a");
   for(vari=0,l = links.length; i<l; i++) {
     links[i].onclick =function() {
       alert(this.innerHTML);
       returnfalse;
     }
   }
 }
<!doctype html>
<html dir="ltr" lang="zh-CN">
  <head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    <style type="text/css">
      body{background:#fff;}
      a { color:#8080C0;text-decoration:none;border-bottom:2px solid #fff;}
      a:hover {color:#336699;border-bottom-color:#B45B3E;}
    </style>
    <script type="text/javascript">
      window.onload = function(){
        var nav = document.getElementById("nav");
        var links = nav.getElementsByTagName("a");
        for (var i=0,l = links.length; i <l; i++) {
          links[i].onclick = function () {
            alert(this.innerHTML);
            return false; 
          }
        }
      }    
    </script>
    <title>delegate</title>
  </head>
  <body>
    <ul id="nav">
      <li><a href="http://www.cnblogs.com/">博客园</a></li>
      <li><a href="http://www.w3school.com.cn/">W3School</a></li>
      <li><a href="http://study.163.com/">网易云课堂</a></li>
      <li><a href="http://www.willspace.cn/">WILLSpace</a></li>
      <li><a href="http://blog.csdn.net/willspace/">CSDN_WILLSpace</a></li>
    </ul>
  </body>
</html>


新的方法,用nav代理了它下面所有元素,让它负责大家的onclick事件,包括它自己的,也不管是否为a元素,所以有时我们需要做一些判断,但少了遍历DOM树,效率明显提高。

window.onload =function(){
  varnav = document.getElementById("nav");
  nav.onclick =function() {
    vare = arguments[0] || window.event,
    target = e.srcElement ? e.srcElement : e.target;
    alert(target.innerHTML);
    returnfalse;
  }
}
 
<!doctype html>
<html dir="ltr" lang="zh-CN">
  <head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    <style type="text/css">
      body{background:#fff;}
      a { color:#8080C0;text-decoration:none;border-bottom:2px solid #fff;}
      a:hover {color:#336699;border-bottom-color:#B45B3E;}
    </style>
    <script type="text/javascript">
      window.onload = function(){
        var nav = document.getElementById("nav");
        nav.onclick = function () {
          var e = arguments[0] || window.event,
          target = e.srcElement ? e.srcElement : e.target;
          alert(target.innerHTML);
          return false;
        }
      }
    </script>
    <title>delegate</title>
  </head>
  <body>
    <ul id="nav">
      <li><a href="http://www.cnblogs.com/">博客园</a></li>
      <li><a href="http://www.w3school.com.cn/">W3School</a></li>
      <li><a href="http://study.163.com/">网易云课堂</a></li>
      <li><a href="http://www.willspace.cn/">WILLSpace</a></li>
      <li><a href="http://blog.csdn.net/willspace/">CSDN_WILLSpace</a></li>
    </ul>
  </body>
</html>

为什么它会行得通呢?!因为DOM2.0的事件模型是这样的,如果某个元素触发一个事件,如onclick,顶层对象document就会发出一个事件流,随着DOM树往目标元素流去,这就是传说中的捕获阶段,也就是原Netscape的事件执行模式,沿途的元素如果绑定了事件,它是不会执行的!第二阶段,就是到达了目标元素,执行它上面的绑定事件,但如果onclick只是个空实现,当然是没有效果啦!第三阶级,就是起泡阶级,原IE的事件执行模式,从目标元素往顶层元素折回,如果沿途有onclick事件,就随个触发!因此我们是点击了a元素,但它的onclick事件为空,当事件流上浮到ul元素时,发现ul元素绑定了onclick事件,就执行当中的函数。如果ul的祖先元素也绑定了onclick事件呢?!继续执行!有多少执行多少!看下面的例子:

 
<!doctype html>
<html dir="ltr" lang="zh-CN">
  <head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    <style type="text/css">
      body{background:#fff;}
      a { color:#8080C0;text-decoration:none;border-bottom:2px solid #fff;}
      a:hover {color:#336699;border-bottom-color:#B45B3E;}
    </style>
    <script type="text/javascript">
      var addEvent = (function () {
        if (document.addEventListener) {
          return function (el, type, fn) {
            el.addEventListener(type, fn, false);
          };
        } else {
          return function (el, type, fn) {
            el.attachEvent('on' + type, function () {
              return fn.call(el, window.event);
            });
          }
        }
      })();

      window.onload = function(){
        var nav = document.getElementById("nav");
        nav.onclick = function () {
          var e = arguments[0] || window.event,
          target = e.srcElement ? e.srcElement : e.target;
          alert(target.innerHTML);
          return false;
        }
        var wrapper = document.getElementById("wrapper");
        addEvent(wrapper,'click',function(){
          alert("冒泡过程连我也惊动了!");
        });
      }
    </script>
    <title>delegate</title>
  </head>
  <body>
    <div id="wrapper">
      <ul id="nav">
        <li><a href="http://www.cnblogs.com/">博客园</a></li>
        <li><a href="http://www.w3school.com.cn/">W3School</a></li>
        <li><a href="http://study.163.com/">网易云课堂</a></li>
        <li><a href="http://www.willspace.cn/">WILLSpace</a></li>
        <li><a href="http://blog.csdn.net/willspace/">CSDN_WILLSpace</a></li>
      </ul>
    </div>
  </body>
</html>
   

正由于这个特性,我们就可以利用ul的onclick去代理它下面所有元素的onclick事件。原来,我们需要给这些a元素准备五个侦听器(EventListener),现在我们只需要1个,节省了4个,如果这个列表有一百行呢?就节省了99个!在商务应用,我们经过会遇到许多报表(grid,实质是用table做的),我们需要为每行添加悬浮变色效果与点击编辑功能,这就用到onmouseover 、 onmouseout 与 onclick事件,如果这个报表有五千行,我们也只需要三个侦听器,节省了14997个!如果用传统方法做这个grid,IE6这样垃圾的游览器不卡到你吐血。因此,善用event delegation会大大提高我们程序的性能。

附上一些有用的链接:

文章转自:
http://www.cnblogs.com/rubylouvre/archive/2009/08/09/1542174.html
感谢作者
posted @ 2015-07-25 15:06  zwwill  阅读(159)  评论(0编辑  收藏  举报