事件委托,就是把事件委托给别人,让别人来帮自己完成。
注:本文 JavaScript 代码部分使用 jQuery 。
本文目录:
- 事件冒泡;
- 事件委托。
事件委托采用了事件冒泡的原理,下面先看一下什么叫事件冒泡。
事件冒泡
众所周知,冒泡是从水底往水面上冒的,而事件冒泡也是这个原理:事件是从子元素往父元素传播的(事件捕获则相反,详情请看 这里),如果子元素和父元素都绑定了相同的事件(如:“click”),那么当事件触发时,子元素和父元素都会执行各自对应的事件处理函数,这时候就会存在事件冲突的问题。
为了更好地解释上面的问题,下面我们来看一个例子:
在页面上有一个三级菜单,实现的功能是这样的:
- 一开始只显示一级菜单,二、三级菜单隐藏;
- 点击一级菜单,显示二级菜单,三级菜单仍然隐藏;
- 点击二级菜单,显示三级菜单;
- 点击三级菜单的某一项,其字体颜色变为红色。
代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>事件冒泡</title>
<style type="text/css">
#_ul2 {
display: none;
}
#_ul3 {
display: none;
}
</style>
</head>
<body>
<ul id="_ul1">
<li id="_li1">一级菜单
<ul id="_ul2">
<li id="_li2">二级菜单
<ul id="_ul3">
<li id="_li3">三级菜单</li>
</ul>
</li>
</ul>
</li>
</ul>
<script src="js/jquery-1.12.4.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
// 点击一级菜单,显示二级菜单
$('#_li1').on('click', function() {
$('#_ul2').css('display') == 'block' ? $('#_ul2').css('display', 'none') : $('#_ul2').css('display', 'block');
});
// 点击二级菜单,显示三级菜单
$('#_li2').on('click', function() {
$('#_ul3').css('display') == 'block' ? $('#_ul3').css('display', 'none') : $('#_ul3').css('display', 'block');
});
// 点击三级菜单,字体颜色变为红色
$('#_li3').on('click', function() {
$(this).css('color', 'red');
})
</script>
</body>
</html>
效果演示:
咦( ′◔ ‸◔`)?效果跟我们想象中的不一样啊?这是怎么回事?我们来简单分析一下:由于我们的给这三级菜单都绑定了点击事件,由于事件冒泡的存在,所以当我们触发了子元素的点击事件时,也相应地触发了父元素的点击事件。
那么,该怎么解决这个问题呢?解决办法就是阻止子元素的事件向父元素传播,也就是阻止事件冒泡啦。我们把上面 JavaScript 的代码修改如下:
代码:
<script src="js/jquery-1.12.4.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
// 点击一级菜单,显示二级菜单
$('#_li1').on('click', function() {
$('#_ul2').css('display') == 'block' ? $('#_ul2').css('display', 'none') : $('#_ul2').css('display', 'block');
});
// 点击二级菜单,显示三级菜单
$('#_li2').on('click', function() {
event.stopPropagation(); // 阻止事件冒泡
$('#_ul3').css('display') == 'block' ? $('#_ul3').css('display', 'none') : $('#_ul3').css('display', 'block');
});
// 点击三级菜单,字体颜色变为红色
$('#_li3').on('click', function() {
event.stopPropagation(); // 阻止事件冒泡
$(this).css('color', 'red');
});
</script>
效果演示:
我们在子元素的事件里加入了一句 <code>event.stopPropagation()</code> 来阻止事件冒泡(IE 下使用 <code>cancelBubble = true</code>)。
注意:并不是所有的事件都能冒泡,blur、focus、load、unload 等事件是不能冒泡的。
事件委托
事件委托利用了事件冒泡的原理,可以为多个元素绑定相同的事件,也可以为尚不存在的元素绑定事件;在 jQuery 中,使用 <code>on()</code> 方法来完成上述事件,关于 <code>on()</code> 方法的使用请看 jQuery - 事件(一)之 事件绑定。
下面我们重点介绍如何为尚不存在的元素绑定事件,这个多发生在 使用 Ajax 的地方,为多个元素绑定相同的事件道理也一样。
下面我们来做这样一个例子:页面上有一个 ul 元素,ul 元素下有若干个 li 元素(li 元素的数量取决于服务器返回的数据),点击 li,其字体变为红色。
代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>事件委托</title>
</head>
<body>
<ul id="_ul">我是 ul
</ul>
<script src="js/jquery-1.12.4.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
// 事件委托
$('#_ul').on('click', 'li', function() {
$(this).css('color', 'red');
});
// 使用 setTimeout() 函数模拟从服务器加载数据,之后渲染页面
setTimeout(function() {
$('#_ul').append('<li>list item 1</li><li>list item 2</li><li>list item 3</li>');
}, 3000);
</script>
</body>
</html>
效果演示:
如上所示,我们使用一个 <code>setTimeout()</code> 方法来模拟从服务器加载数据的耗时过程,拿到数据之后渲染页面,由于父元素 ul 下不存在子元素 li,所以我们利用事件冒泡的原理,将子元素 li 的点击事件委托给父元素 ul 来完成,这就是事件委托。
注意,下面这样写不是事件委托:
代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>事件委托</title>
</head>
<body>
<ul id="_ul">我是 ul
</ul>
<script src="js/jquery-1.12.4.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
// 使用 setTimeout() 函数模拟从服务器加载数据,之后渲染页面
setTimeout(function() {
$('#_ul').append('<li>list item 1</li><li>list item 2</li><li>list item 3</li>');
}, 3000);
$('#_ul li').on('click', function() {
$(this).css('color', 'red');
});
</script>
</body>
</html>
效果演示:
上面只是为 ul 元素下的 li 元素绑定点击事件,由于此时的 li 元素并不存在,所以绑定失败。
参考资料: