JS的事件处理详解

事件就是用户的某个动作,事件处理就是一个用来处理该动作的函数。这一节我们一起来看看JS中的事件处理。

文章目录

    • 添加事件处理的三种方式
      • 行内添加
      • 标签属性添加
      • 添加事件监听
    • 聚焦事件
    • 单击与双击事件
    • 鼠标事件
    • 键盘事件
    • 鼠标的event事件
    • 事件流
    • 阻止冒泡
    • 阻止默认行为

添加事件处理的三种方式

事件是名词字符串,例如click,事件处理是函数,以on开头,例如onclick。

行内添加

例如

<button type="button" onclick="change1()">Boom!button>

这里点击这个按钮,就会去执行JS中定义的change1函数。这种添加方式的缺点就是html和js的代码没有解耦合,所以不是很推荐。

标签属性添加

例如

var box=document.getElementById('box');
box.onclick=()=>{
	box.style.backgroundColor='yellow';
}

对元素的onclick属性赋值一个函数,点击就会执行该函数。这种添加方式已经实现了html和js代码的解耦合,但是其缺点是多次设置的话后面的事件处理会覆盖前面的

例如

box.onclick=()=>{
	alert("'hey, what's up?");
}
box.onclick=()=>{
	alert('hey, WTF?');
}

点击的话只会显示后面的hey, WTF?

如果要清除事件处理,赋值null即可,例如

box.onclick=null;

添加事件监听

例如

function f(){
	this.style.backgroundColor='blue';
}
box.addEventListener('click',f,false);

这里对box元素添加了一个事件监听,前两个参数分别是,事件,处理函数,第三个参数是事件流,先放false。这里将处理函数单独定义,而不采用匿名函数,是为了方便后面的清除操作。

注意这里的关键字this,在大多数情况下指向的是window本身,不过在事件处理函数中执行发生事件的标签元素

比较奇怪的就是我用匿名函数做为事件处理函数的时候this指向的是window,有点不理解

跟上面的标签属性比起来,这种方式添加多个事件不会被覆盖,推荐用这一种。

如果要清除事件处理,采用下面的方法

box.removeEventListener('click',f,false);

参数和上面的三个参数一致。

接下来用这最后一种方式去学习下几种常见的事件。

聚焦事件

聚焦和失焦事件都是针对于input标签来说的,例如对于文本输入框,鼠标点击文本框看到光标在闪就相是聚焦,鼠标再点击别的地方,光标消失就是失焦。


<html>
	<head>
		<meta charset="utf-8" />
		<script src="js/test.js" type="text/javascript" charset="utf-8">script>
		<title>title>				
	head>
	<style type="text/css">
		* {
			margin: 0;
			padding: 0;
		}
		p {
			visibility: hidden;
		}
	style>
	<body>	
	<input type="text" name="info" id="info" value="" /><p>Surprise!p>
	<script type="text/javascript">
		var input=document.getElementById('info');
		var p=document.getElementsByTagName('p')[0];
		
		input.addEventListener('focus',()=>{
			p.style.visibility='visible';
		},false);
		input.addEventListener('blur',()=>{
			p.style.visibility='hidden';
		},false);
	script>
	body>
html>

这里有一个文本输入框,后面有一个p标签。当文本输入框获得焦点的时候p标签内容显示,文本输入框失去焦点的时候p标签内容隐藏。这种应用场景有点像网页表单的内容提示。

单击与双击事件

前面我们已经使用过了单击事件,双击事件只需要把事件改为dblclick即可。

但是现在有个问题,就是如果单击事件和双击事件同时存在的画,执行双击操作也会触发两次单击事件。这个问题可以通过下面的方法借助延时定时器来解决。


<html>
	<head>
		<meta charset="utf-8" />
		<script src="js/test.js" type="text/javascript" charset="utf-8">script>
		<title>title>				
	head>
	<style type="text/css">
		* {
			margin: 0;
			padding: 0;
		}

	style>
	<body>	
	<p>Click me!p>
	<script type="text/javascript">
		var p=document.getElementsByTagName('p')[0];
		var timer;
		p.addEventListener('click',()=>{
			window.clearTimeout(timer);
			timer=window.setTimeout(()=>{
				console.log('One');
			},500);
		},false);
		p.addEventListener('dblclick',()=>{
			window.clearTimeout(timer);
			console.log('Two');
		},false);
	script>
	body>
