YAOHAIXIAO.COM

HTML(5),CSS(3),JavaScript,DOM,Ajax,JSON,Front-end technologies & Yaohaixiao

热门标签:JavaScript Performance 前端开发 前端性能优化 原创

Rss

Home » Frontend » JavaScript » Events » Event Delegation in JavaScript

Event Delegation in JavaScript

Event Delegation 是我很久前就想写的一个话题。Event Delegation 是一个非常高效的事件处理模式,相信做过一段时间前端开发的朋友也都对 Event Delegation 有所耳闻。

什么是 Event Delegation?

直接翻译 Event Delegation 可以翻译为事件代理(委托),那到底什么是 Event Delegation 呢?

我的理解, Event Delegation 应该是利用 JavaScript 中的 Event bubbling (事件冒泡) 机制,通过给单个 DOM (父)节点绑定事件,从而可以管理这个节点下所有子节点的事件处理Deligage(代理活委托)就是指的所有事件的处理都是通过父节点来代理处理的)。

所以,总结一下 Event Delegation 的关键词:

  • Event bubbling
  • 单个父节点,(代理)管理其下所有子节点的事件处理

Event Bubbling 和 Event Capturing

要理解 Event Delegation 实现的机制,Event Bubbling 是不得不说的,因为能够实现代理的关键就在 Event Bubbling。我想大家应该都都清楚,JavaScript 中,有两种事件处理机制:Event bubbling(事件冒泡) 和 Event capturing(事件捕获)。

这两个事件处理机制分别是由 Microsoft 和 Netscape 公司制定的。两个事件处理机制正好相反。让我们看看这样一段 HTML代码的 DOM 树结构:

<ul id="list">
	<li><p><a href="#1">Link One</a></p></li>
	<li><p><a href="#1">Link Two</a></p></li>
	<li><p><a href="#1">Link Three</a></p></li>
	<li><p><a href="#1">Link Four</a></p></li>
	<li><p><a href="#1">Link Five</a></p></li>
</ul>

如果我们点击上面代码中的 Link One 链接,Event bubbling(事件冒泡)首先会触发上面的DOM书中,层次最深的Link One链接 A 标签的事件,然后是上一层的 P 标签的事件,然后继续向上触发 LI 标签,然后是 UL 标签。

Event capturing(事件捕获)则正好相反,首先会触发最顶层的 UL 事件,然后向下是 LI 标签,最后猜到最底层的 A 标签。

event-delegation

Event Delegation 是使用的 Event bubbling(事件冒泡),因为根据冒泡机制,我们会看到,无论我们点击了UL#list 下的任意子节点 LI、P、A 标签,都会向上冒泡到 UL这个父节点。所以如果使用 Event Delegation,我们只需要给 UL#list 绑定事件,就可以管理其下所有子节点的事件处理。

好了,说了半天的 Event Delegation,现在就看看用 Event Delegation 是如何只给 UL#list 绑定 click 事件,来管理它下面所有 A 标签子节点的的 click 事件的,代码如下:

// Event Delegation处理方式
var List = document.getElementById('list');
	
List.onclick = function(evt){
    var evt = evt || window.event;
	var link = evt.target || evt.srcElement;
		
	if(link.tagName.toLowerCase() === 'a'){
		alert("this is \'" + link.innerHTML + "\' in UL#List");
	}
};

Event Delegation 如何判定点击的子节点

这里需要解释一下 Event Delegation 是怎么判定点击是 UL#list 中的哪个子节点呢?我猜,打一开始你就好会有这样的疑问把?只给单个父节点绑定事件处理,UL#list 下有那么多的子节点,我怎么判定点击了 UL#list 下的哪个子节点呢?

利用 event.target 和 event.srcElement 来判定点击了哪个子节点

List.onclick = function(evt){
    var evt = evt || window.event;
    // 获得鼠标点击的目标
    var link = evt.target || evt.srcElement;
		
    if(link.tagName.toLowerCase() === 'a'){
	    alert("this is \'" + link.innerHTML + "\' in UL#List");
    }
};

看看上面代码加粗的代码var link = evt.target || evt.srcElementEvent Delegation 判断点击目标的关键就是利用 event 对象的 target 属性,而 srcElement 则是 IE 特有的。没有办法,只要你还需要考虑 IE9 以下的浏览器,就必须这么处理:

var link = evt.target || evt.srcElement

