为什么要用事件委托???事件委托有什么好处???
一般来说,dom是需要有事件处理程序的,我们会直接给他事件处理程序就好了,那么如果是很多dom元素需要添加事件处理呢???
比如 100个li,每个li 都有相同的click点击事件,可能我们会有for循环的方法,来遍历所有li,然后为每个li添加绑定事件。
这么做毫无疑问的是对性能有很大的影响;
在js中,添加到页面上的事件处理程序的多少将直接关系到页面运行的整体性能,因为需要不断的与dom节点进行交互,访问dom次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间。
这就是性能优化,减少dom操作的原因;
如果采用事件委托,就会将所有的操作放到js程序里面,与dom的操作就只交互一次,这样减少了dom交互次数,性能就会提升;
事件委托原理:
事件委托就是利用事件冒泡原理实现的!
事件冒泡:就是事件从最深节点开始,然后逐步向上传播事件;
例:页面上有一个节点树,div > ul > li > a
比如给最里面的a 加一个click 事件,那么事件就会一层一层的往外执行,执行顺序 a > li > ul > div, 有这样一个机制,当我们给最外层的div 添加点击事件,那么里面的ul , li , a 做点击事件的时候,都会冒泡到最外层的div上,所以都会触发,这就是事件委托,委托他们父集代为执行事件;
业务需求:实现功能,点击td ,单元格变色;
html结构
- <!-- 事件绑定 -->
- <table id="myTable" border="1">
- <tr>
- <td>1</td>
- <td>2</td>
- <td>3</td>
- </tr>
- </table>
- window.onload = function(){
- var oTa = document.getElementById("myTable");
- var aTd = oTa.getElementsByTagName('td');
- for(var i=0;i<aTd.length;i++){
- aTd[i].onclick = function(){
- aTd[i].style.background = 'red';
- }
- }
- }
那我们用
事件委托的方式怎么来写呢,??
- window.onload = function(){
- var oTa= document.getElementById("myTable");
- oTa.onclick = function(){ //点击 table、td均可以alert(123)
- alert(123);
- }}
- <pre></pre>
- <pre></pre>
这里用父集做事件处理,当td被点击时,由于冒泡原理事件就会冒泡到table上,因此table上有点击事件,所以事件就会被触发;
当然单当点击table本身的时候也是会触发的;
如果我们只想让td触发而不想让table触发,怎么办呢???
Event对象提供了一个属性叫做
target,可以返回事件的目标节点,我们称之为事件源,也就是说,target就可以表示
当前事件操作的dom,但可能不是真正操作的dom,
存在兼容性问题:标准浏览器:event.target,IE浏览器:event.srcElement,
此时只是获取了当前节点的位置,但并不知道节点名称,这里我们用
nodeName来获取具体是什么标签名,这个返回值是一个大写的,判断时需要转换为小写;
这样改一下,就只有td会触发事件啦,且每次只执行一次dom操作,如果td很多的话,将大大减小dom的操作;
- window.onload = function(){
- var oTa = document.getElementById("myTable");
- oTa.onclick = function(e){
- var e = e || window.event; //处理兼容性
- var target = e.target || e.srcElement;
- target.nodeName.toLowerCase() == 'td' ? alert('我点中了table') :(target.style.background = 'red'); //三元运算符进行判断
- }
- }
上面的例子是说td点击都是产生同样的效果,要是每个td被点击的效果都不一样,那么事件委托还有用吗,???
- <!-- 事件绑定 -->
- <table id="myTable" border="1">
- <tr>
- <td id="add">增加</td>
- <td id="delete">删除</td>
- <td id="modfiy">修改</td>
- <td id="select">查找</td>
- </tr>
- </table>
非事件委托写法
- window.onload = function(){
- var Add = document.getElementById("add");
- var Delete = document.getElementById("delete");
- var Move = document.getElementById("move");
- var Select = document.getElementById("select");
- Add.onclick = function(){
- alert('添加');
- };
- Remove.onclick = function(){
- alert('删除');
- };
- Move.onclick = function(){
- alert('移动');
- };
- Select.onclick = function(){
- alert('选择');
- }
- }
如果用事件委托,能进行优化吗?
事件委托写法
用事件委托,只用一次dom操作,就能完成所有的效果,性能肯定比上面的好;
- window.onload = function(){
- var myTable = document.getElementById("myTable");
- myTable.onclick = function (ev) {
- var ev = ev || window.event;
- var target = ev.target || ev.srcElement;
- if(target.nodeName.toLocaleLowerCase() == 'table'){
- switch(target.id){
- case 'add' :
- alert('添加');
- break;
- case 'remove' :
- alert('删除');
- break;
- case 'move' :
- alert('移动');
- break;
- case 'select' :
- alert('选择');
- break;
- }
- }
- }
- }
前面讲的都是document
加载完成后现有的dom节点的操作;
那么如果是新增的节点,新增的节点会有事件吗???也就是说,新来的一个员工,他能收到快递吗????
看一下正常添加节点的方法:
- <input type="button" name="" id="btn" value="添加" />
- <ul id="ul1">
- <li>111</li>
- <li>222</li>
- <li>333</li>
- <li>444</li>
- </ul>
解决办法:
一般情况下,我们会用一个函数把那个for循环包起来,然后再在点击事件里调用这个函数,这样也能实现目的,功能虽然实现了,但无疑又增加了dom操作,
用
事件委托的方式如何做呢???
- <script type="text/javascript">
- window.onload = function(){
- var ul1 = document.getElementById('ul1');
- var li = document.getElementsByTagName('li');
- var btn = document.getElementById('btn');
- var num = 1;
- // for(var i=0;i<li.length;i++){
- // li[i].onclick = function(){
- // alert(this.innerHTML);
- // }
- // num++;
- // }
- ul1.onclick = function(e){
- var e = e || window.event;
- var target = e.target || event.srcElement;
- console.log(target.nodeName);
- target.nodeName.toLowerCase() == 'li' ? alert(target.innerHTML) : alert("您点中了ul标签");
- }
- btn.onclick = function(){
- var oLi = document.createElement('li');
- oLi.innerHTML = 111*num;
- ul1.appendChild(oLi);
- }
- }
我们可以发现,当用事件委托的时候,根本不需要遍历元素的子节点,只需要给父级元素添加事件就好了,
其他的代码都是在js里面执行的,这样可以大大减少dom操作,
这就是我理解的事件委托的精髓;
jQuery事件委托:
我直接上code了,jquery的操作是如此的简单,一个on全搞定!!!
- <!-- 事件绑定 -->
- <table id="myTable" border="1">
- <tr>
- <td>111</td>
- <td>222</td>
- <td>333</td>
- <td>444</td>
- </tr>
- </table>
- <script src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script>
- <script type="text/javascript">
- $(function(){
- $("#myTable td").click(function(){
- $(this).html(); //普通写法,如果有100个td元素,就要绑定100次事件,十分浪费性能
- })
- $("#myTable").click(function(e){ //这种点击方式怎么排除父元素????
- console.log(e.target);
- var $clicked = $(e.target); //e.target 捕捉到触发的元素
- console.log(e.target.nodeName); //结果为大写,用toLowerCase() 转换为小写
- e.target.nodeName.toLowerCase() == 'table' ? alert('我点中table了') : $clicked.html(); //排除父元素的点击效果
- })
- // jQuery1.7的on绑定方法,替代了以往的bind、live等方法,内部自动含有事件委托机制
- // $(selector).on(event,childSelector,data,function,map) on方法的语法
- // $(selector).on(event,childSelector,function(){}) on方法语法,绑定在父元素上 事件,子选择器,回调函数
- $("#myTable").on('click','td',function(){
- $(this).html();
- })
- })
- </script>