1、事件概述
(1)事件
事件:可被理解为是JavaScript侦测到的行为。
举例:这些行为指的就是页面的加载、鼠标单击页面、鼠标滑过某个区域等。
(2)事件处理程序
事件处理程序:指的就是JavaScript为响应用户行为所执行的程序代码。
举例:用户单击button按钮,这个行为就会被JavaScript中的click事件侦测到;然后让其自动执行,为click事件编写的程序代码,如在控制台输出“按钮被单击”。
(3)事件驱动式
事件驱动式:是指在Web页面中JavaScript的事件,侦测到的用户行为,并执行相应的事件处理程序的过程。
举例:鼠标移入文本区域,文本区域变色这一过程。
(4)事件流
事件流:事件发生时,会在发生事件的元素节点与DOM树根节点之间按照特定的顺序进行传播,这个事件传播的过程就是事件流。
事件流的传播顺序解决方案:网景(Netscape)提出了“事件捕获方式”、微软(Microsoft)提出了“事件冒泡方式”。
(5)事件捕获方式(网景)
事件流传播的顺序应该是从DOM树的根节点到发生事件的元素节点。
(6)事件冒泡方式(微软)
事件流传播的顺序应该是从发生事件的元素节点到DOM树的根节点。
JavaScript事件冒泡(Bubble):所谓冒泡指的就是事件的向上转导,当后代上的元素被触发时,其祖先元素相同事件也会被触发。在大部分情况中冒泡都是有用的,如果不希望冒泡可以通过事件对象取消冒泡 event.cancelBubble=true.
(7)W3C的解决方案
规定事件发生后,先实现事件捕获,但不会对事件进行处理。
接着进行到目标阶段,执行当前元素对象的事件处理程序,但它会被看成是冒泡阶段的一部分。
最后实现事件的冒泡,逐级对事件进行处理。
2、事件的绑定方式
(1)行内绑定式
事件的行内绑定式是通过HTML标签的属性设置实现的。
标签名可以是任意的HTML标签,如<div>标签、<button>标签等;
事件是由on和事件名称组成的一个HTML属性,如单击事件对应的属性名为onclick;
事件的处理程序指的是JavaScript代码,如匿名函数等。
(2)动态绑定式
在JavaScript代码中,为需要事件处理的DOM元素对象,添加事件与事件处理程序。
事件的处理程序一般都是匿名函数或有名的函数。
行内绑定式与动态绑定式的异同
不同点:
实现语法不同,前者通过HTML标签的属性设置,后者在JS中处理DOM对象。
事件处理程序中关键字this的指向也不同。前者指向window对象,后者指向当前正在操作的DOM元素对象。
相同点:
同一个DOM对象的同一个事件只能有一个事件处理程序。
<!-- 事件流描述的是 从页面中接受事件的顺序 -->
<!-- 事件流包含三个阶段:
事件捕获阶段(自上而下,从父到子)、
处于目标阶段、
事件冒泡阶段(自下而上,从子到父) -->
<style>
#one{width: 1200px;height: 200px;background: pink;}
#two{width: 1200px;height: 200px;background: greenyellow;}
#three{width: 1200px;height: 200px;background: yellow;}
</style>
<body>
<!-- 行内式 -->
<div id="one" onclick="a()"></div>
<div id="two" onclick="alert('two')"></div>
<div id="three"></div>
<script>
var num = 'one'
var a = function(){
console.log(this.num);//this--->window
}
// 动态绑定 ---》DOM元素对象.事件 = 事件的处理程序;
var three = document.getElementById('three');
three.onclick = function(){
// 元素样式 this 当前元素对象three
this.style.background='plum'
}
</script>
</body>
(3)事件监听式
同一个DOM对象的同一个事件只能有一个事件处理程序,即可给同一个DOM对象的同一个事件添加多个事件处理程序。
实现的方式:具有兼容性问题,一类是早期版本的IE浏览器(如IE6~8),一类遵循W3C标准的浏览器(以下简称标准浏览器)。
事件监听式的两种不同实现方式的区别
实现的语法不同。
事件处理程序的触发顺序也不相同,同一个对象的相同事件,早期版本IE浏览器的事件处理程序按照添加的顺序倒序执行。而标准浏览器的事件处理程序按照添加顺序正序执行。
事件监听的处理程序是一个有名的函数时,可移出事件监听。
DOM对象.detachEvent(type, callback); // 早期版本IE浏览器
DOM对象.removeEventListener(type, callback); // 标准浏览器
参数type值的设置要与添加事件监听的事件类型相同,参数callback表示事件处理程序的名称,即函数名。
<!-- 早期版本IE 事件处理程序,按照添加顺序的倒叙执行 -->
<!-- <div id="t">test</div>
<script>
var obj = document.getElementById('t');
// 添加第一个事件处理程序
obj.attachEvent('onclick',function(){
console.log('one')
})
// 添加第二个事件处理程序
obj.attachEvent('onclick',function(){
console.log('two')
})
</script> -->
<!-- 标准浏览器 事件处理程序,按照添加顺序执行
DOM对象.addEventListener(type, callback, [capture]); -->
<div id="t">test</div>
<script>
var obj = document.getElementById('t');
// 添加第一个事件处理程序
obj.addEventListener('click',function(){
console.log('one')
})
// 添加第二个事件处理程序
obj.addEventListener('click',function(){
console.log('two')
})
</script>
3、事件对象
(1)获取事件对象
获取事件对象的方式
早期IE浏览器(IE6~8):window.event
标准浏览器:会将一个event对象直接传入到事件处理程序中。
事件对象的作用:这个对象中包含着所有与事件相关的信息,包括发生事件的DOM元素、事件的类型以及其他与特定事件相关的信息。
<button id="btn">获取event对象</button>
<script>
var btn = document.getElementById('btn');
btn.onclick = function(e) {
var event = e || window.event; // 获取事件对象的兼容处理
console.log(event);
};
</script>
(2)常用属性和方法
<button id="btn" class="btnClass">获取event对象</button>
<script>
var btn = document.getElementById('btn');
btn.onclick = function(e) {
var obj = event.target || window.event.srcElement; // 处理兼容问题:触发此事件的元素对象
console.log(obj.nodeName); // 获取元素节点名,如:BUTTON
console.log(obj.id); // 获取元素的id值,如:btn
console.log(obj.className); // 获取元素的class名,如:btnClass
console.log(obj.innerText); // 获取元素的文本值,如:获取event对象
};
</script>
<style>
#red{background:red;width:120px;height:120px;}
#green{background:green;width:80px;height:80px;}
#yellow{background:yellow;width:40px;height:40px;}
</style>
<div id="red">
<div id="green">
<div id="yellow"></div>
</div>
</div>
<script>
// 分别获取互相嵌套的div元素
var red = document.getElementById('red');
var green = document.getElementById('green');
var yellow = document.getElementById('yellow');
// 分别为互相嵌套的div元素添加单击事件
red.onclick = function() {
console.log('red');
};
green.onclick = function() {
console.log('green');
};
yellow.onclick = function() {
console.log('yellow');
};
</script>
<script>
red.onclick = function(e) {
// func(e);
console.log('red');
};
green.onclick = function(e) {
// func(e);
console.log('green');
};
yellow.onclick = function(e) {
// func(e);
console.log('yellow');
};
function func(e) {
if (window.event) { // 早期版本的的浏览器
window.event.cancelBubble = true;
} else { //标准浏览器
e.stopPropagation();
}
}
</script>
案例:取消默认行为
HTML中有些元素标签拥有一些特殊的行为。例如,单击<a>标签后,会自动跳转到href属性指定的URL链接;单击表单的submit按钮后,会自动将表单数据提交到指定的服务器端页面处理。因此,我们把标签具有的这种行为称为默认行为。
<a id="test" href="http://www.example.com">默认链接</a>
<script>
document.getElementById('test').onclick = function(e) {
if (window.event) { // 早期版本IE浏览器
window.event.returnValue = false;
} else { //标准浏览器
e.preventDefault();
}
};
</script>
案例:缓动的小球
缓动的实现原理:通过定时器连续的修改当前DOM元素的某个样式值,达到一个动态的特效。
缓动动画公式:计算每次缓动的步长–step = ( target - leader ) / 10
计算下次的起始点------leader = leader + step
target表示目标点。
leader表示起始点。
step表示从起始点到目标点每次缓动的步长。而缓动特效在实现时,随着距离target越来越近,step步长值逐渐变小,从而达到非常逼真的缓动效果。
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>缓动的小球</title>
<style>
#box{position:absolute;width:120px;height:120px;line-height:120px;text-align:center;background:red;color:#fff;border-radius:60px;}
</style>
</head>
<body>
<div id="box">点我啊,跑!</div>
<script>
function animate(obj, option) {
clearInterval(obj.timer); // 防止多次触发事件,重复开启定时器
obj.timer = setInterval(function() {
var flag = true; // 元素对象移动的标志,true表示已完成
for (var k in option) {
var leader = parseInt(getStyle(obj, k)) || 0; // 获取指定元素当前属性值
var target = option[k]; // 获取指定元素目标属性值
var step = (target - leader) / 10; // 计算每次移动的步长
step = step > 0 ? Math.ceil(step) : Math.floor(step);
leader = leader + step; // 计算属性值
obj.style[k] = leader + 'px'; // 设置属性值
if (leader != target) { // 判断是否完成移动
flag = false;
}
}
if (flag) { // 移动完成后清除定时器
clearInterval(obj.timer);
}
}, 15);
}
function getStyle(obj, attr) {
if (window.getComputedStyle) { // 标准浏览器
return window.getComputedStyle(obj, null)[attr];
} else { // 早期版本IE的浏览器,IE6~8
return obj.currentStyle[attr];
}
}
var obj = document.getElementById('box');
obj.onclick = function() {
animate(obj, {'left': 200, 'top': 50});
};
</script>
</body>
</html>
4、事件分类
(1)页面事件
load事件:用于body内所有标签都加载完成后才触发,又因其无需考虑页面加载顺序的问题,常常在开发具体功能时添加。
unload事件:用于页面关闭时触发,经常用于清除引用避免内存泄漏时使用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// load 指页面包含图片等文件在内的所有元素都加载完毕后执行的事件
// ready事件 文档结构加载完成(不包含图片等非文字媒体文件)
// onload
// unload 关闭网页的时候
window.onload = function() {
document.getElementById('box').onclick = function(){
console.log('点击')
}
}
</script>
<div id="box">测试</div>
</body>
</html>
(2)焦点事件
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>验证用户名和密码是否为空</title>
<style>
body{background:#ddd;}
.box{background:#fff;padding:20px 30px;width:400px;margin: 0 auto;text-align:center;}
.btn{width:180px;height:40px;background:#3388ff;border:1px solid #fff;color:#fff;font-size:14px;}
.ipt{width:260px;padding:4px 2px;}
.tips{width:440px;height:30px;margin:5px auto;background:#fff;color:red;border:1px solid #ccc;display:none;line-height:30px;padding-left:20px;font-size:13px;}
</style>
</head>
<body>
<div id="tips" class="tips"></div>
<div class="box">
<p><label>用户名:<input id="user" class="ipt" type="text"></label></p>
<p><label>密 码:<input id="pass" class="ipt" type="password"></label></p>
<p><button id="login" class="btn">登录</button></p>
</div>
<script>
window.onload = function() {
addBlur($('user')); // 检测id为user的元素失去焦点后,value值是否为空
addBlur($('pass')); // 检测id为pass的元素失去焦点后,value值是否为空
};
function $(obj) { // 根据id获取指定元素
return document.getElementById(obj);
}
function addBlur(obj) { // 为指定元素添加失去焦点事件
obj.onblur = function() {
isEmpty(this);
};
}
function isEmpty(obj) { // 检测表单是否为空
if (obj.value === '') {
$('tips').style.display = 'block';
$('tips').innerHTML = '注意:输入内容不能为空! ';
} else {
$('tips').style.display = 'none';
}
}
</script>
</body>
</html>
(3)鼠标事件
;
注意:IE6~8浏览器中不兼容pageX和pageY属性。因此,项目开发时需要对IE6~8浏览器进行兼容处理。
var pageX = event.pageX || event.clientX + document.documentElement.scrollLeft;
var pageY = event.pageY || event.clientY + document.documentElement.scrollTop;
鼠标在文档中的坐标等于鼠标在当前窗口中的坐标加上滚动条卷去的文本长度。
案例:鼠标拖曳特效
盒子的位置(left和top值)= 鼠标的位置(left和top值)- 鼠标按下时与盒子之间的距离(left和top值)。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>鼠标拖动</title>
<style>
body{margin:0}
.box{width:400px;height:300px;border:5px solid #eee;box-shadow:2px 2px 2px 2px #666;
position:absolute;top:30%;left:30%}
.hd{width:100%;height:25px;background-color:#7c9299;border-bottom:1px solid #369;
line-height:25px;color:#fff;cursor:move}
#box_close{float:right;cursor:pointer}
</style>
</head>
<body>
<div id="box" class="box">
<div id="drop" class="hd">
注册信息 (可以拖拽)
<span id="box_close">【关闭】</span>
</div>
<div class="bd"></div>
</div>
<script>
// 获取被拖动的盒子和拖动条
var box = document.getElementById('box');
var drop = document.getElementById('drop');
drop.onmousedown = function(event) { // 鼠标在拖动条上 按下 可拖动盒子
var event = event || window.event;
// 获取鼠标按下时的位置
var pageX = event.pageX || event.clientX + document.documentElement.scrollLeft;
var pageY = event.pageY || event.clientY + document.documentElement.scrollTop;
// 计算鼠标按下的位置 距 盒子的位置
var spaceX = pageX - box.offsetLeft;
var spaceY = pageY - box.offsetTop;
document.onmousemove = function(event) { // 鼠标移动的时候 获取鼠标的位置 整个盒子跟着鼠标的位置走
var event = event || window.event;
// 获取移动后鼠标的位置
var pageX = event.pageX || event.clientX + document.documentElement.scrollLeft;
var pageY = event.pageY || event.clientY + document.documentElement.scrollTop;
// 计算并设置盒子移动后的位置
box.style.left = pageX - spaceX + 'px';
box.style.top = pageY - spaceY + 'px';
// 清理鼠标拖动时,选中拖动条中文字的情况
// JS清除选择内容的方法
// getSelection() 方法表示用户选择的文本范围或光标的当前位置。
// 在谷歌、火狐、Opera浏览器中,window对象有getSelection属性,而在IE中没有。
// IE中的document对象有selection属性,因此清除页面中选择的内容也就可以得到解决。
// 在谷歌、火狐、Opera浏览器中我们可以很容易的通过 window.getSelection().removeAllRanges() 清除选中的内容,
// 在IE中我们可以通过 document.selection.empty() 来清除选中的内容。
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
};
};
document.onmouseup = function() {// 释放鼠标按键时 取消盒子的移动
document.onmousemove = null;
};
</script>
</body>
</html>
案例:鼠标点击位置
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>显示鼠标单击位置</title>
<style>
.mouse{position:absolute;background:#ffd965;width:40px;height:40px;border-radius:20px;}
</style>
</head>
<body>
<div id="mouse" class="mouse"></div>
<script>
var mouse = document.getElementById('mouse');
//需求:鼠标在页面上单击时,获取单击时的位置,并显示一个小圆点
document.onclick = function(event) {
// 获取事件对象的兼容处理
var event = event || window.event;
// 鼠标在页面上的位置
var pageX = event.pageX || event.clientX + document.documentElement.scrollLeft;
var pageY = event.pageY || event.clientY + document.documentElement.scrollTop;
// 计算<div>应该显示的位置
var targetX = pageX - mouse.offsetWidth / 2;
var targetY = pageY - mouse.offsetHeight / 2;
// 在鼠标单击的位置显示<div>
mouse.style.display = 'block';
mouse.style.left = targetX + 'px';
mouse.style.top = targetY + 'px';
};
</script>
</body>
</html>
(4)键盘事件
键盘事件是指用户在使用键盘时触发的事件。
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>按Enter键切换</title>
</head>
<body>
<p>用户姓名:<input type="text"></p>
<p>电子邮箱:<input type="text"></p>
<p>手机号码:<input type="text"></p>
<p>个人描述:<input type="text"></p>
<script>
var inputs = document.getElementsByTagName('input');
for (var i = 0; i < inputs.length; ++i) {
inputs[i].onkeydown = function(e) {
// 获取事件对象的兼容处理
var e = event || window.event;
// 判断按下的是不是回车,如果是,让下一个input获取焦点
if (e.keyCode === 13) {
// 遍历所有input框,找到当前input的下标
for (var i = 0; i < inputs.length; ++i) {
if (inputs[i] === this) {
// 计算下一个input元素的下标
var index = i + 1 >= inputs.length ? 0 : i + 1;
break;
}
}
// 如果下一个input还是文本框,则获取键盘焦点
if (inputs[index].type === 'text') {
inputs[index].focus(); // 触发focus事件
}
}
};
}
</script>
</body>
</html>
keypress事件保存的按键值是ASCII码,
keydown和keyup事件保存的按键值是虚拟键码。
(5)表单事件
表单事件指的是对Web表单操作时发生的事件。
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>表单事件</title>
</head>
<body>
<form id="register">
<label>用户名:<input id="user" type="text"></label>
<input type="submit" value="提交">
<input type="reset" value="重置">
</form>
<script>
// 获取表单和需要验证的元素对象
var regist = document.getElementById('register');
var user = document.getElementById('user');
regist.onsubmit = function(event) { // 为表单添加submit事件
// 获取事件对象、输出当前事件类型
var event = event || window.event;
console.log(event.type);
// 判断表单元素内容是否为空,若为空,则返回false,否则返回true
return user.value ? true : false;
};
regist.onreset = function (event) { // 为表单添加reset事件
// 获取事件对象、输出当前事件类型
var event = event || window.event;
console.log(event.type);
// 判断是否确认重置,按“确定”则返回true,按“取消”返回false
return confirm('请确认是否要重置信息,重置后表单填写的内容将全部清空');
};
</script>
</body>
</html>
案例:绑定事件
<body>
点击‘执行’按钮,把文本框1的内容赋给文本框2
<br>
点击‘解除’按钮,再点击‘执行’按钮,将不再把文本框1的内容赋给文本框2
<br>
文本框1:<input type="text" id="fone" placeholder="输入文本">
<br>
文本框2:<input type="text" id="ftwo" placeholder="文本">
<br>
<input type="button" value="执行" id="bone">
<input type="button" value="解除" id="btwo">
<script>
// 1、获取添加点击事件的元素
var bone = document.getElementById('bone');
var btwo = document.getElementById('btwo');
// 2、为元素绑定事件处理程序
bone.addEventListener('click',fun1)
// btwo.addEventListener('click',fun2)
btwo.addEventListener('click',function(){
bone.removeEventListener('click',fun1)
})
// 定义函数,把文本框1的值给到文本框2
function fun1(){
document.getElementById('ftwo').value = document.getElementById('fone').value
}
function fun2(){
bone.removeEventListener('click',fun1)
}
</script>
</body>