rwson

rwson

一个前端开发

javascript中事件代理和取消

在日常前端开发中,事件是必不可少的一部分,有时候我们需要根据后端接口的返回来动态输出HTML,这时候直接用jQuery中的click之类的方法已经不能达到目的,需要重新绑定或者用事件代理来实现事件绑定。

事件代理的原理很简单: 事件冒泡会触发容器 dom 的相关事件并执行监听函数。

比如我们有这样一段HTML:

    <div class="container">
        <div class="block1"></div>
        <div class="block2"></div>
        <div class="block3"></div>
        <div class="block4"></div>
    </div>

我们想给class中带有block的元素绑定一个点击事件:

window.onload = function() {
   var container = document.querySelector(".selector");
   
  container.addEventListener("click", function(e) {
      var ev = e || window.event,
          target = ev.target,
          classList = target.classList;
   	  if (classList.contains("block1") || classList.contains("block2") || classList.contains("block3") || classList.contains("block4")) {
          console.log("target clicked," + classList[0]);
      }
  });
};

如上我们就实现一个事件代理。

这种方法最大的好处的简单方便,但是问题来了,如果我们在某个操作后把相关元素从DOM中删除了之后,之前代理的事件并不会随着一起取消,在jQuery中有deletegateundeletegate之类的方法来进行和取消事件代理,下面我们用javascript来实现并且封装一个事件代理函数,完成事件代理和提供取消事件代理两个功能:

还是上面的HTML,这次我们加点样式:

    <style type="text/css">
    .container div {
        width: 100px;
        height: 100px;
    }

    .block1 {
        background: red;
    }

    .block2 {
        background: green;
    }

    .block3 {
        background: yellow;
    }

    .block4 {
        background: black;
    }
    </style>
    <div class="container">
        <div class="block1"></div>
        <div class="block2"></div>
        <div class="block3"></div>
        <div class="block4"></div>
    </div>

下面我们完成对刚才需求的封装:

//	event.match() Polyfill
//	https://developer.mozilla.org/zh-CN/docs/Web/API/Element/matches
if (!Element.prototype.matches) {
        Element.prototype.matches =
            Element.prototype.matchesSelector ||
            Element.prototype.mozMatchesSelector ||
            Element.prototype.msMatchesSelector ||
            Element.prototype.oMatchesSelector ||
            Element.prototype.webkitMatchesSelector ||
            function(s) {
                var matches = (this.document || this.ownerDocument).querySelectorAll(s),
                    i = matches.length;
                while (--i >= 0 && matches.item(i) !== this) {}
                return i > -1;
            };
    }


    window.onload = function() {
        var container = document.querySelector(".container"),
            
            //	定义每个元素的处理函数
            handlers = {
                block1: function(evt, inst) {
                    console.log("block1 clicked!");
                },
                block2: function(evt, inst) {
                    console.log("block2 clicked!");
                  	//	响应处理一遍后将当前代理取消
                    handlers["block2-undelegate"].destroy();
                },
                block3: function(evt, inst) {
                    console.log("block3 clicked!");
                },
                block4: function(evt, inst) {
                    console.log("block4 clicked!");
                }
            },
            evt;

      	
       /**
         *  @param   {HTMLElement}  container  绑定事件的元素
         *  @param   {String}  		type       需要代理的事件类型
         *  @param   {String}	    selector   css选择器
         *  @param   {Function}  	handler    事件处理函数
         *  @param   {Boolean}  	capture    useCapture
         *  @return  {Object}             	   取消代理承载对象
         */
        function deletegate(container, type, selector, handler, capture) {
        	var ev, target, inst, listener;

        	//	响应函数
        	listener = function (e) {
	            ev = e || window.event;
	            target = ev.target;

	            if (target.matches(selector)) {
	            	handler(ev);
	            }
        	};

        	//	提供destroy接口
        	inst = {
	        	destroy: function() {
	        		//	解除元素上的事件绑定
	        		container.removeEventListener(type, listener, capture);
	        	}
	        };

	        //	添加事件绑定, 处理函数用我们上面组织的
			container.addEventListener(type, listener, capture);

	        return inst;
        }

      	//	枚举刚才定义的handlers, 给每个元素代理click事件
        for (var i in handlers) {
        	if (handlers.hasOwnProperty(i)) {
              	//	给handlers上添加新属性,包含该元素上的destroy
        		handlers[i + "-undelegate"] = deletegate(container, "click", "." + i, handlers[i]);
        	}
        }
    };

上面就是我们对事件代理和取消事件代理的封装和应用,下图就是我们的运行效果:

事件代理

总结下:

事件代理有可以给动态添加到DOM中的元素绑定事件,减少事件注册的优点

缺点就是该方法的局限性,仅适合动态加入到页面中的元素(当然例子中只是做demo),就没有动态创建DOM元素了