js之事件
js操作css称为脚本化的css,而js与html交互通过事件完成, 事件就是文档或浏览器窗口中发生的一些交互瞬间,事件流(事件传播)描述的是从页面中接受事件的顺序
历史
如果单机了某个按钮,认为单击事件不仅仅发生在按钮上,甚至单击整个页面
两个流派:IE和Netscape
- IE 事件流是事件冒泡流
- Netscape 事件流是事件捕获流
事件冒泡和事件捕获
事件冒泡:
事件开始时由最具体的元素去接受,然后逐级向上传播到较为不具体的节点(文档)
注意: 现在所有浏览器都支持冒泡 但IE9以上 火狐 都是冒泡到window对象 IE9以下就只能到 document
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
#box {
width: 200px;
height: 200px;
background-color: red;
}
</style>
</head>
<body>
<div id="box"></div>
<script type="text/javascript">
var box = document.getElementById('box')
box.onclick = function() {
box.innerHTML += 'div\n'
}
document.body.onclick = function() {
box.innerHTML += 'body\n'
}
document.documentElement.onclick = function() {
box.innerHTML += 'html\n'
}
document.onclick = function() {
box.innerHTML += 'document\n'
}
window.onclick = function() {
box.innerHTML += 'window\n'
} </script>
</body>
</html>
事件捕获: 由不太具体的节点更早的接受事件,而最具体的节点应该最后接收到事件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
#box {
width: 200px;
height: 200px;
background-color: red;
}
</style>
</head>
<body>
<div id="box"></div>
<script type="text/javascript">
var box = document.getElementById('box')
//事件捕获:由不太具体的节点更早的接受事件,而最具体的节点应该最后接收到事件。
box.addEventListener('click', function() {
box.innerHTML += 'box\n'
}, true)
document.body.addEventListener('click', function() {
box.innerHTML += 'body\n'
}, true)
document.documentElement.addEventListener('click', function() {
box.innerHTML += 'html\n'
}, true)
document.addEventListener('click', function() {
box.innerHTML += 'document\n'
}, true)
window.addEventListener('click', function() {
box.innerHTML += 'window\n'
}, true)
// 事件冒泡
box.addEventListener('click', function() {
box.innerHTML += 'box\n'
}, false)
document.body.addEventListener('click', function() {
box.innerHTML += 'body\n'
}, false)
document.documentElement.addEventListener('click', function() {
box.innerHTML += 'html\n'
}, false)
document.addEventListener('click', function() {
box.innerHTML += 'document\n'
}, false)
window.addEventListener('click', function() {
box.innerHTML += 'window\n'
}, false)
</script>
</body>
</html>
事件处理程序
- HTML事件处理程序 直接在标签内绑定事件 缺点:html+js无分离,后期不易维护
- DOM0级处理程序
- DOM2级处理程序
- IE事件处理程序、
html事件处理程序: 主要就是行内式来进行事件的绑定和事件的操作处理
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<!-- <div id="box" style="width: 200px;height: 100px;background-color: aqua;" onclick="this.innerHTML+=1">
</div> -->
<div id="box" style="width: 200px;height: 100px;background-color: aqua;" onclick="test()">
</div>
<div id="box2" onclick="this.innerHTML += event.type"
style="width: 200px;height: 200px;background-color: rebeccapurple;"></div>
<button id="box3" value='hetao' onclick="this.innerHTML += value"
style="width: 200px;height: 200px;background-color: gold;"></button>
<script type="text/javascript">
function test() {
document.getElementById('box').innerHTML += '1'
}
</script>
</body>
</html>
DOM0级事件处理程序:简单 具有跨浏览器的优势
缺点:不能给同一个元素来绑定相同的事件处理程序,如果绑定了,会有覆盖现象
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
#box {
width: 200px;
height: 200px;
background-color: red;
}
</style>
</head>
<body>
<div id="box"></div>
<script type="text/javascript">
var box = document.getElementById('box')
box.onclick = function() {
this.innerHTML += 1
}
//删除事件处理程序
box.onclick = null;
box.onclick = function() {
this.innerHTML += 2
}
</script>
</body>
</html>
DOM2级事件处理程序:addEventListener(事件名,处理程序的函数,布尔值) removeEventListener()
布尔值默认为false,决定了事件流处于冒泡阶段,如果为true是处于捕获阶段
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
#box {
width: 200px;
height: 200px;
background-color: red;
}
</style>
</head>
<body>
<div id="box"></div>
<script type="text/javascript">
// DOM2级事件中处理定义程序:
//addEventListener(事件名,处理程序的函数,布尔值) removeEventListener()
//布尔值默认为false,决定了事件流处于冒泡阶段,如果为true是处于捕获阶段
var box = document.getElementById('box')
// IE8浏览器不支持DOM2级事件处理程序
// setTimeout(function() {
// box.addEventListener('click', function() {
// this.innerHTML += 1;
// }, false)
// }, 10)
// //监听函数传参,可以用匿名函数包装一个监听函数
// box.addEventListener('click', function() {
// this.innerHTML += 2;
// test('hetao')
// }, false)
// function test(x) {
// alert(x);
// }
//无效的移出事件的方式
// box.addEventListener('click', function() {
// this.innerHTML += 1;
// }, false)
// box.removeEventListener('click', function() {
// this.innerHTML += 1;
// }, false)
//正确移除事件的方式
function handler() {
this.innerHTML += 1
}
box.addEventListener('click', handler, false)
box.removeEventListener('click', handler, false))
</script>
</body>
</html>
IE事件处理程序 基本上很少
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
#box {
width: 200px;
height: 200px;
background-color: red;
}
</style>
</head>
<body>
<div id="box"></div>
<script type="text/javascript">
// IE: attachEvent() detachEvent()
var box = document.getElementById('box')
//IE 是onclick
// box.attachEvent('onclick', function() {
// alert(1)
// this.innerHTML += '1'
// })
// box.attachEvent('onclick', function() {
// alert(1)
// this.innerHTML += '2'
// })
function handler() {
box.innerHTML += '1'
}
box.attachEvent('onclick', handler) //新增
box.datachEbent('onclick', handler) //移除
</script>
</body>
</html>
事件绑定兼容写法
DOM2级事件处理程序 addEventListener() IE8不支持,IE是 attachEvent(), attachEvent()内部的this指向了window,我们要对this的指向也做兼容
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<button id="btn">你过来呀</button>
<script type="text/javascript">
// btn.addEventListener('click', function(){}, false)
// btn.attachEvent('onclick',function(){})
var btn = document.getElementById('btn')
function handler() {
console.log(this.innerText)
}
btn.attachEvent('onclick', function() {
handler.call(btn)
})
// btn.addEventListener('click', function(){}, false)
// btn.attachEvent('onclick',function(){})
//-----------------全浏览器处理程序的兼容性代码-------------------------
addEvent(btn, 'click', function() {
console.log(this.innerHTML);
})
function addEvent(target, eventType, handler) {
if (target.addEventListener) {
// chromw ff safari
target.addEventListener(eventType, handler, false)
} else {
target.attachEvent('on' + eventType, function() {
handler.call(target)
})
}
}
//--------------call 改变this指向------------------------------------------
var obj = {
name: 'hetao'
}
function fn() {
console.log(this.name)
}
call 方法改变 this 指向问题
fn.call(obj) //this 就指向了obj
</script>
</body>
</html>
事件的调用顺序
总结:
- 相同点,如果同时出现HTML事件处理程序和DOM0级事件处理程序,DOM0级会覆盖HTML事件处理程序
- 不同点
2.1 chrome,safari,firefox以及IE11 结果: DOM0级 DOM2级
2.2 IE9/10 结果为:DOM0级 DOM2级 IE
2.3 IE8 : DOM0级 IE
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
#box {
width: 200px;
height: 200px;
background-color: gray;
}
</style>
</head>
<body>
<div id="box" onclick="this.innerHTML += 'html\n'"></div>
<script type="text/javascript">
var box = document.getElementById('box')
// DOM0级事件处理程序
box.onclick = function() {
this.innerHTML += 'DOM0级\n'
}
// DOM2级事件处理程序
if (box.addEventListener) {
box.addEventListener('click', function() {
this.innerHTML += 'DOM2级\n'
})
}
// IE级事件处理程序
if (box.attachEvent) {
box.attachEvent('click', function() {
this.innerHTML += 'IE\n'
})
}
</script>
</body>
</html>
如何获取事件对象
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
#box {
width: 200px;
height: 200px;
background-color: gray;
}
</style>
</head>
<body>
<!--取消a默认的跳转行为 -->
<a href="javascript:void(0);"></a>
<div id="box"></div>
<script type="text/javascript">
//兼容性
window.onload = function() {
var box = document.getElementById('box')
// 1.event对象是事件处理程序的第一个默认参数 IE8浏览器不兼容
// IE8浏览器得到的结果是undefined,其他浏览器
box.onclick = function(e) {
console.log(e);
this.innerHTML = e
}
//-----------------------------
// 2.直接可以使用event变量 在firefox低版本undefined不识别!
box.onclick = function() {
box.innerHTML = event
}
//----------兼容性写法-----------
box.onclick = function(e) {
e = e || window.event
this.innerHTML = e
}
}
</script>
</body>
</html>
事件目标对象
currentTarget target和 srcElement
currentTarget属性返回事件当前所在的节点,正在执行的监听函数所绑定的节点
target和 srcElement 返回的是事件的实际目标对象,srcElement 和 target一样只是浏览器不同IE8支持 但是不支持低版本firefox!!
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
.item {
width: 100px;
height: 80px;
background-color: lightblue;
margin: 50px 10px;
}
</style>
</head>
<body>
<ul id="box">
<li class="item"> 1</li>
<li class="item"> 2</li>
</ul>
<script type="text/javascript">
var box = document.getElementById('box')
// 1.currentTarget 属性 返回事件当前所在的节点,正在执行的监听函数所绑定的节点
var box = document.getElementById('box')
box.onclick = function(e) {
e = e || window.event
console.log(e.currentTarget)
var items = document.getElementsByTagName('li')
items[0].innerHTML = e.currentTarget
}
//---------------------------------------
// 2.target属性 返回的是事件的实际目标对象
//IE8不支持
box.onclick = function(e) {
e = e || window.event
console.log(e.target)
//this对象跟e.currentTarget属性是一致的 box
console.log(e.target === this) //false
console.log(e.currentTarget === this) //true
}
//-------------------------------------------
//3.srcElement 和 target一样
// IE8支持 但是不支持低版本firefox!!
box.onclick = function(e) {
e = e || window.event
// console.log(e.srcElement)
var target = e.target || e.srcElement;
target.style.backgroundColor = 'orange'
}
box.onmouseout = function(e) {
e = e || window.event
// console.log(e.srcElement)
var target = e.target || e.srcElement;
target.style.backgroundColor = 'blue'
}
</script>
</body>
</html>
事件代理
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
* {
padding: 0;
margin: 0;
}
ul {
list-style: none;
overflow: hidden;
margin-top: 80px;
}
ul li {
float: left;
width: 100px;
height: 30px;
text-align: center;
line-height: 30px;
color: #fff;
background-color: #000;
margin: 0 10px;
}
</style>
</head>
<body>
<ul id="box">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script type="text/javascript">
window.onload = function() {
//常规方法实现
// 1.获取标签
var lis = document.getElementsByTagName('li')
for (var i = 0; i < lis.length; i++) {
lis[i].onmouseover = function() {
this.style.backgroundColor = 'blue'
}
lis[i].onmouseout = function() {
this.style.backgroundColor = 'black'
}
}
//通过事件代理的方式实现 事件实际目标对象来实现
//事件代理应用: 事件实际目标对象target和srcElement属性完成
//优点: 提高性能以及降低代码的复杂度
var box = document.getElementById('box')
box.onmouseover = function(e) {
e = e || window.event
var target = e.target || e.srcElement
target.style.backgroundColor = 'blue'
}
box.onmouseout = function(e) {
e = e || window.event
var target = e.target || e.srcElement
target.style.backgroundColor = 'black'
}
}
</script>
</body>
</html>
给未来的元素添加事件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
* {
padding: 0;
margin: 0;
}
ul {
list-style: none;
overflow: hidden;
margin-top: 80px;
}
ul li {
float: left;
width: 100px;
height: 30px;
text-align: center;
line-height: 30px;
color: #fff;
background-color: #000;
margin: 0 10px;
}
</style>
</head>
<body>
<ul id="box">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script type="text/javascript">
window.onload = function() {
var box = document.getElementById('box')
// 模拟未来的某个事件添加对应的数据
setTimeout(function() {
var item = document.createElement('li')
item.innerHTML = '6'
box.appendChild(item)
}, 3000)
//常规方法实现 不能触发未来添加的事件
//1.获取标签
var lis = document.getElementsByTagName('li')
for (var i = 0; i < lis.length; i++) {
lis[i].onmouseover = function() {
this.style.backgroundColor = 'blue'
}
lis[i].onmouseout = function() {
this.style.backgroundColor = 'black'
}
}
//通过事件代理的方式实现 事件实际目标对象来实现
//时间代理应用: 事件实际目标对象target和srcElement属性完成
//优点: 提高性能以及降低代码的复杂度
box.onmouseover = function(e) {
e = e || window.event
var target = e.target || e.srcElement
target.style.backgroundColor = 'blue'
}
box.onmouseout = function(e) {
e = e || window.event
var target = e.target || e.srcElement
target.style.backgroundColor = 'black'
}
}
</script>
</body>
</html>
事件冒泡的方法
bubbles返回一个布尔值 表示当前事件是否会冒泡,只读cancelBubble表示取消事件的进一步冒泡 无返回值,但是无法阻止同一事件的其他监听函数被调用stopPropagation()既可以阻止冒泡,也可以阻止同一事件的其他监听函数被调用stopImmediatePropagation()属性 用于阻止冒泡 可读写 默认值为false,当为true时 会取消事件冒泡
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<button id="btn" style="height: 30px;width: 200px;">按钮</button>
<input type="text" id="test" />
<script type="text/javascript">
//bubbles cancelBubble stopPropagation() stopImmediatePropagation()
//1.bubbles 返回一个布尔值 表示当前事件是否会冒泡,只读
//注意: 大部分事件都会冒泡,但是 focus blur sroll事件不会冒泡
var btn = document.getElementById('btn')
var test = document.getElementById('test')
btn.onclick = function(e) {
e = e || window.event
console.log(e.bubbles);
}
test.onfocus = function(e) {
e = e || window.event
console.log(e.bubbles);
}
//---------------------------------------------
// 2.stopPropagation()表示取消事件的进一步冒泡 无返回值,但是无法阻止同一事件的其他监听函数被调用
//IE8浏览器不支持
btn.onclick = function(e) {
e = e || window.event
//阻止冒泡
e.stopPropagation()
this.innerHTML = '阻止冒泡'
}
btn.addEventListener('click', function(e) {
e = e || window.event
e.stopPropagation()
this.innerHTML = '修改了'
}, false)
btn.addEventListener('click', function(e) {
e = e || window.event
this.style.backgroundColor = 'blue'
}, false)
document.body.onclick = function(e) {
e = e || window.event
console.log('body');
}
//-----------------------------------
// 3.stopImmediatePropagation() 既可以阻止冒泡,也可以阻止同一事件的其他监听函数被调用
btn.addEventListener('click', function(e) {
e = e || window.event
e.stopImmediatePropagation()
this.innerHTML = '修改了'
}, false)
btn.addEventListener('click', function(e) {
e = e || window.event
this.style.backgroundColor = 'blue'
}, false)
document.body.onclick = function(e) {
e = e || window.event
console.log('body');
}
//--------------------------------------------
// 4.cancelBubble 属性用于阻止冒泡 可读写 默认值为false,当为true时 会取消事件冒泡
btn.onclick = function(e) {
e = e || window.event
if (e.stopPropagation) {
e.stopPropagation()
} else {
e.cancelBubble = true
}
console.log(e.bubbles)
this.innerHTML = '修改了'
}
document.body.onclick = function(e) {
e = e || window.event
console.log('body');
}
//兼容stopPropagation() stopImmediatePropagation()IE8不支持
//e.cancelBubble = true ;全浏览器都支持 不是标准写法
var handler = function(e) {
e = e || window.event
if (e.stopPropagation) {
e.stopPropagation()
} else {
e.cancelBubble = true
}
}
</script>
</body>
</html>
事件流阶段
e.eventPhase表示当前事件流的阶段
- 0 表示事件没有发生
- 1 表示捕获阶段
- 2 目标阶段
- 3 冒泡阶段
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<button id="btn">事件流</button>
<script type="text/javascript">
var btn = document.getElementById('btn')
// e.eventPhase 0 表示事件没有发生 1 表示捕获阶段
// 2 目标阶段 3 冒泡阶段
//---------------1 捕获阶段--------------
document.body.addEventListener('click', function(e) {
e = e || window.event
console.log(e.eventPhase)
}, true)
//---------------2 目标阶段--------------
btn.onclick = function(e) {
e = e || window.event
console.log(e.eventPhase)
}
//---------------3 冒泡阶段--------------
document.body.addEventListener('click', function(e) {
e = e || window.event
console.log(e.eventPhase)
}, false)
</script>
</body>
</html>
取消默认行为
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<a href="javascript:void(0)">百度</a>
<a href="javascript:;">百度2</a>
<a href="#" id="baidu">preventDefault</a>
<script type="text/javascript">
//事件对象中两个方法 阻止默认事件: preventDefault() returnValue return false
var baidu = document.getElementById('baidu')
// 1. preventDefault() IE8不支持
baidu.onclick = function(e) {
e = e || window.event
//阻止默认事件
// e.preventDefault()
//阻止默认事件 低版本火狐 IE8以上不支持
// e.returnValue = false
//-----兼容性--------
if (e.preventDefault) {
e.preventDefault()
} else {
//兼容IE8以下浏览器
e.returnValue = false
}
//3.return false 所有浏览器
// return false
}
</script>
</body>
</html>
鼠标事件对象属性
clientXclientY相对于浏览器(浏览器的有效区域)的X轴和Y轴的距离offsetXoffsetY相对于事件源的X轴和Y轴距离screenXscreenY相对于显示器屏幕的X轴和Y轴的距离pageXpageY相对于页面的X轴和Y轴距离
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
#box {
width: 200px;
height: 200px;
background-color: red;
position: fixed;
}
</style>
</head>
<body style="height: 2000px;">
<div id="box"></div>
<script type="text/javascript">
// 坐标位置
//事件对象中提供了:clientX/Y, x/y,offsetX/Y,screenX/Y,pageX/Y
var box = document.getElementById('box')
box.onmousemove = function(e) {
e = e || window.event
//clientX/Y和x/y:是相对于浏览器(浏览器的有效区域)的X轴和Y轴的距离
this.innerHTML = `clientX:${e.clientX};clientY:${e.clientY};X:${e.x};Y:${e.y}`
//screenX/Y是相对于显示器屏幕的X轴和Y轴的距离
this.innerHTML = `screenX:${e.screenX};screenY:${e.screenY};`
//pageX/Y 是相对于页面的X轴和Y轴距离
this.innerHTML = `pageX:${e.pageX};page:${e.pageY};`
//offsetX/Y 是相对于事件源的X轴和Y轴距离
this.innerHTML = `offsetX:${e.offsetX};offsetY:${e.offsetY};`
}
</script>
</body>
</html>