event.target 或者 event.srcElement 只是帮我们获取到了事件触发的目标,如果你需要更进一步过滤,就需要类似 link.tagName.toLowerCase() === 'a' 这样的过滤方式,来更进一步的精确点击的目标。这里 link.tagName.toLowerCase() === 'a' 就是找到了 UL#list 下所有 A 标签。

Event Delegation 的优势

上面介绍很多了 Event Delegation 基础知识,现在我要正式介绍Event Delegation 的优势了:

  1. 更少的(事件绑定)函数管理
  2. 占用更少的内存资源
  3. 更少的JavaScriptDOM 处理
  4. 不用担心 DOM 被移除时相关事件处理函数的内存泄漏问题

Event Deligate 的这些优势在需要处理大量事件绑定需要频繁添加移除 DOM 的交互操作时尤为明显。例如一个购物车的程序:购物车演示效果

<div class="wrap container">
    <div class="main" id="main">
        <ul id="products-list">
            <li id="product-1">
                <img class="post-thumbnail" src="http://www.yaohaixiao.com/scripts/yalbum/samples/car-01.jpg" alt="car" width="80" height="80" />
                <p>
                    直接翻译 Event Delegation 可以翻译为事件代理(委托),那到底什么是 Event Delegation 呢?
                </p>
                <a class="button link-fav-added" href="#add-fav">加入购物车</a>
            </li>
            <li id="product-2">
                <img class="post-thumbnail" src="http://www.yaohaixiao.com/scripts/yalbum/samples/car-02.jpg" alt="car" width="80" height="80" />
                <p>
                    直接翻译 Event Delegation 可以翻译为事件代理(委托),那到底什么是 Event Delegation 呢?
                </p>
                <a class="button link-add" href="#add-fav">加入购物车</a>
            </li>
            <li id="product-3">
                <img class="post-thumbnail" src="http://www.yaohaixiao.com/scripts/yalbum/samples/car-03.jpg" alt="car" width="80" height="80" />
                <p>
                    直接翻译 Event Delegation 可以翻译为事件代理(委托),那到底什么是 Event Delegation 呢?
                </p>
                <a class="button link-add" href="#add-fav">加入购物车</a>
            </li>
            <li id="product-4">
                <img class="post-thumbnail" src="http://www.yaohaixiao.com/scripts/yalbum/samples/car-04.jpg" alt="car" width="80" height="80" />
                <p>
                    直接翻译 Event Delegation 可以翻译为事件代理(委托),那到底什么是 Event Delegation 呢?
                </p>
                <a class="button link-add" href="#add-fav">加入购物车</a>
            </li>
            <li id="product-5">
                <img class="post-thumbnail" src="http://www.yaohaixiao.com/scripts/yalbum/samples/car-05.jpg" alt="car" width="80" height="80" />
                <p>
                    直接翻译 Event Delegation 可以翻译为事件代理(委托),那到底什么是 Event Delegation 呢?
                </p>
                <a class="button link-add" href="#add-fav">加入购物车</a>
            </li>
            <li id="product-6">
                <img class="post-thumbnail" src="http://www.yaohaixiao.com/scripts/yalbum/samples/car-06.jpg" alt="car" width="80" height="80" />
                <p>
                    直接翻译 Event Delegation 可以翻译为事件代理(委托),那到底什么是 Event Delegation 呢?
                </p>
                <a class="button link-add" href="#add-fav">加入购物车</a>
            </li>
            <li id="product-7">
                <img class="post-thumbnail" src="http://www.yaohaixiao.com/scripts/yalbum/samples/car-07.jpg" alt="car" width="80" height="80" />
                <p>
                    直接翻译 Event Delegation 可以翻译为事件代理(委托),那到底什么是 Event Delegation 呢?
                </p>
                <a class="button link-add" href="#add-fav">加入购物车</a>
            </li>
            <li id="product-8">
                <img class="post-thumbnail" src="http://www.yaohaixiao.com/scripts/yalbum/samples/car-08.jpg" alt="car" width="80" height="80" />
                <p>
                    直接翻译 Event Delegation 可以翻译为事件代理(委托),那到底什么是 Event Delegation 呢?
                </p>
                <a class="button link-add" href="#add-fav">加入购物车</a>
            </li>
            <li id="product-9">
                <img class="post-thumbnail" src="http://www.yaohaixiao.com/scripts/yalbum/samples/car-09.jpg" alt="car" width="80" height="80" />
                <p>
                    直接翻译 Event Delegation 可以翻译为事件代理(委托),那到底什么是 Event Delegation 呢?
                </p>
                <a class="button link-add" href="#add-fav">加入购物车</a>
            </li>
            <li id="product-10">
                <img class="post-thumbnail" src="http://www.yaohaixiao.com/scripts/yalbum/samples/car-10.jpg" alt="car" width="80" height="80" />
                <p>
                    直接翻译 Event Delegation 可以翻译为事件代理(委托),那到底什么是 Event Delegation 呢?
                </p>
                <a class="button link-add" href="#add-fav">加入购物车</a>
            </li>
        </ul>
    </div>
    <div class="side" id="side">
        <h2>购物车</h2>
        <ul id="fav-list">
            <li>
                <img width="32" height="32" alt="car" src="http://www.yaohaixiao.com/scripts/yalbum/samples/car-01.jpg" class="post-thumbnail">
                <p>
                    直接翻译 Event Delegation 可以翻译为事件代理(委托),那到底什么是 Event Delegation 呢?
                </p>
                <a class="button link-remove" rel="product-1" href="#remove-fav">从购物车移除</a>
            </li>
        </ul>
    </div>
