js之事件

72 阅读9分钟

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>

事件处理程序

  1. HTML事件处理程序 直接在标签内绑定事件 缺点:html+js无分离,后期不易维护
  2. DOM0级处理程序
  3. DOM2级处理程序
  4. 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>

事件的调用顺序

总结:

  1. 相同点,如果同时出现HTML事件处理程序和DOM0级事件处理程序,DOM0级会覆盖HTML事件处理程序
  2. 不同点
    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 targetsrcElement
currentTarget属性返回事件当前所在的节点,正在执行的监听函数所绑定的节点
targetsrcElement 返回的是事件的实际目标对象,srcElementtarget一样只是浏览器不同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>

鼠标事件对象属性

  • clientX clientY 相对于浏览器(浏览器的有效区域)的X轴和Y轴的距离
  • offsetX offsetY相对于事件源的X轴和Y轴距离
  • screenX screenY相对于显示器屏幕的X轴和Y轴的距离
  • pageX pageY相对于页面的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>