js事件代理(委托)

JavaScript事件代理(委托)一般用于以下情况:

  1. 事件注册在祖先级元素上,代理其子级元素。可以减少事件注册数量,节约内存开销,提高性能。

  2. 对js动态添加的子元素可自动绑定事件。

 

之前一直用各种js库的事件代理,如 jQuery,非常方便实用。今天尝试用原生 js 实现该功能。

  1 var addEvent = (function () {
  2   if (document.addEventListener) {
  3     return function (element, type, handler) {
  4       element.addEventListener(type, handler, false);
  5     };
  6   } else if (document.attachEvent) {
  7     return function (element, type, handler) {
  8       element.attachEvent('on' + type, function () {
  9         handler.apply(element, arguments);
 10       });
 11     };
 12   } else {
 13     return function (element, type, handler) {
 14       element['on' + type] = function () {
 15         return handler.apply(element, arguments);
 16       };
 17     };
 18   }
 19 })(),
 20 
 21 getClassElements = function (parentElement, classname) {
 22   var all, element, classArr = [], classElements = [];
 23 
 24   if (parentElement.getElementsByClassName) {
 25     return parentElement.getElementsByClassName(classname);
 26   } else {
 27     all = parentElement.getElementsByTagName('*');
 28 
 29     for (var i = 0, len = all.length; i < len; i++) {
 30       element = all[i];
 31       classArr = element && element.className && element.className.split(' ');
 32 
 33       if (classArr) {
 34         for (var j = 0; j < classArr.length; j++) {
 35           if (classArr[j] === classname) {
 36             classElements.push(element);
 37           }
 38         }
 39       }
 40     }
 41 
 42     return classElements;
 43   }
 44 },
 45 
 46 delegate = function () { // 参数:element, type, [selector,] handler
 47   var args = arguments,
 48   element = args[0],
 49   type = args[1],
 50   handler;
 51 
 52   if (args.length === 3) {
 53     handler = args[2];
 54     return addEvent(element, type, handler);
 55   }
 56 
 57   if (args.length === 4) {
 58     selector = args[2];
 59     handler = args[3];
 60 
 61     return addEvent(element, type, function (event) {
 62       var event = event || window.event,
 63       target = event.target || event.srcElement,
 64       quickExpr = /^(?:[a-zA-Z]*#([\w-]+)|(\w+)|[a-zA-Z]*\.([\w-]+))$/,
 65       match,
 66       idElement,
 67       elements,
 68       tagName,
 69       count = 0,
 70       len;
 71 
 72       if (typeof selector === 'string') {
 73         match = quickExpr.exec(selector);
 74 
 75         if (match) {
 76           // #ID selector
 77           if (match[1]) {
 78             idElement = document.getElementById(match[1]);
 79             tagName = match[0].slice(0, match[0].indexOf('#'));
 80 
 81           // tag selector
 82           } else if (match[2]) {
 83             elements = element.getElementsByTagName(selector);
 84 
 85           // .class selector
 86           } else if (match[3]) {
 87             elements = getClassElements(element, match[3]);
 88             tagName = match[0].slice(0, match[0].indexOf('.'));
 89           }
 90         }
 91 
 92         if (idElement) {
 93           if ( tagName ? tagName === idElement.nodeName.toLowerCase() && target === idElement : target === idElement ) {
 94             return handler.apply(idElement, arguments);
 95           }
 96         } else if (elements) {
 97           for (len = elements.length; count < len; count++) {
 98             if ( tagName ? tagName === elements[count].nodeName.toLowerCase() && target === elements[count] : target === elements[count] ) {
 99               return handler.apply(elements[count], arguments);
100             }
101           }
102         }
103       }
104     });
105   }
106 };

主要是用 apply 改变 this 的指向

handler.apply(idElement, arguments);

handler.apply(elements[count], arguments);

 

测试一下:

<style>
#outer {padding: 50px; background-color: lightpink;}
#inner {padding: 30px; background-color: aliceblue;}
#paragraph1, #paragraph3 {background-color: cadetblue}
</style>
<div id="outer">outer
  <div id="inner">inner
    <p id="paragraph1" class="parag1">paragraph1</p>
    <p id="paragraph2" class="parag">paragraph2</p>
    <span>span</span>
    <p id="paragraph3" class="parag">paragraph3</p>
  </div>
</div>
var outer = document.getElementById('outer');

delegate(outer, 'click', function () {
  console.log(this.id); // outer
});
delegate(outer, 'click', 'p', function () {
  console.log(this.id);  //点击 paragraph1 元素,输出其id为 "paragraph1"
});

 

 

模仿 jQuery 的风格,优化代码:

  1 (function () {
  2   var $ = function (element) {
  3     return new _$(element);
  4   };
  5 
  6   var _$ = function (element) {
  7     this.element = element && element.nodeType === 1 ? element : document;
  8   };
  9 
 10   _$.prototype = {
 11     constructor: _$,
 12 
 13     addEvent: function (type, handler, useCapture) {
 14       var element = this.element;
 15 
 16       if (document.addEventListener) {
 17         element.addEventListener(type, handler, (useCapture ? useCapture : false));
 18       } else if (document.attachEvent) {
 19         element.attachEvent('on' + type, function () {
 20           handler.apply(element, arguments);
 21         });
 22       } else {
 23         element['on' + type] = function () {
 24           return handler.apply(element, arguments);
 25         };
 26       }
 27 
 28       return this;
 29     },
 30 
 31     getClassElements: function (classname) {
 32       var element = this.element, all, ele, classArr = [], classElements = [];
 33 
 34       if (element.getElementsByClassName) {
 35         return element.getElementsByClassName(classname);
 36       } else {
 37         all = element.getElementsByTagName('*');
 38 
 39         for (var i = 0, len = all.length; i < len; i++) {
 40           ele = all[i];
 41           classArr = ele && ele.className && ele.className.split(' ');
 42 
 43           if (classArr) {
 44             for (var j = 0; j < classArr.length; j++) {
 45               if (classArr[j] === classname) {
 46                 classElements.push(ele);
 47               }
 48             }
 49           }
 50         }
 51 
 52         return classElements;
 53       }
 54     },
 55 
 56     delegate: function () { //参数:type, [selector,] handler
 57       var self = this,
 58       element = this.element,
 59       type = arguments[0],
 60       handler;
 61 
 62       if (arguments.length === 2) {
 63         handler = arguments[1];
 64         return self.addEvent(type, handler);
 65       } else if (arguments.length === 3) {
 66         selector = arguments[1];
 67         handler = arguments[2];
 68 
 69         return self.addEvent(type, function (event) {
 70           var event = event || window.event,
 71           target = event.target || event.srcElement,
 72           quickExpr = /^(?:[a-zA-Z]*#([\w-]+)|(\w+)|[a-zA-Z]*\.([\w-]+))$/,
 73           match,
 74           idElement,
 75           elements,
 76           tagName,
 77           count = 0,
 78           len;
 79 
 80           if (typeof selector === 'string') {
 81             match = quickExpr.exec(selector);
 82 
 83             if (match) {
 84               // #ID selector
 85               if (match[1]) {
 86                 idElement = document.getElementById(match[1]);
 87                 tagName = match[0].slice(0, match[0].indexOf('#'));
 88 
 89               // tag selector
 90               } else if (match[2]) {
 91                 elements = element.getElementsByTagName(selector);
 92 
 93               // .class selector
 94               } else if (match[3]) {
 95                 elements = self.getClassElements(match[3]);
 96                 tagName = match[0].slice(0, match[0].indexOf('.'));
 97               }
 98             }
 99 
100             if (idElement) {
101               if ( tagName ? tagName === idElement.nodeName.toLowerCase() && target === idElement ? target === idElement ) {
102                 return handler.apply(idElement, arguments);
103               }
104             } else if (elements) {
105               for (len = elements.length; count < len; count++) {
106                 if ( tagName ? tagName === elements[count].nodeName.toLowerCase() && target === elements[count] : target === elements[count] ) {
107                   return handler.apply(elements[count], arguments);
108                 }
109               }
110             }
111           }
112         });
113       }
114     }
115   };
116 
117   window.$ = $;
118   return $;
119 }());
View Code

 

使用如下:

var outer = document.getElementById('outer');
$(outer).delegate('click', '.parag', function (event) {
  console.log(this.id);
});

 

posted on 2016-02-29 12:49  caihg  阅读(465)  评论(0编辑  收藏  举报