事件
事件:用户与浏览器特定的交互瞬间。
事件对象
在触发DOM上的事件时,会产生一个事件对象Event,它包含了与事件相关的所有信息。如:事件的类型、导致事件的元素(当前元素)、以及其它与事件相关的信息。如鼠标操作事件中,包含了鼠标的位置。键盘操作事件中,包含了是否敲击了键盘等。
DOM中的事件对象
兼容DOM的浏览器会将 事件对象Event 传入事件处理程序中,无论指定事件处理程序的方式是什么(DOM 0级、DOM 2级或HTML特性)。
<body>
<input id="input1" type="button" value="单击事件">
<script>
var input1 = document.getElementById("input1");
var event = function (e) {
alert(e.type);
};
input1.onclick = event; //click
input1.addEventListener("click", event, false); //click
</script>
</body>
弹出事件的类型为"click"事件。
对于HTML特性指定的事件处理程序:
<input id="input1" type="button" value="单击事件" οnclick="alert(event.type);">
在事件处理程序内部,对象this始终等于currentTarget(当前正在处理事件的那个元素),而target表示实际目标(即当前元素)。
当事件处理程序直接指定给input按钮时,
<body>
<input id="input1" type="button" value="单击事件" οnclick="alert(event.type);">
<script>
var input1 = document.getElementById("input1");
var event = function (e) {
alert(e.currentTarget === this); //true
alert(e.target === this); //true
};
input1.onclick = event; //true
</script>
</body>
这个例子是直接将事件处理程序指定给了当前元素(即input)。因此this、target、currentTarget这三个值是相等的。
如果
事件处理程序在按钮的父节点(如:document.body)中:
第一种情况,当我们
直接点击按钮时,
<body>
<input id="inpt1" type="button" width="100px" value="单击事件">
<script>
var inpt1 = document.querySelector("#inpt1");
//直接将事件处理程序指定给当前元素,即input按钮。
inpt1.onclick = function (e){
console.log(e.currentTarget === this); //true 处理事件的就是input按钮
console.log(this.type); // button
console.log(e.target === this); //true 事件的真正目标就是input按钮
};
//将事件处理程序指定给父级节点。
document.body.onclick = function (e){
console.log(e.currentTarget === document.body); // true 处理事件的是document.body
console.log(document.body === this); //true this等于currentTarget,此时为document.body
console.log(e.target === this); // false
console.log(e.target === inpt1); //true 事件的真正目标就是input按钮。
console.log(e.target === document.body); //false 事件的真正目标就是input按钮。不是document.body
};
</script>
</body>
效果:
我们可以看到,当直接点击按钮时,就会在input按钮和document.body上均触发onclick事件。当将事件处理程序指定给input按钮时,currentTarget、this、target的值都是当前元素(input按钮),处理事件的就是input按钮,事件的真正目标是input按钮。当将事件处理程序指定给其document.body时,this === currentTarget,此时处理事件的是document.body,而这个事件的真正目标还是input按钮。
第二种情况,当我们点击input按钮即点击body内部时:效果就不一样了
<body>
<input id="inpt1" type="button" width="100px" value="单击事件">
<div>撑开body</div>
<div>撑开body</div>
<script>
var inpt1 = document.querySelector("#inpt1");
//此时不触发
inpt1.onclick = function (e){
console.log(e.currentTarget === this);
console.log(this.type); // button
console.log(e.target === this);
};
//当点击body内部时,是body在处理事件,在document.body上触发
document.body.onclick = function (e){
console.log(e.currentTarget === document.body); // true 处理事件的是document.body
console.log(document.body === this); //true this等于currentTarget,此时为document.body
console.log(e.target === this); // true
console.log(e.target === inpt1); //false 事件的真正目标就是input按钮。
console.log(e.target === document.body); //true 事件的真正目标就是input按钮。不是document.body
};
</script>
</body>
效果:
此时,我们可以看到,当点击body内部除input按钮之外的地方时,就会在document.body上触发事件,此时,this===currentTarget===document.body,处理事件的是document.body,
注,此时事件的真正目标就是document.body,因为按钮没有触发事件,即e.target===document.body。
有时需要一个函数处理多个事件,因此可以利用 开关语句 为其添加事件。
var input1 = document.getElementById("input1");
var event = function (e) {
switch (e.type) {
case "click":
alert("clicked");
break;
case "mouseover":
e.target.style.color = "red"; //当前元素的颜色变成红色
breadk;
case "mouseout":
e.target.style.color = "blue";
}
};
input1.oclick = event;
input1.mouseover = event;
input1.mouseout = event;
先要判断事件的类型,再执行相应操作。
想要阻止事件的默认行为,可以使用preventDefult()方法来取消事件的默认行为。如:<a>超链接,点击超链接时,会默认跳转到指定的ULR页面。可以用perventDefult()来取消这种行为。
var a1 = document.getElementById("a1");
a1.onclick = function (e) {
e.preventDefult(); //阻止了a元素默认的跳转行为,点出不会跳转
};
当然,只有cancelable属性的值为true的情况下,才能使用preventDefult()来取消默认的行为。
stopPropagation()停止事件在DOM层次中传播,也就是取消进一步事件捕获或冒泡。
var a1 = document.getElementById("a1");
a1.onclick = function (e) {
alert("hi");
e.stopProPagation(); //取消事件继续在DOM层次中传播
};
//取消了传播,下列代码不会执行
document.body.onclick = function (e) {
alert("HI");
};
本来要出现两个警告框的,但通过stopPropagation()已经取消了事件继续在DOM层次中传播,onlcick事件也就不会传播(冒泡)到document.body这里来,因此不会触发注册到document.body上的事件处理程序了,document,body也就不会处理事件了。
eventPhase属性来确定当前事件在哪个阶段了,返回1表示处于捕获阶段,返回2表示事件处理程序处于目标对象上,返回3表示处于冒泡阶段。虽然“处于目标”发生在冒泡阶段,但会返回2。
<body>
<input id="inpt1" type="button" width="100px" value="单击事件">
<div>撑开body</div>
<div>撑开body</div>
<script>
var inpt1 = document.querySelector("#inpt1");
//事件处于目标上
inpt1.onclick = function (e){
console.log("返回:" + e.eventPhase + "表示事件处于目标上"); //2
};
//捕获阶段,布尔值为true
document.body.addEventListener("click", function (e) {
console.log("返回:" + e.eventPhase + "表示事件处于捕获阶段"); //3
}, true);
//冒泡阶段,也就是事件从目标向上传播了。
document.body.onclick = function (e) {
console.log("返回:" + e.eventPhase + "表示事件处于冒泡阶段"); //1
};
</script>
</body>
效果:
从这个例子中,我们发现,
当我们点出按钮时,会触发事件的三个阶段,即第一阶段捕获阶段,第二阶段事件处于目标上,第三阶段冒泡阶段。但
当我们点出body内部除了按钮之外的地方时,只会触发事件的二个阶段,分别是第一阶段捕获阶段,第三阶段冒泡阶段,这是因为此时没有点出按钮,那么按钮就不会触发事件处理程序,触发事件的是document.body,也就是说document.body在处理事件,即target===this==currentTarget===document.body。
IE中的事件对象
与访问DOM中的event对象不同的是,IE中访问event对象取决于指定 事件处理程序 的方式(DOM 0级、IE特有的、HTML特性)。
在IE中,event对象是未定义的,也就是undefined,返回的是window.event。
使用DOM 0级指定事件处理程序时,event对象是作为window对象的属性来看的。
var input1 = document.getElementById("input1");
input1.onclick = function () { //
var event = window.event; //全局对象的属性,所以不用传递参数。
alert(event.type); //click
};
可以这样理解:IE中的window.event相当于DOM中的event。且DOM 0级中,IE无需传入event作为参数。
而对于用attachEvent()方法指定事件处理程序,将event对象作为参数传入事件处理函数。
var input1 = document.getElementById("input1");
input1.attachEvent("onclick", function (event) { //传入参数
alert(event.type); //click
});
用这种方法指定事件处理程序,与在DOM中用addEventListener()指定一样,将event对象作为参数传入事件处理函数。
用HTML特性指定事件处理程序,可以通过event变量来访问event对象(与在DOM中用HTML特性指定一样)。
<input id="input1" type="button" value="单击事件" οnclick="alert(event.type);">
在IE中,用srcElement来表示事件的目标(与DOM中target一样)。
var input1 = document.getElementById("input1");
//DOM 0级方式
input1.onclick = function () { //在IE中使用DOM 0级,无需传入参数
alert(window.event.srcElement === this); //true
};
在前面已经提过:在IE中
通过attachEvent()添加事件,事件处理程序是在全局作用域运行的,也就是this对象指向window对象,属于全局对象。
var inpt1 = document.querySelector("#inpt1");
inpt1.attachEvent("onclick", function (e) {
alert(e.srcElement == this); //false 此时this对象指向window对象,事件目标日显不是全局对象。
});
var inpt1 = document.querySelector("#inpt1");
inpt1.attachEvent("onclick", function (e) {
alert(e.srcElement == inpt1); //true
});
在IE中,用returnValue来阻止事件的默认行为。与DOM中的perventDefult()方法一样的效果。不过,returnValue要赋值false才有效果。
var input1 = document.getElementById("input1");
//DOM 0级方式
input1.onclick = function () {
window.event.returnValue = false; //阻止IE中事件的默认行为
};
在IE中,用canceBubble来停止事件在DOM层次中继续传播。与DOM中的stopPropagation()方法一样的效果。不过,要给canceBubble赋值true才有效果。
当然,IE中只有事件冒泡,所以就是停止事件冒泡的继续传播。
var input1 = document.getElementById("input1");
//事件处于目标上阶段
input1.onclick = function () {
alert("hi");
window.event.canceBubble = true; //停止事件在DOM层次中继续传播,也就是停止事件冒泡。
};
//事件冒泡阶段
document.body.onclick = function () {
alert("HI");
};
本来要出现两个警告框的,但通过canceBubble已经取消了事件继续在DOM层次中传播,所以,也就不会传播到document.body这是来了。停止通过冒泡触发document.body中注册的事件处理程序。
跨浏览器的事件对象(event)
IE中的event与DOM的event不同,但IE中event对象的全部信息和方法在DOM中也有,所以可以根据它们的相似性做跨浏览器的方法来。将前面提到的EventUtil对象加强即可。
加强后的EventUtil对象:
var EventUtil = {
//添加事件句柄
addHandler : function (element, type, handler) {
//DOM 2级事件处理程序
if (element.addEventListener) {
element.addEventListener(type, handler, false);
} else if (element.attachEvent) { // IE事件处理程序
element.attachEvent("on" + type, handler);
} else {
//将当前元素看作对象,引用事件处理程序这个属性
element["on" + type] = handler; // DOM 0级事件处理程序
}
},
//获取event对象
getEvent : function (event) {
//利用条件赋值操作符来确定event,如果支持event,则返回DOM中event对象,如果不支持则返回IE中的event对象
return event ? event : window.event
},
//定义getDefult方法,用于阻止事件的默认行为
getDefult : function (event) {
if (event.preventDefult) {
event.preventDefult();
} else {
window.event.returnValue = false;
}
},
//定义getTarget方法,用于获取实际目标
getTarget : function (event) {
return event.target || event.srcElement;
}
//这是删除事件
removeHandler : function (element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else if (element.detachEvent) {
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null; //DOM 0级删除事件赋值null给事件处理程序名即可。
}
},
//定义stopPropagation方法,用于停止事件继续向DOM层次传播
stopPropagaton : function (event) {
if (event.stopPropagation) {
event.stopPropagation();
} else {
event.canceBubble = true;
}
}
};
在兼容DOM的浏览器中,返回事件对象Event
var input1 = document.getElementById("input1");
input1.onclick = function (event) {
event = EventUtil.getEvent(event); //这里返回的是event.
};
var input1 = document.getElementById("input1");
input1.onclick = function () {
event = EventUtil.getEvent(event); //这里返回的是window.event
};
返回事件目标。
var input1 = document.getElementById("input1");
input1.onclick = function (event) {
var target = EventUtil.getTarget(event);
};
想要返回什么,就可以用EventUtil对象调用其属性即可。是得到event对象,还是阻止默认行为,或者是停止事件的传播均可。
关于Event对象,再多说一句:
在兼容DOM中的Event参数,只是简单地传入和返回;而在IE中的Event参数是未定义的,也就是undefined,返回的是window.event。