</div>

// JavaScript 代码
var ProductList = document.getElementById("products-list"), 
    FavList = document.getElementById("fav-list"), 
	TAG_A = "a", 
	TAG_LI = "li", 
	CLS_BTN = "button link-add", 
	CLS_REMOVE_BTN = "button link-remove", 
	CLS_ADDED_BTN = "button link-fav-added", 
	TXT_ADD = "加入购物车", 
	TXT_REMOVE = "从购物车移除",
    TXT_EMPTY = "购物车中无任何商品", 
	TXT_ITEM_ADDED = "购物车中已经添加了改商品!", 
	HASH_ADD = "#add-fav", 
	HASH_REMOVE = "#remove-fav", 
	EMPTY_ID = "empty-item", 
	emptyItem = document.getElementById(EMPTY_ID);

ProductList.onclick = function(evt){
    var evt = evt || window.event;
    var target = evt.target || evt.srcElement, tmpLi = document.createElement(TAG_LI), productId = "";
    
    tmpLi.innerHTML = target.parentNode.innerHTML.replace(CLS_BTN, CLS_REMOVE_BTN).replace(TXT_ADD, TXT_REMOVE).replace(HASH_ADD, HASH_REMOVE);
    productId = target.parentNode.id;
    
    if (target.tagName.toLowerCase() === TAG_A) {
        if (target.className === CLS_BTN) {
            if (emptyItem) {
                FavList.removeChild(emptyItem);
		emptyItem = null;
            }
            FavList.appendChild(tmpLi);
            target.className = CLS_ADDED_BTN;
            tmpLi.getElementsByTagName(TAG_A)[0].rel = productId;
        }
        else {
            if (target.className === CLS_ADDED_BTN) {
                alert(TXT_ITEM_ADDED);
            }
        }
    }
};

FavList.onclick = function(evt){
    var evt = evt || window.event;
    var target = evt.target || evt.srcElement, relatedItem = null, favItem = null;
    
    if (target.className === CLS_REMOVE_BTN) {
        favItem = target.parentNode;
        relatedItem = document.getElementById(target.rel);
        relatedItem.innerHTML = relatedItem.innerHTML.replace(CLS_ADDED_BTN, CLS_BTN);
        FavList.removeChild(favItem);
        
        if (FavList.getElementsByTagName(TAG_LI).length < 1) {
            emptyItem = document.createElement(TAG_LI);
            emptyItem.id = EMPTY_ID;
            emptyItem.innerHTML = TXT_EMPTY;
            FavList.appendChild(emptyItem);
        }
    }
};

更少的(事件绑定)函数管理

这个购物车的添加和移除按钮的事件绑定处理就是使用的 Event Delegation 实现的。我们可以看到,所有的产品列表中的 “加入购物车”和购物车列表中的“从购物车移除”按钮的 click 事件触发都是通过它们各自的父节点 UL#products-list 和 UL#fav-list 代理处理的。

很明显,我只做了2次事件处理函数的绑定(更少的(事件绑定)函数管理),如果是传统的处理方式,我必须先遍历 UL#product-list 列表下的所有 “加入购物车”按钮,然后逐个绑定 click 事件。所以按照我的示例,我至少要绑定10次事件处理。

占用更少的内存资源

我们知道,页面中 DOM 事件绑定得越多,要占用的内存资源也就越多。使用 Event Delegation 处理,只用2绑定,至少了8次事件绑定,自然就占用更少的内存资源。而且,事实上使用 Event Delegation 根据 jQuery event delegation 的测试,代码的效率也是会更高的。

更少的JavaScriptDOM 处理

很显然,使用了 Event Delegation 处理方式后,我只用与 UL#product-list 和 UL#fav-list 两个 DOM 节点打交道,确实是更少的JavaScriptDOM 处理

不用担心 DOM 被移除时相关事件处理函数的内存泄漏问题

(个人认为) Event Delegation 最大优势就在于不用担心 DOM 被移除时相关事件处理函数的内存泄漏问题。在上面的购物车实例中,用户会频繁的添加移除商品到购物车。在 JavaScript 的处理 DOM 的交互中,这种给 DOM 绑定处理函数,然后处理函数中又反向指向这个 DOM 节点的这种相互指向的 DOM 操作。如果在移除 DOM 节点时,没有及时的清除 DOM 节点的绑定的处理函数,这个时候在 IE 浏览器中就会因为绑定函数的内存资源没有及时回收而产生内存泄漏问题(具体的内容可以参考 Douglas Crockford 的《JScript的内存泄漏》)。拿这里的购物车为例,如果以传统的方式处理,代码大致是下面这样的:

// 传统的事件绑定处理
var FavList = document.getElementById("fav-list"),
    removeButtons = FavList.getElementsByTagName("a"),
    i = 0,
    len = removeButtons.length,
	TAG_A = "a", 
	TAG_LI = "li", 
	CLS_BTN = "button link-add", 
	CLS_REMOVE_BTN = "button link-remove", 
	CLS_ADDED_BTN = "button link-fav-added", 
	TXT_ADD = "加入购物车", 
	TXT_REMOVE = "从购物车移除",
    TXT_EMPTY = "购物车中无任何商品", 
	TXT_ITEM_ADDED = "购物车中已经添加了改商品!", 
	HASH_ADD = "#add-fav", 
	HASH_REMOVE = "#remove-fav", 
	EMPTY_ID = "empty-item", 
	relatedItem = null, 
	favItem = null,
	emptyItem = document.getElementById(EMPTY_ID),
	removeItem = function(button){
        if (button.className === CLS_REMOVE_BTN) {
            favItem = button.parentNode;
            relatedItem = document.getElementById(button.rel);
            relatedItem.innerHTML = relatedItem.innerHTML.replace(CLS_ADDED_BTN, CLS_BTN);
            FavList.removeChild(favItem);
        
            if (FavList.getElementsByTagName(TAG_LI).length < 1) {
                emptyItem = document.createElement(TAG_LI);
                emptyItem.id = EMPTY_ID;
                emptyItem.innerHTML = TXT_EMPTY;
                FavList.appendChild(emptyItem);
            }
        };
	};
	
	for(;i < len;i+=1){
        (function(index){
            removeButtons[index].onclick = function(evt){
			    removeItem(removeButtons[index]);
			};
		})(i);
	}

很明显,从我上面的 HTML 代码可以看到,UL#fav-list 中默认只有一个“从购物车移除”按钮,而且通常情况下,购物车栏里是没有收藏任何商品的,因此传统的事件绑定多数情况下是根本起不了作用的。

这里再举个例子说明用传统的方式绑定事件处理会出现的问题,演示地址:http://www.yaohaixiao.com/script/event-delegate/remove.html

<html>
<head>
<meta charset="utf-8">
	<title>移除 DOM - Event Delegation in JavaScript</title>
	<meta name="keywords" content="DOM,Event Delegation,JavaScript" />
	<meta name="description" content="移除 DOM - Event Delegation in JavaScript" />
</head>
<body>
<ul id="list">
	<li><a href="#delete">删除</a></li>
	<li><a href="#delete">删除</a></li>
	<li><a href="#delete">删除</a></li>
	<li><a href="#delete">删除</a></li>
	<li><a href="#delete">删除</a></li>
	<li><a href="#delete">删除</a></li>
	<li><a href="#delete">删除</a></li>
	<li><a href="#delete">删除</a></li>
	<li><a href="#delete">删除</a></li>
	<li><a href="#delete">删除</a></li>
</ul>
<script type="text/javascript">
(function(){
	var list = document.getElementById("list"),
	    buttons = list.getElementsByTagName("a"),
		i = 0,
		len = buttons.length;
		
	for(;i < len;i+=1){
		(function(index){
			buttons[index].onclick = function(evt){
			    // UL#list 下所有 A 标签的 nodeList 为 buttons
				// 传统的方式都是通过 buttons 的索引值 index 来指定删除按钮的
				var curButton = buttons[index],
				    listItem = curButton.parentNode;
				
				list.removeChild(listItem);
				alert("你移除了 list 中的第 " + (index+1) + " 项!");
			};
		})(i);
	}
})();	
</script>	
</body>
</html>

