JS的事件处理机制以及事件代理(事件委托)
一、先记个小知识点。cssText
cssText 本质:设置 HTML 元素的 style 属性值。
用法:document.getElementById("d1").style.cssText= "color:red; font-size:13px;";
cssText 返回值:在某些浏览器中(比如 Chrome),你给他赋什么值,它就返回什么值。在 IE 中则比较痛苦,它会格式化输出、会把属性大写、会改变属性顺序、会去掉最后一个分号,比如:
cssText的使用优势:样式一多,代码就很多;而且通过JS来覆写对象的样式是比较典型的一种销毁原样式并重建的过程,这种销毁和重建,都会增加浏览器的开销。语法为:obj.style.cssText=”样式”;这样就可以尽量避免页面reflow,提高页面性能。
但是,这样会有一个问题,会把原有的cssText清掉,比如原来的style中有’display:none;’,那么执行完上面的JS后,display就被删掉了。为了解决这个问题,可以采用cssText累加的方法:
Element.style.cssText += ‘width:100px;height:100px;top:100px;left:100px;’
注意:上面cssText累加的方法在IE中是无效的。解决办法是,可以在前面添加一个分号来解决这个问题:
Element.style.cssText += ‘;width:100px;height:100px;top:100px;left:100px;’
补充:如果前面有样式表文件写着 div {text-decoration:underline; },这个会被覆盖吗?不会!因为它不是直接作用于 HTML元素的 style 属性。
二、JS的事件处理机制
1、事件流:指从页面中接收事件的顺序,有冒泡流和捕获流。
2、DOM2级事件规定事件流包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。首先发生的是事件捕获,然后是实际的目标接收道事件,最后是冒泡阶段,可以在这个阶段对事件做出响应。
分析:实际的(text)元素在捕获阶段不会接收到事件,意味着在捕获阶段,事件从document到<body>再到<div>后就停止了。下一个阶段是“处于目标阶段”,于是事件在(text)上发生,并在事件处理中被看成是冒泡阶段的一部分。最后,冒泡阶段发生,事件又传播回文档。
3、事件处理程序
诸如click,load,mouseover都是事件的名字,而响应某个事件的函数就是事件处理程序(事件侦听器)。事件处理程序的名字以on开头,比如onclick.onmouseover等。
(1)HTML事件处理程序:某个元素支持的每种事件,都可以用一个相应事件处理程序同名的HTML特性来决定。
<input type="button" value="click" οnclick="alert('clicked')"/>
<input type="button" value="click" οnclick="alert(event.type)"/>
第二动态创建的函数中会有一个局部变量event,也就是事件对象。通过event变量,可以直接访问事件对象。
另外,这个动态创建的函数扩展作用域的方式如下:使用with
在这个函数内部,可以像访问局部变量一样访问document及该元素本身的成员。
function(){with(documnet){with(this){/元素属性值}}
}
(2)DOM0级事件处理程序
基于DOM0的事件,对于同一个dom节点而言,只能注册一个,后边注册的 同种事件 会覆盖之前注册的。利用这个原理我们可以解除事件,btn5.onclick=null;其中this就是绑定事件的那个元素;
这里添加的事件处理程序是在其依附的元素的作用域中运行。
DOM0级对每个事件只支持一个事件处理程序。
(3)DOM2级事件处理程序
布尔值是true,表示在捕获阶段调用事件处理程序。false时表示在事件冒泡阶段调用事件处理程序,一般建议在冒泡阶段使用,特殊情况才在捕获阶段;
使用DOM2级事件处理程序可以添加多个事件处理程序:
var btn2 = document.getElementById('btn2');
var handlers = function () { console.log(this.id); }; btn2.addEventListener('click',handlers,false); btn2.addEventListener("click",function(){alert("hello")},false);btn2.removeEventListener('click',handlers.false);
这里为按钮添加了两个事件处理程序,他们会按照添加他们的顺序触发。
(4)IE事件处理程序
IE用了attachEvent(),和detachEvent(),接收两个参数,事件名称和事件处理程序函数。由于IE8及以前只支持事件冒泡;通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。所以平时为了兼容更多的浏览器最好将事件添加到事件冒泡阶段。
var btn3 = document.getElementById('btn3');var handlers2=function(){console.log(this===window);//true,注意attachEvent()添加的事件处理程序运行在全局作用域中;};btn3.attachEvent('onclick',handlers2);
分析:attachEvent()的第一个参数是“onclick”DOM则是“click”
重点:在使用attachEvent()方法的情况下,事件处理程序会在全局作用域中运行。因此this等于window。
attachEvent()也可以为同一元素添加两个不同的事件处理程序。只是执行事件时以相反的顺序被触发。
(5)跨浏览器事件处理程序
为了以跨浏览器的方式处理事件,有两个方法,addHandler(),它的职责是视情况分别使用DOM0和DOM2或者IE方法来添加或删除事件。这个方法属于一个名叫EventUtil的对象,可以处理浏览器差异。这个方法接收三个参数。要操作的元素、事件名称、和事件处理程序函数。对应的方法是removeHandler()函数,它的职责是移除事件处理程序。默认采用DOM0级方法。
4 事件对象
触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含了所有与事件有关的信息,比如导致事件的元素target,事件的类型,及其他特定的相关信息。例如鼠标操作导致的事件对象中会包含鼠标的位置,单双击等,而键盘操作导致的事件对象会包含按下的键等信息;
(1)DOM中的事件对象
在通过HTML特性指定事件处理函数时,变量event中保存着event对象。event对象包含与创建它的特定事件的有关的属性和方法。触发的事件类型不一样,可用的属性和方法也不一样。
currentTarget | 只读 | 事件处理程序当前正在处理事件的那个元素 |
datail | 只读 | 与事件相关的细节 |
eventPhase | 只读 | 调用事件处理程序的阶段1 捕获阶段 2 处于目标 3 冒泡阶段 |
target | 只读 | 事件的目标 |
type | 只读 | 被触发的事件的类型 |
在事件处理程序内部,对象this始终等于currentTarget的值,target包含事件的实际目标。
ps:关于事件对象中的this,target,currentTarget,看个例子:(注:event.target不支持IE浏览器,应该用event.srcElement;还有 IE中通过attachment添加的事件是运行在全局作用域中的,this===window。
preventDefault() 阻止事件的默认行为,只有cancelabel属性的值设为true时,才可以使用preventDefalut. |
event.stopPropagation()可以阻止事件的传播.,取消进一步的事件冒泡或者捕获 |
(2)IE中的事件对象
要访问IE的event对象有几种不同的方式,取决于指定事件处理程序的方法。
比如使用DOM0级方法添加事件处理程序时,event对象作为window对象的一个属性存在,因此可以通过window.event来访问event对象。
var btn=document.getElementById("myBtn");
btn.οnclick=function(){var event=window.event;alert(event.type);
}
输出结果是click.
var btn=document.getElementById("myBtn");
btn.attachEvent("onclick",function(event){alert(event.type);
});
输出结果是click.
IE中event对象同样包含与创建它的事件相关的方法和属性。其中很多属性和方法都有对应的或者相关的DOM属性和方法。这些属性和方法会因为事类型的不同而不同。
srcElement | 只读 | 事件的目标(与DOM中target属性相同) |
type | 只读 | 被触发的事件的类型 |
cancelBubble | 读/写 | 默认值为false,设置为true可以取消事件冒泡(与DOM中stopPropagation()一样) |
returnValue | 读/写 | 默认为true,设置为false,就可以阻止默认行为。(与DOM中的preventDefault()一样) |
将returnValue设置为false,就可以阻止默认行为。 |
cancelBubble属性值为true,可以取消事件冒泡。 |
(3)跨浏览器的事件对象
虽然DOM和IE中对象不同,但基于二者之间的相似性依旧可以拿出跨浏览器的方案来。
IE中的event中的全部信息和方法都是类似的只是实现方式不同,可以用前面提到过的EventUtil对象来求同存异。
var EventUtil(){addHandler:function(element,type,handler){
//省略代码
},
getEvent:function(event){return event?event:window.event;},
getTarget:function(event){return event.target||event.srcElement;},
preventDefault:function(event){if(event.preventDefault){event.preventDefault();}else{event.returnValue=false;}},
removeHandler:function(element,type,handler){//省略代码
},
stopPropagation:function(event){if(event.stopPropagation){
event.preventDefault();}else{
event.cancelBubble=true; }
}
};
以上代码为EventUtil 添加了4个方法;getEvent(),返回event对象的引用。其它方法类似。
5 事件委托
因为冒泡机制,比如既然点击子元素,也会触发父元素的点击事件,那我们完全可以将子元素的事件要做的事写到父元素的事件里,也就是将子元素的事件处理程序写到父元素的事件处理程序中,这就是事件委托;利用事件委托,只指定一个事件处理程序,就可以管理某一个类型的所有事件;
通俗来说:事件委托是利用事件的冒泡原理来实现的,何为事件冒泡呢?就是事件从最深的节点开始,然后逐步向上传播事件,举个例子:页面上有这么一个节点树,div>ul>li>a;比如给最里面的a加一个click点击事件,那么这个事件就会一层一层的往外执行,执行顺序a>li>ul>div,有这样一个机制,那么我们给最外面的div加点击事件,那么里面的ul,li,a做点击事件的时候,都会冒泡到最外层的div上,所以都会触发,这就是事件委托,委托它们父级代为执行事件。
示例1:
<ul id="ul1"><li>111</li><li>222</li><li>333</li><li>444</li>
</ul>
实现点击li出现123.
传统方法:
window.onload = function(){var oUl = document.getElementById("ul1");var aLi = oUl.getElementsByTagName('li');for(var i=0;i<aLi.length;i++){aLi[i].onclick = function(){alert(123);}} }
使用事件委托:
window.onload = function(){var oUl = document.getElementById("ul1");oUl.onclick = function(){alert(123);}
}
这里用父级ul做事件处理,当li被点击时,由于冒泡原理,事件就会冒泡到ul上,因为ul上有点击事件,所以事件就会触发,当然,这里当点击ul的时候,也是会触发的,那么问题就来了,如果我想让事件代理的效果跟直接给节点的事件效果一样怎么办,比如说只有点击li才会触发???
示例2:
Event对象提供了一个属性叫target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom,当然,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement。
window.onload = function(){var oUl = document.getElementById("ul1");oUl.onclick = function(ev){var ev = ev || window.event;var target = ev.target || ev.srcElement;if(target.nodeName.toLowerCase() == 'li'){alert(123);alert(target.innerHTML);}}
}
这样,只有点击li才会触发事件。
示例3
对比下列两段代码实现:
window.onload = function(){var oBtn = document.getElementById("btn");var oUl = document.getElementById("ul1");var aLi = oUl.getElementsByTagName('li');var num = 4;//鼠标移入变红,移出变白for(var i=0; i<aLi.length;i++){aLi[i].onmouseover = function(){this.style.background = 'red';};aLi[i].onmouseout = function(){this.style.background = '#fff';}}//添加新节点oBtn.onclick = function(){num++;var oLi = document.createElement('li');oLi.innerHTML = 111*num;oUl.appendChild(oLi);};}
注意:这里添加的新节点并不会有事件处理程序。
window.onload = function(){var oBtn = document.getElementById("btn");var oUl = document.getElementById("ul1");var aLi = oUl.getElementsByTagName('li');var num = 4;//事件委托,添加的子元素也有事件oUl.onmouseover = function(ev){var ev = ev || window.event;var target = ev.target || ev.srcElement;if(target.nodeName.toLowerCase() == 'li'){target.style.background = "red";}};oUl.onmouseout = function(ev){var ev = ev || window.event;var target = ev.target || ev.srcElement;if(target.nodeName.toLowerCase() == 'li'){target.style.background = "#fff";}};//添加新节点oBtn.onclick = function(){num++;var oLi = document.createElement('li');oLi.innerHTML = 111*num;oUl.appendChild(oLi);};}
用事件委托的方式,新添加的子元素是带有事件效果的,我们可以发现,当用事件委托的时候,根本就不需要去遍历元素的子节点,只需要给父级元素添加事件就好了,其他的都是在js里面的执行,这样可以大大的减少dom操作,这才是事件委托的精髓所在。
示例4: 点击某一个 Li 标签时,将 Li 的背景色显示在 P 标签内,并将 P 标签中的文字颜色设置成 Li 的背景色
传统实现:
var list = document.querySelectorAll("li");for (var i = 0, len = list.length; i < len; i++) {list[i].onclick = function(e) {var t = e.target;var c = t.style.backgroundColor;var p = document.getElementsByClassName("color-picker")[0];p.innerHTML = c;p.style.color = c;}}
运用事件委托:
var ulist=document.getElementsByClassName("palette")[0];ulist.οnclick=function(ev){var ev = ev || window.event;var target = ev.target || ev.srcElement;if (target.nodeName.toLowerCase() === 'li') {var c = target.style.backgroundColor;var p = document.getElementsByClassName("color-picker")[0];p.innerHTML = c;p.style.color = c;}}
总结一下js委托相关的:
- 因为把事件绑定到了父节点上,因此省了绑定事件。就算后面新增的子节点也有了相关事件,删除部分子节点不用去销毁对应节点上绑定的事件
- 父节点是通过event.target来找对应的子节点的。(事件处理程序中的this值始终等于currentTarget的值,指向的是绑定到的那个元素)
JS的事件处理机制以及事件代理(事件委托)相关推荐
- JavaScript事件代理和委托
2019独角兽企业重金招聘Python工程师标准>>> 浏览器的事件冒泡 当事件发生后,这个事件就要开始传播.例如我们点击一个按钮时,就会产生一个click事件,但这个按钮本身不能处 ...
- 简诉事件代理(事件委托)及其优点
事件代理 不给每个子节点单独设置事件监听器,而是设置在其父节点上,然后利用冒泡原理设置每个子节点. 优点 1. 减少内存消耗和 dom 操作,提高性能. 在 JavaScript 中,添加到页面上的事 ...
- 记录JS event Loop机制及Node v8事件执行机制
转载于:https://www.cnblogs.com/liujiekun/p/11295536.html
- JS 事件代理和事件委托
目录 事件委托的概念理解 为什么要用事件委托 事件委托的原理: 事件代理(委托)实现 总结: 事件委托的概念理解 为什么叫事件委托?它还有一个名字叫事件代理. JavaScript高级程序设计上讲:事 ...
- a标签点击事件_DOM事件机制
前言 本文主要介绍DOM事件级别.DOM事件模型.事件流.事件代理和Event对象常见的应用,希望对你们有些帮助和启发! 一.DOM事件级别 DOM级别一共可以分为四个级别:DOM0级.DOM1级.D ...
- 用例子解释事件模型和事件代理
事件模型 事件传播模型 在说事件代理之前,先来说一下事件模型. 在浏览器开发的早期,面对事件触发模型的问题,所有的程序员都认为事件触发不应该是直接触发的,而应该在文档中有一个传播的过程,然而事件传播的 ...
- 事件链、事件代理、页面的渲染过程、style的操作、防抖与节流【DOM(四)】
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 目录 文章目录 1.事件链(冒泡目标捕获) (1)事件链原理 (2)阻止冒泡和默认事件 2.事件代理(面试笔试题重点) 3. ...
- 使用事件代理实现vue的手风琴组件
1.为什么要使用事件代理? 在项目中要做一个手风琴组件,需求是页面created事件中请求数据,以显示在列表中,加载数据时显示"正在加载",没有数据了就显示"没有更多数据 ...
- Event事件-1:addEventListener事件监听 / 事件冒泡事件捕获 / 事件委托 / preventDefault 阻止默认行为 / cancelBubble、stopPropa...
addEventListener 事件监听器 target.addEventListener(type, listener[, options|useCapture]) 添加事件监听 参数: ...
最新文章
- tensorflow 使用CPU而不使用GPU的问题解决
- 存clob为空的值_oracle clob 存储空间
- Python | raise...from... 是个什么操作?
- 洛谷 - P5192 Zoj3229 Shoot the Bullet|东方文花帖|【模板】有源汇上下界最大流(有源汇有上下界的最大流)
- python高级玩法_python pandas to_excel 高级玩法
- 仅完成部分的readprocessmemory或write_王者荣耀:三种李小龙获取方式!无需完成任务、28号即可兑换...
- Java讲课笔记08:数组
- lvs的调度算法有几种_LVS支持哪些调度算法?
- 排序sort,统计wc
- LeetCode 303. 区域和检索 - 数组不可变(动态规划)
- struts2.2跟jstl怎么整合
- 5.Docker技术入门与实战 --- 访问 Docker 仓库
- 智能优化算法:黏菌优化算法 - 附代码
- 定制性MES系统软件
- 思科交换机接口配置trunk_思科交换机虚拟串口配置VLAN Trunk的步骤
- java中产生0-100之间的随机整数
- 【ASE入门学习】ASE入门系列六——塞尔达扰动火焰
- QJsonObject 和 QByteArray 互转
- 国足0-2日本 出线仅存理论可能
- ISE中显示IP核的图形化界面