JS里的事件委托:就是当事件触发时,把要做的事委托给父元素来处理。
再通俗点:就是自己的事不想干,叫它爸爸,甚至爷爷、甚至祖先来干。
作用:
作用1:节约内存
作用2:能为之后新增的DOM元素依然添加事件(如:js动态添加li)
应用场景:
场景1:当多个li标签需要添加点击事件时
场景2: 新增元素没有绑定事件的问题(界面上有一个ul里面默认有5个li,并且还有一个按钮,当点击按钮就创建一个新的li,需要不管是默认有的li还是新的li都有点击事件)
//场景1示例
<html> <head> <meta charset="UTF-8"> <title>demo</title> </head> <body> <ul> <li>item1</li> <li>item2</li> <li>item3</li> <li>item4</li> <li>item5</li> </ul> <script type="text/javascript"> // 找到所有的li var liList = document.getElementsByTagName('li'); // 遍历所有的li,并给每个li添加点击事件 for (var i=0;i<liList.length;i++){ liList[i].onclick = function () { alert(this.innerHTML); } } </script> </body> </html>
代码解析:给5个li标签加了点击事件,当界面上点击li时,会打印它们各自li标签显示的内容。
出现的问题:此时5个li,看起来每个li的点击事件触发时调用的都是同一个函数,即:
function () { alert(this.innerHTML); }
但其实并不是这样。每个li绑定的都是一个全新的函数,只不过每个函数的样子都一模一样。
如何验证这个结论呢?很简单,判断每个li标签的onclick是否相等就可以了。
代码验证如下:
alert(liList[0].onclick==liList[1].onclick); //弹出false
至于上面说的函数长得一样,但不是同一个数据这种情况就类似于 var obj1 = {name:"jack",age:16}和var obj2 = {name:"jack",age:16},obj1 == obj2 得到的结果也会是false。
代码如下:
var ul = document.getElementsByTagName('ul')[0]; // 只用给ul加点击事件即可 ul.onclick = function (e) {
//事件对象在ie8中要通过window.event才能取到,因此e = e || window.event是做兼容处理 e = e || window.event; // e.target指的是被点击的那个li alert(e.target.innerHTML); }
原理解析:
回顾事件冒泡
事件冒泡:即一个元素的事件触发后,会依次一级一级往上调用父级元素的同名事件,直到window(注:IE8和之前的浏览器只到document)
例:div > p > span 当div和p以及span都添加了click事件,当点击span时,会依次向上触发span、p、div的click事件。其中,在每个触发的事件里,通过事件对象.target能拿到触发事件的源头元素,也就是 事件源alert(e.target),结果都是[object HTMLSpanElement].
在回顾完事件冒泡后,我们显而易见得到结论:给所有li添加点击事件,只要加到它们的父元素ul身上的根本原因是利用了事件冒泡。也即:无论点击哪个li,都会自动触发ul的点击事件,然后在ul里通过e.target能获得真正被点击的那个li,继而拿到它的innerHTML
看,这样的形式是不是就相当于把自己的事件,委托在父元素身上处理了呢?因而它才会叫事件委托!
思考:如果ul里还有其他子元素例如span,可我只想给li加点击事件,用原来写的事件委托还行吗?
答案是否定的,因为根据事件冒泡原理,所有子元素点击后都会触发父元素的点击,因此,如果你点击了span,也会调用ul的点击事件,这就相当于给span也加了点击事件。这时候该怎么解决呢?
很简单,只要判断一下事件源是不是li就行了,如果是li才执行代码,否则不执行,代码如下:
<html> <head> <meta charset="UTF-8"> <title>demo</title> </head> <body> <ul> <li>item1</li> <li>item2</li> <li>item3</li> <li>item4</li> <li>item5</li> <span>我是span</span> </ul> <script type="text/javascript"> var ul = document.getElementsByTagName('ul')[0]; // 只用给ul加点击事件即可 ul.onclick = function (e) { e = e || window.event; // 判断事件源是不是li if(e.target.nodeName.toLowerCase()=='li'){ // e.target指的是被点击的那个li alert(e.target.innerHTML); } } </script> </body> </html>
jQuery事件委托语法:
解释:
参数1:事件名,代表要绑定什么事件,但是记得不用加on,也就是说如果你想加点击事件,只要写'click'即可,注意是字符串!所以要打单引号或者双引号
参数2:只能由哪个子元素触发,例如我写 "li",就代表只能由这个父元素里面的li触发事件,其他子元素不会触发。需要注意的是,这也是字符串,并且,参数2可以不写,那就代表仅仅只是给父元素加一个点击事件,并且所有子元素都能触发到这个事件(因为事件冒泡)
参数3:其实一般不会用,就是如果想事件触发时,自己给回调函数传一些值就写,这个参数也可以不写!
参数4:事件触发时的回调函数
总结:参数1和参数4是必须的,其他是可选的,如果你要用事件委托,请写上参数2
$('ul').on('click','li','我是数据',function (e) { console.log(e.data); console.log(e.target.innerHTML); // 或者 console.log(this.innerHTML); })
说明:
1.on这个方法的参数3可以通过回调函数里的e.data拿到(但一般不会用,大家了解一下有这么个东西即可)
2.在jQuery事件委托的回调函数里this和e.target是同一个东西,但是在JS里this和e.target不是同一个东西