html>

每次单击事件会延迟500ms执行,同时不管是双击还是单击都会首先清除定时器。也就是说只要双击的间隔少过500ms就会只响应双击事件,但是这样的弊端就是每次单击会延迟500ms响应,不过对人眼来说基本影响不大。

鼠标事件

鼠标的事件很多,这里用常见的4种举例。


<html>
	<head>
		<meta charset="utf-8" />
		<script src="js/test.js" type="text/javascript" charset="utf-8">script>
		<title>title>				
	head>
	<style type="text/css">
		* {
			margin: 0;
			padding: 0;
		}
		p {
			width: 200px;
		}
	style>
	<body>	
	<p>Mouse Eventp>
	<script type="text/javascript">
		var p=document.getElementsByTagName('p')[0];
		p.addEventListener('mouseenter',()=>{
			p.style.backgroundColor='orangered';
		},false);
		p.addEventListener('mouseout',()=>{
			p.style.backgroundColor='white';
		},false);
		p.addEventListener('mousedown',()=>{
			p.style.fontSize=parseInt(window.getComputedStyle(p,null).fontSize)*2+'px';
		},false);
		p.addEventListener('mouseup',()=>{
			p.style.fontSize=parseInt(window.getComputedStyle(p,null).fontSize)/2+'px';
		},false);
	script>
	body>
html>

鼠标移入,p标签背景变红,鼠标移出背景恢复。鼠标按下字体变大两倍,松开还原。

这里有个问题,就是如果鼠标不是在p标签上松开的字体无法还原

除了给具体标签加鼠标事件,还可以直接给document添加。这里留一个扩展,就是制作一个页面中任意位置抬起鼠标都会在该处显示自定义文字的效果,我在下一节会分享下我自己的代码。

键盘事件

键盘事件通常是给document进行添加。


<html>
	<head>
		<meta charset="utf-8" />
		<script src="js/test.js" type="text/javascript" charset="utf-8">script>
		<title>title>				
	head>
	<style type="text/css">
		* {
			margin: 0;
			padding: 0;
		}
	style>
	<body>	
	<script type="text/javascript">
		document.addEventListener('keydown',(e)=>{
			var event = e || window.event;
			console.log(event);
		},false);
	script>
	body>
html>

一共有keydownkeyupkeypress三种事件,其中前两种是针对所有的按键,而第三种只支持字符按键。

如果此时按下a,会打印出如下内容

JS的事件处理详解_第1张图片

其中箭头处是我们比较关系的几个字段,例如如果按住Ctrl+a,此时ctrlKey就会为true。

下面的代码实现了上下左右四个按键控制浏览器中的文字进行移动的效果


<html>
	<head>
		<meta charset="utf-8" />
		<script src="js/test.js" type="text/javascript" charset="utf-8">script>
		<title>title>				
	head>
	<style type="text/css">
		* {
			margin: 0;
			padding: 0;
		}
		p {
			width: 100px;
			position: relative;
		}
	style>
	<body>	
	<p>Move me!p>
	<script type="text/javascript">
		document.addEventListener('keydown',(e)=>{
			var event = e || window.event;
			var delta_x = 1;
			var delta_y = 1;
			var unit = 10;
			var p = document.getElementsByTagName('p')[0];
			var left = window.getComputedStyle(p,null).left;
			var top = window.getComputedStyle(p,null).top;
			switch(event.keyCode){
				case 37:
				delta_x=-1;
				delta_y=0;
				break;
				case 38:
				delta_x=0;
				delta_y=-1;
				break;
				case 39:
				delta_x=1;
				delta_y=0;
				break;
				case 40:
				delta_x=0;
				delta_y=1;
				break;
				default:
				delta_x=0;
				delta_y=0;
				break;
			}
			p.style.left=parseInt(left)+delta_x*unit+'px';
			p.style.top=parseInt(top)+delta_y*unit+'px';
		},false);
	script>
	body>
html>

只需要记住按键的keyCode数字即可进行判断,这里利用了switch...case语句来判断。