上面的示例,应该是我们平时绑定事件时处理的方式。这么写,由于 UL#list 在绑定事件时,列表项的个数是固定的。如果你点击删除的按钮不是按顺序删除,程序就会报错。因为在删除后,不再是固定的10个列表项了,索引值 index 就改变了。例如你先删除第1项,再删除第10项,程序就会出错的。

当然,这个问题我们也可以解决,就是使用 event.target 来获取当前点击的目标:

(function(){
    var list = document.getElementById("list"), 
        buttons = list.getElementsByTagName("a"), 
        i = 0, 
        len = buttons.length;
    
    for (; i < len; i += 1) {
        (function(index){
            buttons[index].onclick = function(evt){
                // evt.target 可确保你点击是一个存在的节点
                // 这也正是 Event Delegation 的处理方式
                var evt = evt || window.event;
                var target = evt.target || evt.srcElement;
                var curButton = target, listItem = curButton.parentNode;
                
                list.removeChild(listItem);
                alert("你移除了 list 中的第 " + (index + 1) + " 项!");
            };
        })(i);
    }
})();

这样处理后,虽然是不会有“点错”的问题。但是还是做了比较多的绑定,移除 DOM 的时候代码里又没有销毁事件处理函数的处理,所以仍然是有内存泄漏的问题发生的。

再回到购物车实例,如果用传统的处理方式处理,由于默认静态的for循环绑定事件对动态创建的按钮不起作用,所以我们在点击“添加到购物车”按钮的时候,需要将产品添加购物车,同时还需要给新创建的“从购物车移除”按钮绑定事件。那么代码大致是这样的:

// 外面会有一段for循环的处理
for(;i < len ;i+=1){
addFavButton.onclick = function(evt){
    var TAG_A = "a", 
	TAG_LI = "li", 
	CLS_BTN = "button link-add", 
	CLS_REMOVE_BTN = "button link-remove", 
	CLS_ADDED_BTN = "button link-fav-added", 
	TXT_ADD = "加入购物车", 
	TXT_REMOVE = "从购物车移除",
    TXT_EMPTY = "购物车中无任何商品", 
	TXT_ITEM_ADDED = "购物车中已经添加了改商品!", 
	HASH_ADD = "#add-fav", 
	HASH_REMOVE = "#remove-fav", 
	EMPTY_ID = "empty-item", 
	tmpLi = document.createElement(TAG_LI), 
	emptyItem = document.getElementById(EMPTY_ID),
	productId = "";
    
    tmpLi.innerHTML = addFavButton.parentNode.innerHTML.replace(CLS_BTN, CLS_REMOVE_BTN).replace(TXT_ADD, TXT_REMOVE).replace(HASH_ADD, HASH_REMOVE);
    productId = addFavButton.parentNode.id;
    
    if (addFavButton.className === CLS_BTN) {
        if (emptyItem) {
            FavList.removeChild(emptyItem);
        }
        FavList.appendChild(tmpLi);
        addFavButton.className = CLS_ADDED_BTN;
        tmpLi.getElementsByTagName(TAG_A)[0].rel = productId;
		
		// 给新的“从购物车移除”按钮绑定事件处理函数
		tmpLi.onclick = function(evt){
		     // removeItem 见上例中的内容
			 removeItem(tmpLi);
		};
    }
    else {
        if (addFavButton.className === CLS_ADDED_BTN) {
            alert(TXT_ITEM_ADDED);
        }
    }
};
}

请注意这段代码,这里就是我前面说的有 DOM 和事件处理函数有相互指向的地方:

// 给新的“从购物车移除”按钮绑定事件处理函数
tmpLi.onclick = function(evt){
	// removeItem 见上例中的内容
	removeItem(tmpLi);
};

这样有DOM 和事件处理函数有相互指向的调用方式,由于在移除tmpLi 按钮前又缺少了对 tmpLi 按钮事件处理函数的销毁(内存回收),这样在之后移除这个按钮时就会带来内存泄漏的问题。(我这里反复的讲述了内存泄漏是如何产生的,希望大家看明白了!)而“加入购物车”按钮的处理函数,既要负责添加内容,又要为新添加的按钮绑定事件处理函数,所以这里就有更糟糕的2次的相互指向调用了,也就更容易出现内存泄漏了。

