- 事件
- 事件模型
- 事件列表
- 单击和双击
- 解决单击和双击冲突
- mouse事件
- 表单事件
- 事件对象
- target
- 鼠标事件对象
- 鼠标按钮 button
- 兼容IE
- preventDefault())
- 自定义右键菜单
- 事件传递
- 事件冒泡
- 阻止事件冒泡
- 事件委托
- DOM2 事件监听
- addEventListener
- removeEventListener
事件
所谓事件,浏览器与用户操作进行交互
- 用户点击浏览器某个位置,这个行为会产生一个点击事件.
- 浏览器主动为用户播放轮播图,这也属于一个事件
- 用户滑动浏览器,页面产生滚动,这是滚动事件
在JS中事件有非常多,我们再这章里学习一些主要的事件
事件模型
- 内联模型
- 脚本模型
- DOM2模型
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button onclick="test1()">内联模型</button>
<button id="btn">脚本模型</button>
<button id="dom2">dom2模型</button>
</body>
<script>
function test1(){
console.log("1");
}
//脚本模型
var btn = document.getElementById("btn");
btn.onclick = function(){
console.log("2");
}
var dom2 = document.getElementById("dom2");
dom2.addEventListener("click",function(){
console.log("3");
},false)
</script>
</html>
事件列表
事件名称 | 作用 |
---|---|
onload | 文档或者图片加载完成之后触发 |
onclick | 点击元素的时候触发 |
ondbclick | 双击元素的时候触发 |
onblur | 当焦点从元素上移开时触发 |
onfocus | 当元素获取到焦点的时候触发 |
onchange | 改变一个元素的值的时候触发,一般都是form表单元素 |
ondragdrop | 拖拽并放下的时候触发 |
onkeydown | 当按键被按下的时候触发 |
onkeypress | 当按键被按下的时候触发 |
onkeyup | 当按键被松开时触发 |
onmouseout | 当图标移出时触发 |
onmouseover | 当鼠标划过的时候触发 |
onmouseenter | 当鼠标进入元素范围时触发(优先级高) |
onmouseleave | 当鼠标离开元素范围时触发(优先级高) |
onreset | 单击表单的reset按钮 |
onresize | 选择一个表单对象时 |
onselect | 选中表单里的内容 |
onsubmit | 点击form表单的 |
单击和双击
如果一个元素同时单击和双击事件,就有可能出现以下情况
- 只执行单击事件,无法执行双击事件
- 单击事件和双击事件先后执行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#app {
width:100px;
height:100px;
background-color: cyan;
}
</style>
</head>
<body>
<div id="app"></div>
</body>
<script>
var app = document.getElementById("app");
app.onclick = function(){
console.log("单击");
}
app.ondblclick = function(){
console.log("双击");
}
</script>
</html>
解决单击和双击冲突
解决方案:
- 将单击事件的功能放到一个 延时器里等待执行,当在200毫后没有双击事件,则执行单击事件的功能
- 如果200毫内执行了双击事件,则取消单击事件里的定时器任务
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#app {
width:100px;
height:100px;
background-color: cyan;
}
</style>
</head>
<body>
<div id="app"></div>
</body>
<script>
var app = document.getElementById("app");
var timer = null;
app.onclick = function(){
//由于双击事件会产生两次单击,因此需要将上一次单击事件取消
clearTimeout(timer);
timer = setTimeout(function(){
console.log("单击");
},300)
}
app.ondblclick = function(){
console.log("双击");
clearTimeout(timer);
}
</script>
</html>
mouse事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#app {
width:100px;
height:100px;
background-color: cyan;
}
</style>
</head>
<body>
<div id="app">
</div>
</body>
<script>
var app = document.getElementById("app");
//鼠标离开时 app元素背景色变为 天蓝色
app.onmouseout = function(){
this.style.backgroundColor = "cyan";
}
//当鼠标按下不放时,app元素背景色变为 绿色
app.onmousedown = function(){
this.style.backgroundColor = "green"
}
//当鼠标发开时,app元素背景色变为 粉色
app.onmouseup = function(){
this.style.backgroundColor = "pink"
}
//当鼠标进入app元素范围的时候,app元素背景色变为 蓝色
app.onmouseenter = function(){
this.style.backgroundColor = "blue";
}
//当鼠标离开app元素范围的时候,app元素背景色变为 红色
app.onmouseover = function(){
console.log(this);
this.style.backgroundColor = "red";
}
//当鼠标离开app元素范围的时候,app元素背景色变为 黄色
app.onmouseleave = function(){
this.style.backgroundColor = "yellow"
}
</script>
</html>
表单事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="#" onsubmit="sb()" onreset="rs()">
<input type="text" id="username">
<select id="city">
<option value="河池">河池</option>
<option value="深圳">深圳</option>
</select>
<textarea></textarea>
<input type="submit" value="提交"/>
<input type="reset" value="重置">
</form>
</body>
<script>
var usernameInput = document.getElementById("username");
usernameInput.onfocus = function(){
console.log("聚焦到input元素了");
}
usernameInput.onblur = function(){
console.log("input元素失焦了");
}
usernameInput.onchange = function(){
console.log("输入完成,失焦后.内容发生变化");
}
usernameInput.oninput = function(){
console.log("正在输入内容",this.value);
}
usernameInput.onselect = function(){
console.log("选中的内容",this.value);
}
var city = document.getElementById("city");
city.onchange = function(){
console.log(this.value);
}
function rs(){
alert("重置");
}
function sb(){
alert("提交");
}
</script>
</html>
事件对象
当事件被触发的时候,会自动生成一个 事件对象 作为参数传递给要触发的函数.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="btn1">点击</button>
</body>
<script>
var btn1 = document.getElementById('btn1');
btn1.onclick = function(ev){
console.log(ev);
}
</script>
</html>
属性 | 说明 |
---|---|
altKey | alt键是否被按下 |
bubbles | 是否是冒泡事件类型 |
cancelBubble | 阻止事件冒泡 |
cancelable | 返回布尔值,指示事件是否可拥可取消的默认动作。 |
ctrlKey | 'ctrl'键是否被按下 |
currentTarget | 返回其事件监听器触发该事件的元素。 |
defaultPrevented | 返回一个布尔值,表明当前事件是否调用了 event.preventDefault()方法。 |
eventPhase | 返回一个代表当前执行阶段的 整数值 |
shiftKey | 是否同时按下shift键 |
target | 返回触发此事件的元素 |
type | 事件类型 |
target
该属性直接获取触发事件的元素对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" id="username">
</body>
<script>
var usernameInput = document.getElementById("username");
usernameInput.onchange = function(ev){
//兼容性
var event = ev || window.event;
var target = event.target;
console.log(target.value);
console.log(target.id);
console.log(target.style);
}
</script>
</html>
鼠标事件对象
当我们按下鼠标左键,右键,滚轮等行为的时候,它也会生成一个事件对象,在触发的事件函数里使用.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="btn">按钮1</button>
</body>
<script>
var btn = document.getElementById("btn");
btn.onclick = function(ev){
var e = ev || window.event;
console.log(e);
}
</script>
</html>
从上图中我们看到鼠标事件多出了很多属性
属性名 | 说明 |
---|---|
altKey | 是否使用了alt键 |
button | 使用的按键 |
buttons | 触发时哪些鼠标按键被按下,或者有多少个触控点 |
clientX | 与浏览器可视区域左边的距离 |
clientY | 与浏览器可视区域顶部的距离 |
ctrlKey | 是否使用了ctrl键 |
layerX | 与最近有定位的元素的x轴距离 |
layerY | 与最近有定位的元素的y轴距离 |
metaKey | 是否按下了meta键(windows或者mac command) |
movementX | 与上一个mousemove事件之间的水平距离 |
movementY | 与上一个mousemove事件之间的垂直距离 |
offsetX | 鼠标相对于事件源x轴的位置 |
offsetY | 鼠标相对于事件源y轴的位置 |
pageX | |
pageY | |
screenX | 鼠标相对于显示器屏幕x轴的位置 |
screenY | 鼠标相对于显示器屏幕y轴的位置 |
shiftKey | 是否按下了shift键 |
type | "click" |
x | 鼠标相对于可视区域左侧的距离 |
y | 鼠标相对于可视区域顶部的距离 |
鼠标按钮 button
它返回一个值,代表用户按下并触发了事件的鼠标按键。
按键值 | 说明 |
---|---|
0 | 按下主键(左键) |
1 | 按下滚轮 |
2 | 按下右键 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#app {
width: 100px;
height: 100px;
background-color: cyan;
}
</style>
</head>
<body>
<div id="app"></div>
</body>
</html>
<script>
var app = document.getElementById("app");
app.onmouseup = function(ev){
var e = ev || window.event;
alert(e.button);
}
</script>
兼容IE
按键值 | 说明 |
---|---|
0 | 没有按下按钮 |
1 | 主按钮 (左键) |
2 | 次要按钮(右键) |
4 | 中间滚轮 |
function checkButton(ev){
var e = ev || window.event;
if(ev){
return e.button;
}else if(window.event){
switch(e.button){
case 1:
return 0;
case 2
return 2;
case 4:
return 1;
}
}
}
//为整个document对象注册鼠标事件
document.onmouseup = function(ev){
var button = checkButton(ev);
if(button(ev) === 0){
console.log("按下了左键");
}else if(button==1){
console.log("按下了中键");
}else if(button==2){
console.log("按下了右键");
}
}
preventDefault()
阻止默认事件,例如,阻止右键行为
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#app {
width: 300px;
height:300px;
background: lightblue;
}
</style>
</head>
<body>
<div id="app"></div>
</body>
</html>
<script>
document.getElementById("app").oncontextmenu = function(ev){
var event = ev || window.event;
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue = false;
}
alert("禁止使用鼠标右键");
}
</script>
自定义右键菜单
下方例子中, 当我们右键点击app元素的时候,会弹出我们自定义的菜单.左键点击页面的时候,隐藏菜单
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
list-style: none;
}
#app {
width: 300px;
height:300px;
background: lightblue;
}
#contextMenu{
display: none;
width: 100px;
background-color: gray;
padding: 10px;
box-sizing: border-box;
}
#contextMenu li {
width: 100%;
height: 30px;
}
#contextMenu li:hover{
background-color: lightgray;
}
</style>
</head>
<body>
<div id="app"></div>
<ul id="contextMenu">
<li>html</li>
<li>css</li>
<li>javascript</li>
</ul>
</body>
</html>
<script>
document.getElementById("app").oncontextmenu = function(ev){
var event = ev || window.event;
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue = false;
}
var contextMenu = document.getElementById("contextMenu");
contextMenu.style.position = 'absolute';
contextMenu.style.top = event.clientY+10+"px";
contextMenu.style.left = event.clientX+"px";
contextMenu.style.display = "block";
}
//取消右键
document.onclick = function(){
contextMenu.style.display = "none"
}
</script>
事件传递
事件传递(事件流).它是描述页面接收事件的顺序.当一个页面有多层元素的叠加到一起时,当你点击一个元素,它并不会只触发当前元素的绑定的事件,而是将所有叠加的元素的事件都触发一遍.
事件传递的方向 有两种:
- 事件冒泡
- 事件捕获
事件冒泡
事件从里往外逐层触发
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#app {
width: 100px;
height: 100px;
background-color: lightblue;
}
</style>
</head>
<body>
<div id="app">
<button id="btn">按钮</button>
</div>
</body>
</html>
<script>
var app = document.getElementById("app");
var btn = document.getElementById("btn");
var body = document.body;
app.onclick = function(){
console.log("app");
}
btn.onclick = function(){
console.log("btn");
}
body.onclick = function(){
console.log("body");
}
document.onclick = function(){
console.log("document");
}
</script>
输出顺序如下
阻止事件冒泡
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#app {
width: 100px;
height: 100px;
background-color: lightblue;
}
</style>
</head>
<body>
<div id="app">
<button id="btn">按钮</button>
</div>
</body>
</html>
<script>
var app = document.getElementById("app");
var btn = document.getElementById("btn");
var body = document.body;
app.onclick = function(){
console.log("app");
}
btn.onclick = function(ev){
console.log("btn");
var e = ev || window.event;
window.event?e.cancelBubble=true:e.stopPropagation();
}
body.onclick = function(){
console.log("body");
}
document.onclick = function(){
console.log("document");
}
</script>
事件委托
事件委托就是将 当前元素的要触发的事件,委托给其他元素来触发.
优点:
- 大量节省内存的占用,减少事件注册
- 对新增元素实现动态绑定事件
大量占用内存的例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#app {
width: 100px;
height: 100px;
background-color: lightblue;
}
</style>
</head>
<body>
<div id="app">
<ul id="list">
</ul>
</div>
</body>
</html>
<script>
var list = document.getElementById("list");
for(var i=0;i<10000;i++){
var li = document.createElement("li");
li.index = i;
li.innerHTML = i;
/*
注释部分
*/
li.onclick = function(){
console.log(this.index);
}
list.appendChild(li);
}
</script>
上面的代码中,我们一次性给ul元素插入了1万个li元素,并且为每个li元素对象增加了onclick事件. 为了测试对内存的影响,我们对上面的代码中 li.onclick进行注释和不注释的代码测试.然后在火狐浏览器中, 通过内存分析工具,得出下面两张图
注释了代码测试
取消注释代码测试
从上面两张图中,我们可以看出如果我们为每一个li元素对象都注册一个事件,就会增加内存的消耗.降低页面的性能
为了降低内存的占用,我们可以将li的事件,注册给它的父级元素ul,这样就可以只用注册一个事件,就可以对每个li元素进行处理.大大地提供性能.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#app {
width: 100px;
height: 100px;
background-color: lightblue;
}
</style>
</head>
<body>
<div id="app">
<ul id="list">
</ul>
</div>
</body>
</html>
<script>
var list = document.getElementById("list");
for(var i=0;i<10000;i++){
var li = document.createElement("li");
li.index = i;
li.innerHTML = i;
list.appendChild(li);
}
list.onclick = function(ev){
var e = ev || window.event;
//获取点击ul元素时,鼠标实际点中的元素
var target = e.target;
//判断target元素是否是li
switch(target.tagName){
case "LI":
//在这里做你想做的事
console.log(target.index);
break;
default:
break;
}
}
</script>
DOM2 事件监听
在前面学习的事件绑定中,我们都是将函数赋值给一个事件来处理.但这种赋值方式的绑定,决定了一个元素只能绑定同类型的事件一次.如果多次绑定,则会被最后一次绑定覆盖.如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#app {
width: 100px;
height: 100px;
background-color: cadetblue;
}
</style>
<script>
window.onload = function(){
console.log("onload第一次");
}
window.onload = function(){
console.log("onload第二次");
}
</script>
</head>
<body>
<div id="app"></div>
</body>
</html>
<script>
var app = document.getElementById("app");
app.onclick = function(){
console.log("第一次绑定");
}
app.onclick = function(){
console.log("第二次绑定");
}
</script>
执行结果
onload第二次
第二次绑定
从上面的例子中,可以看出.如果同一个对象注册了相同的事件,只会执行最后一次的事件绑定.如果我们是处于多人开发的,而另外一个同事给同一个对象注册相同的事件,最终结果就只有一种 产生冲突,只执行最有一次注册的事件.
addEventListener
每一个元素对象有这个方法,该函数接受三个参数
- 事件名称
- 函数名
- 冒泡或捕获(true表示捕获,false表示冒泡)
将上面的例子修改如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#app {
width: 100px;
height: 100px;
background-color: cadetblue;
}
</style>
<script>
window.addEventListener("load",function(){
console.log("onload第一次");
},false)
window.addEventListener("load",function(){
console.log("onload第二次");
},false)
</script>
</head>
<body>
<div id="app"></div>
</body>
</html>
<script>
var app = document.getElementById("app");
app.addEventListener("click",function(){
console.log("第一次绑定");
},false)
app.addEventListener("click",function(){
console.log("第二次绑定");
},false)
</script>
输出结果
onload第一次
onload第二次
第一次绑定
第二次绑定
removeEventListener
移除事件监听: 可以对注册的事件进行移除,不再触发.但是需要注意的是,注册的函数必须是 单独声明的函数, 否则无法移除
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#app {
width: 100px;
height: 100px;
background-color: cadetblue;
}
</style>
</head>
<body>
<div id="app"></div>
<button id="btn">取消绑定</button>
</body>
</html>
<script>
var app = document.getElementById("app");
var btn = document.getElementById("btn");
var func = function(){
console.log("被绑定的事件");
}
app.addEventListener("click",func,false)
app.addEventListener("click",function(){
console.log("绑定时间2");
},false)
btn.addEventListener("click",function(){
//取消app绑定的click事件的func方法
app.removeEventListener("click",func);
},false)
</script>