但是注意,这段代码有个缺点就是不能实现两个按键同时按下的同时响应,改进版本也会在后面的博客中分享给大家。

鼠标的event事件

键盘的事件处理函数带了一个参数e,其实鼠标的事件处理也有类似操作


<html>
	<head>
		<meta charset="utf-8" />
		<script src="js/test.js" type="text/javascript" charset="utf-8">script>
		<title>title>				
	head>
	<style type="text/css">
		* {
			margin: 0;
			padding: 0;
		}
		#box {
			width: 100px;
			height: 100px;
			background-color: red;
			position: relative;
			left: 20px;
			top: 20px;
		}
	style>
	<body>	
	<div id="box">
		
	div>
	<script type="text/javascript">
		var box=document.getElementById('box');
		box.addEventListener('click',(e)=>{
			var ev = e || window.event;
			console.log(ev);
		},false);
	script>
	body>
html>

点击红色方框的靠近右侧的边缘,会打印如下内容

JS的事件处理详解_第2张图片

其中箭头处的四个字段需要注意一下,offsetXoffsetY是距离元素边框的距离,而pageXpageY是距离页面边框的距离。后者比较常用一点,例如实现元素拖拽效果。

事件流

出现事件流源自于下面场景的一个争论。

假设有3个div,div1内有个div2,div2内有个div3,并且都有各自的点击事件。那么当鼠标点击最内层的div3时,3个事件触发的顺序是如何的?

有的浏览器是从外到内的顺序触发,有的浏览器是从内到外,于是出现了不和谐。最后经过协商,将这种场景分为三个阶段,如下

  • 事件捕获阶段 - 从外到内的顺序
  • 处于目标阶段
  • 事件冒泡阶段 - 从内到外的顺序

addEventListener方法中有第三个参数,如果是true表示捕获阶段响应事件,也就是从div1到div3依次响应,如果是false表示冒泡阶段响应事件,也就是div3到div1依次响应。

通常设置为false即可,在冒泡阶段响应事件

阻止冒泡

这个时候就有需求产生了,大多数时候我点击内部的一个元素,其实并不想外部的元素也跟着响应。这时候可以在冒泡的时候去掐断这个过程。


<html>
	<head>
		<meta charset="utf-8" />
		<script src="js/test.js" type="text/javascript" charset="utf-8">script>
		<title>title>				
	head>
	<style type="text/css">
		* {
			margin: 0;
			padding: 0;
		}
		#box {
			width: 100px;
			height: 100px;
			background-color: red;
		}
	style>
	<body>	
	<div id="box">	
	div>
	<script type="text/javascript">
		var box = document.getElementById('box');
		var body = document.getElementsByTagName('body')[0];
		box.addEventListener('click',(e)=>{
			var ev = e || window.event;
			box.style.backgroundColor='yellow';
			ev.stopPropagation();
		},false);
		body.addEventListener('click',()=>{
			body.style.backgroundColor='yellow';
		},false);
	script>
	body>
html>

这里通过对box的event事件执行stopPropagation方法来掐断冒泡,这样点击box以后body并不会变为黄色。

注意,需要对外层元素的所有内层元素的点击事件中执行阻止冒泡的操作,不然点击其它内层元素还是会触发外层元素的点击事件

阻止默认行为

有的时候不想让元素的默认行为发生,例如a标签的点击跳转行为,这时候就可以在a标签的点击事件中将默认的跳转行为阻止掉。

例如下面的效果就是在跳转前进行一次询问,如果确认就跳转到百度,不确认则取消跳转。


<html>
	<head>
		<meta charset="utf-8" />
		<script src="js/test.js" type="text/javascript" charset="utf-8">script>
		<title>title>				
	head>
	<style type="text/css">
		* {
			margin: 0;
			padding: 0;
		}
	style>
	<body>	
	<a href="https://www.baidu.com" id="link">Click me!a>
	<script type="text/javascript">
		var link = document.getElementById('link');
		link.addEventListener('click',(e)=>{
			var ev = e || window.event;
			var answer = window.confirm('Are you sure?');
			if (!answer){
				ev.preventDefault();
			}
		},false);
		
	script>
	body>
html>

我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。

你可能感兴趣的:(前端)