1. 概念
事件是可以被JavaScript侦测到的行为,通俗的讲就是当用户与web页面进行某些交互时,解释器就会创建响应的event对象以描述事件信息。
如用户输入用户名和密码,点击登录。浏览器就向服务器发送登录请求。
常见的事件有:
- 用户点击页面上的内容
- 鼠标经过特定的元素
- 用户按下键盘的某个按键
- 用户滚动窗口或改变窗口大小
- 页面元素加载完成或加载失败
事件句柄(事件处理函数、事件监听函数):指用于响应某个事件而调用的函数。每一个事件均对应一个事件句柄,在程序执行时,将相应的函数或语句指定给事件句柄。则在事件发生时,浏览器便执行指定的函数或语句。
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<button id="clickme">点击我</button>
</body>
</html>
<script type="text/javascript">
var btn = document.getElementById("clickme");
var clickme = function () {
alert("我被点击了");
}
function click2() {
alert("我再次被点击了");
}
//句柄指定匿名函数
btn.addEventListener('click',function(){alert("什么事")}, false);//第3个参数默认false
//句柄指定变量
btn.addEventListener('click', clickme);
//句柄指定方法名
btn.addEventListener('click',click2);
btn.removeEventListener('click',clickme);
</script>
2. 事件定义
事件有三要素:事件对象、事件类型、事件句柄。
为特定事件定义监听函数有三种方式
2.1 直接在html中定义元素的事件相关属性
<button onclick="alert('hello')">按钮</button>
<body onclick="init()"></body>
缺点:违反了内容与行为相分离的原则,应尽可能少用
示例
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<!-- 以下面代码为例,button是事件对象,onclick是事件类型,alert是事件句柄 -->
<button onclick="alert('给你说了,不要点')">不要点</button>
<button onclick="noClick()">不要点</button>
</body>
</html>
<script type="text/javascript">
function noClick(){
alert("点什么点,啥都没有");
}
</script>
2.2 DOM0级事件
在JavaScript中为元素的事件相关属性赋值
document.getElementById("btn").onclick=function(){}
document.body.onload=init;
function init(){...}
缺点:此语法实现了“内容与行为相分离”,但元素仍只能绑定一个监听函数。
示例
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<button id="clickme">点击我</button>
</body>
</html>
<script type="text/javascript">
var btn = document.getElementById("clickme");
var clickme = function () {
alert("DOM0级事件绑定方式");
}
btn.onclick= clickme;
</script>
2.3 DOM2级事件
高级处理方式,一个事件可以绑定多个监听函数。但要注意浏览器兼容问题
方法 | 说明 | 示例 |
addEventListener | 功能:用于向指定元素添加事件句柄 语法:element.addEventListener(event, function, useCapture); 参数:
| btn.addEventListener("click", function(){}, false); //DOM btn.attachEvent("onclick", function(){}); //IE document.body.addEventListener("load", init); //DOM document.bbody.attachEvent("onalod", init);//IE function init(){...} |
removeEventListener | 功能:移除指定的句柄 语法:element.removeEventListener(event, function, useCapture) 参数
|
|
attachEvent | 功能:添加事件 语法:element.attachEvent(event, function) 参数:
|
|
detachEvent | 功能:移除事件 element.detachEvent(event, function) 参数:
|
示例
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<button id="dom">dom类型</button>
<button id="ie">IE浏览器</button>
</body>
</html>
<script type="text/javascript">
var btn_dom= document.getElementById("dom");
var btn_ie = document.getElementById("ie");
btn_dom.addEventListener("click",function(){sayName("chrome")});
btn_dom.addEventListener("click",sayHello);
btn_dom.removeEventListener("click", sayHello);
btn_ie.attachEvent('onclick',function(){sayName("IE")});
btn_ie.attachEvent('onclick',sayHello);
btn_ie.detachEvent('onclick', sayHello);
function sayName(name){
alert("我是"+name);
}
function sayHello(){
alert("hello");
}
</script>
2.4 封装一套代码兼容所有浏览器
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<button id="mybtn">惦记我</button>
</body>
</html>
<script type="text/javascript">
var EventUtil= {
addHandle: function(element, type, handle){
if(element.addEventListener){
element.addEventListener(type, handle, false);
}else if(element.attachEvent){
element.attachEvent("on"+type, handle);
}else{
element["on"+type]=null;
}
},
removeHandler: function(element, type, handle){
if(element.removeEventListener){
element.removeEventListener(type, handle, false);
}else if(element.detachEvent){
element.detachEvent("on"+type, handle);
}else{
element["on"+type]=null;
}
}
}
var btn = document.getElementById("mybtn");
var handle = function(){
alert("被击中了");
}
EventUtil.addHandle(btn,"click",handle);
EventUtil.removeHandler(btn,'click',handle);
</script>
2.5 总结:
方法 | 区别 |
html中定义 | html中写js代码。 缺点:强耦合,不利于代码复用 |
DOM0级事件 | 事件对象的属性添加绑定事件 优点:松耦合。html与js代码分离。 缺点:有且只能绑定一个事件 |
DOM2级事件 | 通过addEventListener函数绑定事件 优点:松耦合 支持同一DOM元素注册多个同类型事件 新增了捕获和冒泡的概念 |
// IE中this只系那个window
elment.attachEvent("onclick", function(){
alert(this === window);
})
3. 事件冒泡、事件捕获、事件委托
3.1 事件冒泡(顺序是从下往上)
事件冒泡:直系亲属书结构中,点击某个元素,由于冒泡作用,亲属树上的元素凡是添加点击事件的,都会被触发。子元素不会。
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div id="parent">
<div id="child" class="child">儿子
<div id="grandChild">孙子</div>
</div>
</div>
</body>
</html>
<script type="text/javascript">
//事件冒泡
document.getElementById('parent').addEventListener("click", function(){
alert("parent事件被处罚,"+this.id);;
},false);
document.getElementById('child').addEventListener("click", function(){
alert("child事件被触发,"+this.id);
},false);
document.getElementById('grandChild').addEventListener("click", function(){
alert("child事件被触发,"+this.id);
},false);
</script>
3.2 事件捕获(顺序是从上往下)
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div id="parent">
<div id="child" class="child">儿子
<div id="grandChild">孙子</div>
</div>
</div>
</body>
</html>
<script type="text/javascript">
//事件冒泡
document.getElementById('parent').addEventListener("click", function(){
alert("parent事件被处罚,"+this.id);;
},true);
document.getElementById('child').addEventListener("click", function(){
alert("child事件被触发,"+this.id);
},true);
document.getElementById('grandChild').addEventListener("click", function(){
alert("child事件被触发,"+this.id);
},true);
</script>
3.3 事件委托(委托给爸爸处理)
原理就是事件冒泡
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<ul id="ul">
<li id="one">item1</li>
<li id="two">item2</li>
<li id="three">item3</li>
<li id="four">item4</li>
<li id="five">item5</li>
<li id="six">item6</li>
</ul>
</body>
</html>
<script type="text/javascript">
var ul = document.getElementById("ul");
ul.addEventListener("click", function(event){
switch(event.target.id){
case "one":
alert(1);
break;
case "two":
alert(2);
break;
case "three":
alert(3);
break;
case "four":
alert(4);
break;
case "five":
alert(5);
break;
case "six":
alert(6);
break;
}
}, false);
</script>
4. event对象常用属性和方法
4.1 type:事件类型
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<button id="btn">点击我</button>
</body>
</html>
<script type="text/javascript">
var mybtn = document.getElementById("btn");
// type:告诉我们现在的事件类型是什么
var eventFun = function(event){
if(event.type == "click"){
alert("click事件被触发");
}else if(event.type == "mouseout"){
alert("mouseout事件被触发");
}
}
mybtn.addEventListener("click",eventFun);
mybtn.addEventListener("mouseout",eventFun);
</script>
4.2 target和currentTarget
target:当前点击的对象
currentTarget:事件绑定在谁身上,就指向谁
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div id="parent">他爹
<div id="child">儿子</div>
</div>
</body>
</html>
<script type="text/javascript">
var mybtn = document.getElementById("parent");
mybtn.addEventListener("click", function(event) {
console.log(event.target);
console.log(event.currentTarget);
});
</script>
4.3 preventDefault:阻止默认行为
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<a href="http://www.baidu.com" id="baidu">百度一下</a>
</body>
</html>
<script type="text/javascript">
var mybtn = document.getElementById("baidu");
mybtn.addEventListener("click", function(event) {
event.preventDefault(); //页面将不再跳转
});
</script>
4.4 stopPropagation取消事件捕获活着冒泡
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div id="parent">
<dic id="child">点击我</dic>
</div>
</body>
</html>
<script type="text/javascript">
var child = document.getElementById("child");
var parent = document.getElementById("parent");
parent.addEventListener("click", function(){
alert("parent");
})
child.addEventListener("click", function() {
alert("child");
event.stopPropagation();//阻止冒泡,父类不再执行
});
</script>
4.5 client、page、screen、offset
clientY:就是值浏览器顶部底边到鼠标位置,不计算滚动轴距离
pageY:就是指浏览器顶部底边到鼠标位置,但是它计算滚动轴的距离
screenY:屏幕顶部到鼠标位置
offset:当事件发生的时候,鼠标相对于事件源元素左上角的位置
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
div{height: 200px;background-color: lightblue;}
#gaodu{height: 2000px;background-color: lightgreen;}
</style>
</head>
<body>
<div></div>
<div id="gaodu"></div>
</body>
</html>
<script type="text/javascript">
var gaodu = document.getElementById("gaodu");
gaodu.addEventListener("click", function(event){
console.log("clientY:"+event.clientY+"; pageY:"+event.pageY+";screenY:"+event.screenY);
})
</script>
4.6 IE8及以下浏览器对象属性于方法
- type:获取事件类型,一样
- returnValue=false:阻止浏览器默认行为,同preventDefault
- cancelBubble=true:取消事件冒泡,同stopPropagation
- src.element:同target
<!DOCTYPE html>
<html>
<head>
<title>IE8及以下浏览器对象属性与方法</title>
<style type="text/css">
</style>
</head>
<body>
<button id="mybtn"></button>
<a id="baidu" href="http://www.baidu.com">百度一下</a>
<div id="parent"><div id="child">儿子</div></div>
<button id="target">target</button>
</body>
</html>
<script type="text/javascript">
var mybtn = document.geElementById("mybtn");
mybtn.attachEvent("onclick", function(event){
//事件类型
alert(event.type);//click
});
var baidu = document.geElementById("baidu");
baidu.attachEvent("onclick", function(event){
//阻止浏览器默认行为
event.returnValue = false;
});
var parent = document.geElementById("parent");
parent.attachEvent("onclick", function(event){
alert("parent被触发了");
});
var child = document.geElementById("child");
child.attachEvent("onclick", function(event){
alert("child被触发了");
//取消冒泡行为
event.cancelBubble=true;
});
var target = document.geElementById("target");
target.attachEvent("onclick",function(event){
console.log(event.srcElement);
});
</script>
4.7 event跨浏览器兼容
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div id="parent">
<div id="child">点击我</div>
</div>
<a href="http://www.baidu.com" id="baidu">百度一下</a>
</body>
</html>
<script type="text/javascript">
var EventUtil ={
addHandle: function(element, type, handle){
if(element.addEventListener){
element.addEventListener(type, handle, false);
}else if(element.attachEvent){
element.attachEvent("on"+type, handle);
}else{
element["on"+type]=null;
}
},
removeHandler: function(element, type, handle){
if(element.removeEventListener){
element.removeEventListener(type, handle, false);
}else if(element.detachEvent){
element.detachEvent("on"+type, handle);
}else{
element["on"+type]=null;
}
},
getTarget: function(event){
return event.target|| event.srcElement;
},
preventDefault:function(event){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue = false;
}
},
stopPropagation: function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble=true;
}
}
}
var parent = document.getElementById("parent");
EventUtil.addHandle(parent, "click", function(event){
alert("parent被触发了");
});
var child = document.getElementById("child");
EventUtil.addHandle(child, "click", function(event){
alert("child被触发了");
var target = EventUtil.getTarget(event);
console.log(target);
EventUtil.stopPropagation(event);
})
var baidu = document.getElementById("baidu");
EventUtil.addHandle(baidu, "click", function(event){
EventUtil.preventDefault(event);
});
</script>
5. 事件类型
5.1 常用事件
事件类型 | 说明 | 示例 |
UI事件 | ||
load | 当页面完全加载后在window上面触发 | //UI事件 |
unload | 用户从一个页面切换到另一个页面 | EventUtil.addhander(window, "unload", function(event){ |
resize | 窗口大小发生变化时触发 |
|
scroll | 页面滚动 |
|
焦点事件 | ||
focus | 获得焦点,不支持冒泡 | html代码 <div id="article"> <input type="text" id="comment"> </div> js代码 //input标签获得焦点,父元素不会执行,即不支持冒泡 var article = document.getElementById("article"); EventUtil.addHandler(article, "focus", function(event){ console.log("父元素获得焦点被触发"); }); EventUtil.addHandler(article, "blur", function(event){ console.log("父元素失去焦点被触发"); }); var comment = document.getElementById("comment"); EventUtil.addHandler(comment, "focus", function(event){ console.log("获得焦点时被触发"); }); EventUtil.addHandler(comment, "blur", function(event){ console.log("失去焦点时被触发"); }); |
blur | 失去焦点 | |
focusin | 同focus,但支持冒泡 | var article = document.getElementById("article"); EventUtil.addHandler(article, "focusin", function(event){ console.log("父元素获得焦点被触发"); }); EventUtil.addHandler(article, "focusout", function(event){ console.log("父元素失去焦点被触发"); }); var comment = document.getElementById("comment"); EventUtil.addHandler(comment, "focusin", function(event){ console.log("获得焦点时被触发"); }); EventUtil.addHandler(comment, "focusout", function(event){ console.log("失去焦点时被触发"); }); |
focusout | 同blur,但支持冒泡 | |
DOMFocusIn | 同focusIn | var article = document.getElementById("article"); EventUtil.addHandler(article, "DOMFocusIn", function(event){ console.log("父元素获得焦点被触发"); }); EventUtil.addHandler(article, "DOMFocusOut", function(event){ console.log("父元素失去焦点被触发"); }); var comment = document.getElementById("comment"); EventUtil.addHandler(comment, "DOMFocusIn", function(event){ console.log("获得焦点时被触发"); }); EventUtil.addHandler(comment, "DOMFocusOut", function(event){ console.log("失去焦点时被触发"); }); |
DOMFocusOut | 同focusOut | |
鼠标事件 | ||
click | 单击 | var parent = document.getElementById("parent"); EventUtil.addHandler(parent, "dbclick", function(event){ console.log("单击事件"); }); |
dbclick | 双击 | //未生效 var child = document.getElementById("child"); EventUtil.addHandler(child, "dbclick", function(event){ console.log("双击事件"); }); |
mousedown | 鼠标按下 | var parent = document.getElementById("parent"); EventUtil.addHandler(parent, "mousedown", function(event){ console.log("鼠标按下"); }); EventUtil.addHandler(parent, "mouseup", function(event){ console.log("鼠标松开"); }); EventUtil.addHandler(parent, "mousemove", function(event){ console.log("鼠标移动"); }); |
mouseup | 鼠标松开 | |
mousemove | 在目标元素上移动 | |
mouseover | 鼠标移入 | //html <div id="parent" style=" 200px;height: 100px; background-color: lightgreen;"> <div id="child" style=" 50px;height: 50px; background-color: lightblue;position: relative; top: 25px;left: 50px;">点击我</div> </div> //从目标元素进入子元素也会执行 var parent = document.getElementById("parent"); EventUtil.addHandler(parent, "mouseover", function(event){ console.log("鼠标移入"); }); EventUtil.addHandler(parent, "mouseout", function(event){ console.log("鼠标移出"); }); |
mouseout | 鼠标移出 | |
mouseenter | 进入元素 | //进入目标元素的子元素,不会执行 var parent = document.getElementById("parent"); EventUtil.addHandler(parent, "mouseenter", function(event){ console.log("鼠标移入"); }); EventUtil.addHandler(parent, "mouseleave", function(event){ console.log("鼠标移出"); }); |
mouseleave | 离开元素 |
5.2 获取点击时辅助键
var parent = document.getElementById("parent");
EventUtil.addHandler(parent, "click", function(){
var keys = new Array();
if(event.shiftKey){
keys.push("shift");
}
if(event.ctrlKey){
keys.push("ctrl");
}
if(event.altKey){
keys.push("alt");
}
if(event.metaKey){
keys.push("meta");
}
alert("keys:"+keys.join(","));
});
5.3 获取key属性
var parent = document.getElementById("parent");
EventUtil.addHandler(parent, "mousedown", function(event){
console.log(event.button);
});
高级浏览器:0-鼠标左键 1-鼠标滚轮 2-鼠标右键
IE8及以下:0-没有按下按钮 1-主鼠标按钮 2-次鼠标按钮 3-同时按下主次鼠标按钮 4-鼠标滚轮
5.4 键盘事件
keyCode:获取键码
charCode:获取ASCII码
data:获取输入的值
//按下某键时候触发,支持keyCode
var input_text = document.getElementById("comment");
EventUtil.addHandler(input_text, "keydown", function(event){
console.log("keydown keyCode:"+event.keyCode);
});
//释放某键时候触发,支持keyCode
EventUtil.addHandler(input_text, "keyup", function(event){
console.log("keyup keyCode:"+event.keyCode);
});
//字符键触发,keyCode不稳定,用charCode
EventUtil.addHandler(input_text, "keypress", function(event){
console.log("keypress keyCode:"+event.keyCode);
console.log("keypress charCode:"+event.charCode);
});
EventUtil.addHandler(input_text, "textInput", function(event){
console.log("输入的内容:"+event.data);
});
封装获取字符码的函数
//仅适用于keypress,其他如keydown,keyup不用
getCharCode:function(event){
if(typeof event.charCode == "number"){
return event.charCode;
}else{
return event.keyCode;
}
}
5.5 节点变更事件
方法 | 说明 | 操作 |
DOMNodeRemoved | 父元素删除子元素时就会触发 | //html <body> <ul id="mylist"> <li>item1</li> <li>item2</li> <li>item3</li> </ul> <div id="article"> <input type="text" id="comment"> </div> </body> //js代码 var mylist = document.getElementById("mylist"); EventUtil.addHandler(document, "DOMNodeRemoved", function(event){ console.log(1111); }); document.body.removeChild(mylist); |
DOMNodeInserted | 父元素添加子元素时就会触发 | var mylist = document.getElementById("mylist"); var item = document.createElement("li"); item.innerText="item4"; EventUtil.addHandler(mylist, "DOMNodeInserted", function(event){ console.log(1111); }); mylist.appendChild(item); |
DOMNodeInsertedIntoDocument | 在文档中添加之前被触发 | var item = document.createElement("li"); item.innerText="item4"; var mylist = document.getElementById("mylist"); EventUtil.addHandler(item, "DOMNodeInsertedIntoDocument", function(event){ console.log(1111); }); mylist.appendChild(item); |
DOMSubtreeModified | DOM发生任何变化都会被触发 | var child = document.getElementById("comment"); var parent = document.getElementById("article"); var mylist = document.getElementById("mylist"); var item = document.createElement("li"); item.innerText="item4"; EventUtil.addHandler(document, "DOMSubtreeModified", function(event){ console.log(1111); }); mylist.appendChild(item); document.body.removeChild(mylist); parent.removeChild(child); |
DOMNodeRemovedFromDocument | 文档中移除之前被触发 |
|
DOMContentLoaded | 在DOM树完成后就会触发 不会理会图像,javascript文件 css文件或其他资源是否已经下载 |
EventUtil.addHandler(document, "DOMContentLoaded", function(event){ console.log("直接执行"); }); |
readystatechange | 支持IE,firfox,Opera,提供DOM或元素加载过程。 但与load事件一起使用时,很难预料执行过程 1 document.readyState==uninitalized 尚未初始化 2 loading对象正在加载数据 3 interactive可以操作对象,但还没完全加载 4 对象已经加载完毕 |
EventUtil.addHandler(document, "readystatechange", function(event){ console.log(111); }); |
hashchange | 一定给window添加 判断#后面的值是否变化 |
EventUtil.addHandler(window, "hashchange", function(event){ console.log(event.oldURL+" "+event.newURL); }); //在URL后加#hello进行验证 |
5.6 移动端
方法 | 说明 | 操作 |
touchstart | 手指触摸屏幕时触发 | var mybtn = document.getElementById("mybtn"); EventUtil.addHandler(mybtn, "touchstart", function(event){ //当前触点的集合 console.log("当前触摸屏幕的触摸点数组:"+event.touches); //触点移动的信息 console.log("数组中只包含引起事件的触摸点信息"+event.changedTouches); //只在元素上的触点 console.log("只包含放在元素上的触摸信息:"+event.targetTouches); }); EventUtil.addHandler(mybtn, "touchmove", function(event){ console.log("111111"); }); EventUtil.addHandler(mybtn, "touchend", function(event){ console.log("111111"); }); |
touchmove | 手指在屏幕上滑动时触发 | |
touchend | 手指从屏幕上移开时触发 |
5.7 示例:使用事件委托,点击不同标签更换颜色
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>小项目</title>
<style>
/* 去掉默认样式 */
* {margin: 0;padding: 0;}
/* 大方框样式 */
#canvas {height: 500px;}
/* li的样式 */
ul li {list-style: none;float: left; 100px;height: 30px;line-height: 30px;text-align: center;color: white;border-radius: 10px;}
/* 分别设置每一个li的背景颜色 */
ul li:first-child {background: red;}
ul li:nth-child(2) {background: black;}
ul li:nth-child(3) {background: blue;}
ul li:nth-child(4) {background: yellow;}
ul li:last-child {background: green;}
</style>
</head>
<body>
<script>
// 先等其他标签元素加载完再执行script里的函数
window.onload = function () {
// 获取大方框和ul
var canvas = document.getElementById("canvas");
var btn = document.getElementById("btn");
// 采用事件委托方法,子元素li不设置事件,委托给父元素ul处理
btn.addEventListener("click", function (event) {
// 获取点击的目标
var that = event.target;
// 根据li里的内容是什么颜色就把大方框的背景改成什么颜色
if (that.innerHTML == "红色") {
canvas.style.backgroundColor = "red";
} else if (that.innerHTML == "黑色") {
canvas.style.backgroundColor = "black";
} else if (that.innerHTML == "蓝色") {
canvas.style.backgroundColor = "blue";
} else if (that.innerHTML == "黄色") {
canvas.style.backgroundColor = "yellow";
} else if (that.innerHTML == "绿色") {
canvas.style.backgroundColor = "green";
}
});
}
</script>
<div id="canvas"></div>
<ul id="btn">
<li>红色</li>
<li>黑色</li>
<li>蓝色</li>
<li>黄色</li>
<li>绿色</li>
</ul>
</body>
</html>