使用 Event Delegation 则不会出现内存泄漏的情况。因为 Event Delegation 是直接将事件处理函数绑定到 UL#fav-list 这个父节点上的。首先,它是不会被移除的。其次,处理函数里都是对 UL#fav-list 下的子节点的操作,所以也不会产生相互指向的问题。所以使用 Event Delegation 是非常安全的,不用担心出现内存泄漏的问题

另外,通过上面的代码对比,我们可以很直观地感受到 Event Delegation 的代码也是更高效的。采用 Event Delegation 处理方式,代码的可读性和可维护性明显更好。UL#product-list 只用负责“加入购物车”按钮相关的处理,而“从购物车移除”按钮相关的操作则由 UL#fav-list 来管理,两者互不干扰,各司其责。Cool!

让 focus blur 事件的也实现Event Delegation

前面介绍了,Event Delegation 的实现主要利用了 Event Bubbling(事件冒泡)机制。 而 focus、blur 和 change 这3个事件则是特例,它们是不支持 Bubbling Up(冒泡) 的。如果你希望给你的下拉菜单添加 tab 键的快捷访问支持,你就会遇到 focus 和 blur 事件不支持冒泡的问题了。庆幸的是,荷兰的前端大牛 PPK 找到了问题的解决方法:《Delegating the focus and blur events》。这里我就直接引用 PPK 文章里的实例来展示如何让 focus 和 blur 也可以实现 Event Delegation。

<ol id="test">
	<li><a href="#">List item 1</a>
		<ol>
			<li><a href="#">List item 1.1</a></li>
			<li><a href="#">List item 1.2</a></li>
			<li><a href="#">List item 1.3</a></li>
		</ol>
	</li>
	[etc.]
</ol>

function init() {
    // $('id') 是 PPK 对 document.getElementById 的缩写
	var el = $('test');
	// all browsers
	el.onmouseover = handleEvent;
	el.onmouseout = handleEvent;
	// IE
	el.onfocusin = handleEvent;
	el.onfocusout = handleEvent;
	// FF, Saf, Op
	if (el.addEventListener) {
		el.addEventListener('focus',handleEvent,true);
		el.addEventListener('blur',handleEvent,true);
	}
	$('clearLog').onclick = log.clear;
	log.init();
}

var wait;

function handleEvent(e) {
	if (wait){
		clearTimeout(wait);
	}
	var evt = e || window.event;
	var tgt = evt.target || evt.srcElement;
	var writestring = evt.type + ' on ';
	writestring += getElementName(this);
	log.msg(writestring);
	wait = setTimeout(log.end,200);
}

function getElementName(el) {
	var elName = el.type || el.nodeName || 'window';
	return elName.toLowerCase();
}


var log = {
	logEl: undefined,
	currentLI: undefined,
	init: function () {
		this.logEl = $('log');
	},
	msg: function (message) {
		if (!this.currentLI) {
			this.currentLI = document.createElement('li');
			this.logEl.appendChild(this.currentLI);
			if (this.logEl.getElementsByTagName('li').length % 2 == 0){
				this.currentLI.className = 'odd';
			}	
		}
		this.currentLI.innerHTML += message + '
'; }, end: function () { if (log.currentLI){ log.currentLI.removeChild(log.currentLI.lastChild); } log.currentLI = undefined; log.logEl.scrollTop = log.logEl.scrollHeight; }, clear: function clearLog() { while (log.logEl.firstChild){ log.logEl.removeChild(log.logEl.firstChild); } } };

PPK 的演示示例:Focus Blur Example

从 PPK 的例子我们可以看到,针对支持冒泡的事件 mouseover 和 mouseout 我们就可以直接使用 Event Delegation,而针对 focus 和 blur 事件的 Event Delegation, IE 用的它特有的 focusin 和 focusout,其他浏览器则使用的事件捕获处理的。这样一来,focus blur 事件的也实现Event Delegation 了,是不是出乎意料的简单啊?!

看看你的项目中是否能够用 Event Delegation 优化的,赶快行动起来吧。 Event Delegation 值得你一试哦!好了,对 Event Delegation 的相关问题就讲这么多了,如果你有兴趣了解更多的先关内容可以参阅下面的参考阅读。

参考阅读

声明:本文采用BY-NC-SA协议进行授权。转载请注明转自:Event Delegation in JavaScript

« »

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(Spamcheck Enabled)