一、先记个小知识点。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到
再到3、事件处理程序
诸如click,load,mouseover都是事件的名字,而响应某个事件的函数就是事件处理程序(事件侦听器)。事件处理程序的名字以on开头,比如onclick.onmouseover等。
(1)HTML事件处理程序:某个元素支持的每种事件,都可以用一个相应事件处理程序同名的HTML特性来决定。
第二动态创建的函数中会有一个局部变量event,也就是事件对象。通过event变量,可以直接访问事件对象。
另外,这个动态创建的函数扩展作用域的方式如下:使用with
在这个函数内部,可以像访问局部变量一样访问document及该元素本身的成员。
function(){
with(documnet){
with(this){
/元素属性值
}
}
}
(2)DOM0级事件处理程序
基于DOM0的事件,对于同一个dom节点而言,只能注册一个,后边注册的 同种事件 会覆盖之前注册的。利用这个原理我们可以解除事件,btn5.onclick=null;其中this就是绑定事件的那个元素;
这里添加的事件处理程序是在其依附的元素的作用域中运行。
DOM0级对每个事件只支持一个事件处理程序。
(3)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 | 只读 | 被触发的事件的类型 |
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:
- 111
- 222
- 333
- 444
实现点击li出现123.
传统方法:
window.onload = function(){ var oUl = document.getElementById("ul1"); var aLi = oUl.getElementsByTagName('li'); for(var i=0;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
注意:这里添加的新节点并不会有事件处理程序。
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;
}
}