目录
2.4 HTML事件,DOM0级事件,DOM2级事件的优缺点
一. 什么是事件?
事件其实也可以简单的理解为用户进行的每一步操作,用户访问我们的页面,一定会做各种各样的操作。比如我点击某个链接、某张图片、或填写个人信息然后提交按钮等这一系列用户执行的操作都可以称之为事件。
举个简单的例子:用户点击页面可以交互的区域,也会受到交互的反馈。比如我输入账号密码登录,过一会就会反馈我登陆成功;又或者我填写个人信息然后提交,会反馈给我提交成功或保存成功;其实,这种效果都是由事件处理程序来完成的,当用户er各种交互,也会产生各种不同的事件,我们通过写代码对各种事件去进行捕获处理,然后将处理后的结果返回给用户。
二. 事件处理程序
在前端,添加一个事件处理程序事件处理程序主要分为三种。
2.1 HTML事件处理
HTML事件的做法就是将函数写在<script></script>标签当中,然后在标签中添加事件属性,并指定执行哪个函数;
有一点需要注意的是,指定的函数一定要带上括号,不能只写函数名,否则会无效!!!
如下代码所示,定义一个按钮,并给定id属性和点击事件,就打印一句话即可
<!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>
<!-- 创建一个按钮 -->
<div>
<button id="btn1" onclick="btn1Demo()">按钮</button>
</div>
<!-- 定义鼠标单击函数onclick -->
<script>
function btn1Demo(){
console.log("触发点击事件,我点击了按钮");
}
</script>
</body>
</html>
然后保存在浏览器打开并将 console 控制台一并打开,当我点击按钮时,就触发了 onclick 单击事件,console 控制台就会输出一句话,当我在点击一次,次数就会加一;我点击三次,就会显示三次。
2.2 DOM0级事件处理
DOM0级事件的添加方式则是HTML标签正常写,将时间的操作全部写在<script></script>标签中,通过DOM获取HTML文档中指定的标签对象来控制标签的事件;
DOM0级事件语法:
var xxx = document.getElementByXXX("");
xxx.事件 = function(){函数体......};
这里注意:xxx.事件 后面不可以加(),不能写成 xxx.事件() = function(){},否则会报错,这一点与上面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>
<!-- 创建一个按钮 -->
<div>
<button id="btn1">按钮</button>
</div>
<!-- 通过DOM获取按钮对象并创建 onclick 事件函数 -->
<script>
var btnObj = document.getElementById("btn1")
btnObj.onclick = function(){
console.log("触发点击事件,我点击了按钮");
}
</script>
</body>
</html>
保存在浏览器打开,点击五次按钮,就可以在控制台发现按钮计数为5.
DOM0级事件的缺点:只能绑定一个事件,如果绑定多个,最后一个事件会将之前的事件全部覆盖;
我将上面的代码稍作修改,给 btnObj 对象绑定两个 onclick 事件,然后保存在浏览器打开,我单击两次按钮,可以在控制台看到,只有第二个函数被触发了。
这里注意,并不是因为两个函数方法名一样所以只执行了下方的方法,而是DOM0级事件的写法本身就只支持绑定一个事件函数。同学们可以将连个函数的方法体调换,让111在下方,就会发现控制台打印的是111而不是222.。如下所示代码运行图所示:
2.3 DOM2级事件处理
DOM2级事件处理和DOM0级写法相似,但要比DOM0写法麻烦一些,都是把事件写在<script></script>标签中,但是,DOM2级事件可以为一个标签绑定多个事件函数;
DOM2级事件语法:
var xxx = document.getElementByXXX();
xxx.addEventListener("事件名","event1");
xxx.addEventListener("事件名","event2");
xxx.addEventListener("事件名","event3");
function event1(){
// 函数体......
}
function event2(){
// 函数体......
}
function event3(){
// 函数体......
}
示例代码如下:创建一个按钮,但是我给此按钮绑定三个事件
<!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>
<!-- 创建一个按钮 -->
<div>
<button id="btn6">按钮</button>
</div>
<script>
var btnObj6 = document.getElementById("btn6")
btnObj6.addEventListener("click",onclick1);
btnObj6.addEventListener("click",onclick2);
btnObj6.addEventListener("click",onclick3);
function onclick1(){
console.log("用户单击了按钮(1)")
}
function onclick2(){
console.log("用户单击了按钮(2)")
}
function onclick3(){
console.log("用户单击了按钮(3)");
console.log("---------------------------");
}
</script>
</body>
</html>
保存代码到浏览器打开,就可以在控制台看到三个 click 函数是可以同时触发的
2.4 HTML事件,DOM0级事件,DOM2级事件的优缺点
通过上面的代码距离其实不难看出,我就直接总结成一个表格啦!
优点 | 缺点 | |
HTML事件 | 比较直观,可以通过标签直接定位到具体的事件函数 | HTML代码与JavaScript代码没有分离,不推荐的写法: (个人学习练习可以使用,开发基本不会使用,因为开发都会将HTML文件与JavaScript文件分离) |
DOM0级事件 | HTML代码与JavaScript代码分离,实现了解耦合 | 只能绑定一个事件,如果绑定多个只有最后一个事件生效,会覆盖其他事件 |
DOM2级事件 | 1:实现了代码的解耦合; 2:可以为标签元素绑定多个事件; 3:多个事件之间互不影响,不会被覆盖; | 写法相对麻烦(但其实在可接受范围内) |
实际开发过程中, 我们可以根据需求来选择DOM0级事件还是DOM2级事件。
如果非常明确的知道某个属性值会触发一个事件,使用DOM0级事件是个不错的选择;
如果标签只是当前绑定一个事件,但不确定后续会不会添加,建议使用DOM2级事件;
三. 常用的十种鼠标事件
我们用户在使用鼠标操作电脑时,会有各种不同的动作,比如单击、双击、滚轮下滑屏幕等;
鼠标不同的操作也就对应着不同的事件;
常用的有以下十种;
1. click | 按下鼠标时触发 |
2. dbclick | 在同一元素上双击鼠标触发 |
3. mousedown | 按下鼠标时触发(按下鼠标的瞬间) |
4. mouseup | 松开按下的鼠标时触发(松开鼠标的瞬间) |
5. mouseover | 鼠标在节点内部移动时触发,进入子节点不会触发这个事件 |
6. mouseenter | 鼠标进入下一个节点时触发,进入子节点不会触发这个事件 |
7. mouseleave | 鼠标离开一个节点时触发,离开父节点不会触发这个事件 |
8. mouseover | 鼠标进入一个节点时触发,进入子节点会再一次触发这个事件 |
9. mouseout | 鼠标离开一个节点时触发,离开父节点会再一次触发这个事件 |
10. wheel | 滚动鼠标滚轮时触发事件 |
这几个方法也没什么好说的,小伙伴们直接复制上面的代码去试一下效果就行了;
我重点来说一下6、7、8、9,它们四个有些相似但又不完全一样;
在说明之前先要弄清楚父节点子节点这两个概念。
如下代码,小盒子外边套着一个大盒子,二者是父子关系,root2 盒子就是root 盒子的子属性(也叫节点),反之,root盒子即为root2盒子的父属性(也叫父节点)。
<!-- 创建一个div大盒子容器,容器内创建一个小div盒子容器 -->
<div class="root">
<div class="root2"></div>
</div>
我给两个盒子简单设置大小和颜色方便区分,如下图所示
弄清楚了父子节点之后,我们再来说上面的这四个事件。
mouseenter,mouseleave为一对;
mouseover,mouseout为一对;
这两对事件最大的区别就在于事件对标签子属性的态度不同。
mouserenter事件或mouseleave事件:定义在父节点上,即外面的 root 大盒子上,当鼠标进入或退出大盒子的区域时,会触发事件,这一点不用多说,当鼠标进入或退出 root 大盒子内部的小盒子 root2 区域内时,不会触发事件;
但是 mouseover 事件和 mouseout 事件则不同:将这两个事件定义在外面的 root 大盒子上,当鼠标进入或推出大盒子时,会触发事件,当鼠标进入或退出大盒子内部的小盒子时,也会触发事件,这就时这两对时间最大的区别点;
四. 事件对象
4.1 事件对象概述
通过上面的讲解我们知道了,事件是用户与页面交互触发产生的,但事件的处理逻辑程序是由我们程序员通过写代码来实现的,就是我们写的 function 函数(就是下面说的监听函数)。
(因为我们程序员写的 function 函数并不是写好就会生效运行,而是用户与界面交互触发事件才去执行。事件不触发,函数不执行。那么我们是不是就可以理解为函数一直在监听某个事件,当我函数感知到用户触发了事件,就去执行,由此而得名监听函数)。
在事件发生以后,会产生一个事件对象,作为参数传递给监听函数。这个对象中会包含关于当前事件的大量信息和触发此事件的元素信息。
如下代码
<body>
<!-- 创建一个div盒子容器 -->
<div class="root">
<!-- 创建一个按钮赋予id值 -->
<button id="btn">按钮</button>
</div>
<script>
// 通过id值获取到button按钮对象
var btn = document.getElementById("btn");
// 给button按钮对象添加单击事件;
// 事件触发,会产生事件对象event作为参数传递给function;
// 这里直接使用event接收即可,也可以取名e,只要下方在使用时保持一致即可;
// 然后我们打印这个event对象看一下都有哪些信息
btn.onclick = function(event){
console.log("单机了按钮");
console.log(event);
}
</script>
</body>
保存代码在浏览器打开,然后点击按钮,就可以看到事件对象 "PointerEvent"。
下面我们就从属性和方法两部分来分析 event 对象;
4.2 event 事件对象属性
刚才我们可以从截图中看到 event 对象有特别多的属性,但实际上我们并不需要掌握那么多,只需要重点掌握 Event.Target,Event.type 这两个就可以了。
(1)Event.Target 返回事件当前所在的节点,大白话来说就是返回绑定了当前事件的标签对象
为了省事我就不粘贴代码了,直接看下面运行截图;
console.log(event.taget) 即可直接在控制台打印出<button>按钮标签对象;
(2)Event.type 返回当前事件类型,比如单击?双击?总之就是上面表格里的那十种
直接看运行截图吧
4.3 event 事件对象方法
event 对象的方法也需要了解下面两个;
(1)Event.preventDefault() 阻止默认事件发生
比如点击链接后,正常来说会跳转至另一个页面,使用了这个方法之后,就不会再跳转了。
如下图所示,有代码有运行图,就不需要我做过多解释了
(2)Event.stopPropagation() 阻止事件在DOM中继续传播
使用了这个方法可以防止再触发定义在别的节点上的监听函数,但是不包括在当前节点上的监听函数。
这个方法的作用有点类似于上面提到的那两对事件,即下面这四个。
mouseenter,mouseleave为一对;mouseover,mouseout为一对;
举一个简单的例子,我创建两个盒子,分别叫 div1,div2并且div2盒子放在div1盒子之内,那么div2就是div1的子节点了。
如果我给div2盒子添加一个单击事件,因为div2是在div1之内的,所以我们点击了div2,就变相等于是点击div1,所以也会触发div1的点击事件。
如下代码,给div1和div2 分别添加样式做以区分,
<!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>
.div1{
width: 500px;
height: 500px;
background-color: red;
}
.div2{
width: 200px;
height: 200px;
background-color: black;
}
</style>
</head>
<body>
<!-- 创建一个div盒子容器 -->
<div class="div1" id="div1">
<div class="div2" id="div2">
</div>
</div>
<script>
var d1 = document.getElementById("div1");
var d2 = document.getElementById("div2");
d1.onclick = function(){
console.log("单击了div1盒子");
}
d2.onclick = function(){
console.log("单击了div2盒子");
}
</script>
</body>
</html>
保存代码在浏览器打开如下所示,两个不同颜色的区域,打的是div1,小的是div2;
当我点击红色 div1 时,触发了 onclick 事件,输出第一句话;
但是当我单击黑色区域时,先触发了 div2 的 onclick 事件,打印了第二句话,但由于 div2 是 div1 的子节点,所以单击会向上一级传递,也等于单击了 div1 ,所以 div1 的单击事件再一次被触发,又打印了第三句话。
这就是不使用 Event.stopPropagation 方法时的效果;
但通常情况下,我们肯定不希望出现这种效果嘛,我点那就是要点那,你给我传到上一级干嘛勒,还触发了其他事件,万一其他事件我不想让它触发呢?因此我们就需要使用 Event.stoPropagation 阻止事件向父级传递。
所以,我们就直接在 div2 的点击事件中通过 event 事件对象调用此方法就可以了.
如下所示,给 div2 盒子的单击事件中添加此方法,就会发现当点击黑色区域时,只触发了黑色区域div2的 onclick 事件,没有向上传递。当以后我们遇到类似的需求时,就可以通过此方法来实现了哦!
五. 键盘事件
键盘事件相比鼠标事件就少了一些,我也总结成一个表格放下面啦
1.keydown | 按下键盘时触发 |
2. keypress | 1:按下有值的键时触发 2:对于 Ctrl、Alt、Shift、Meta 这样无值的键时则不会触发 3:对于有值的键,按下时先触发keydown事件,再触发此事件 |
3. keyup | 松开键盘时触发该事件 |
六. 表单事件★★★★★(重点必须掌握)
下面几个要说的,就是我们实际开发中经常会碰到的几种事件。
不过现在前端都有许多框架,比如Vue,React,Angular等,框架都对这些表单事件进行了封装,提供了现成的API接口事件语法,因此我们在开发过程中不会直接使用下面这几种 JavaScript 提供的表单事件,而是使用框架已经封装好的接口语法。
但不管什么前端框架,底层万变不离其踪,都是封装的 JavaScript。
所以下面这几种事件,同学们可以不会写,但一定要知道有这几种事件的作用,它们都用在哪里?有什么区别?这样当我们再入手其他前端框架的时候,当你要做某个需求,知道这个需求要绑定哪种事件,直接 Chatgpt 搜索xxx框架的xxx事件语法,马上就可以得到,直接拿来使用就行了。
OK,我们下面开始进入正题,了解这几种事件的用法。
6.1 input 事件
input事件语法格式:xxx.oninput = function(event){......}
input 事件当<input>、<select>、<textarea>的值发生变化时触发。对于复选框<input type = "checkbox">或单选框<input type = "radio">,用户改变选项时,也可以触发。
input 事件的一个特点就是会连续触发,用户每按下一次按键,就会触发一次 input 事件,最典型的例子就是百度搜索栏,同学们可以自己打开百度测验,或者看我下面的例子。
搜索框本身就是一个 <input> 标签,我们输入文本内容。
我先输入一个"我",下面弹出一些关于"我"字的相关搜索,
当我改成"我在",相关内容又变了;
我再改成"我在吃",相关内容又变了,很神奇但也司空见惯了。 这种就输入
OK,举了这样一个例子,同学们应该就知道 input 事件的含义了吧,那么 JavaScript 原生的 input 事件语法 oninput。
<!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="input1" placeholder="请输入文本内容">
<script>
// 获取文本框对象
var ipt = document.getElementById("input1");
// 通过 event.target 获取 input 对象,再 .value 获取文本框中的内容
ipt.oninput = function(event){
console.log(event.target.value);
}
</script>
</body>
</html>
然后我们保存,在浏览器打开,我依次输入123456789,可以发现在控制台,每当变化一次变化一次,就会触发一次函数将内容打印,这个效果是不是有点类似于刚才的百度搜索页面,其实是一个原理的。
6.2 select 事件
select 事件语法:xxx.onselect = function(event){......}
select 事件也很好理解,当<input>、<textarea>里面选中文本时触发,其实很好理解的。小伙伴们自己想一想,如果你经常用浏览器搜索内容,浏览器就会记录下你搜索的东西缓存下来,下次你再去搜索之前,或者输入了部分头部信息,浏览器马上就可以为你展现出最近一段时间你常常搜索的,或者搜索过的一些内容记录。
如下图所示,当出现相关内容之后,我们点击对应内容,直接就可以跳转到对应页面了,这就是 select 事件。
实际开发过程中,我们编写 select 函数,获取到用户点击的内容,然后将内容拼接到 https 请求中,再有JavaScript发现请求到后端,后端 Service 业务层去调用数据库查询接口查询数据,再将查询到的数据返回给前端,前端在转化然后展现给我们用户浏览,这就是一个相对比较完整的用户操作。
用户点击select文本——>触发 select 事件函数发送http请求——>请求指定后端Controler控制器层——>Controler调用Service服务层查询数据库——>返回数据——>前端将数据展示到页面
6.3 Change 事件
change 事件语法:xxx.onchange = function(event){......}
change 事件与上面提到的 input 时间有些相似,但二者也有区别。
上面我们说到,input 事件是只要内容发生改变,就会触发事件。
如果我作为一个用户要输入一个很长的字符串,我每输入一个字,前端就触发一次事件,不过用户是感知不到事件的发生的。
如果我要输入100个字,触发100次事件,实际上前面99次事件都是无效的,因为我还没有输完呢对吧,但是仍然触发了事件去发送HTTP请求访问后台资源,如此做来就会非常浪费系统资源,特别是在高并发多用户的系统中,频繁的发送无效的HTTP请求,会导致服务器压力过大而崩溃。
因此,我们就可以对一个输入框添加 change 事件,它的效果就是当我们用户输入完然后将鼠标点到其他区域或者用户输入之后按下回车键时才会触发,我们可以把输入框当作一个"焦点",当用户将鼠标点在输入框内时,类似于"进入焦点",当我们输入完点击下一个输入框进行输入或者鼠标单击了输入框之外的地方,或者在当前输入框按下回车,当前输入框就失去了"焦点",开发中常说失焦,然后随之触发了事件,这个事件就被称为"失焦事件"。
如下图所示:
我给输入框添加失焦事件,然后我去页面输入框内输入"我爱吃饭",可以发现console 控制台并没有像 input 那样,先打印"我",再打印"我爱"...,而是在我点击回车后才去触发事件,然后打印了输入框内的文本"我爱吃饭",是不是很神奇呀!
在开发过程中,Change 引发的失焦事件是非常常用的且很重要的一个语法,所以同学们一定学会哦!!!
6.4 reset 事件
reset 事件语法:function(event){
xxx(表单对象).reset();
}
reset 事件,重置表单,说白了就是清空表单内容,我们将 reset 事件绑定在页面上的重置按钮上。比如用户复制了别人的表单要填写自己的内容,就点击重置按钮,那么表单中的内容会被全部清空,很好理解没什么难度。
如下图所示
附代码,想测试的同学就直接CV大法;
<!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="" id="form1">
<!-- 创建一个文本框 -->
<input type="text" id="input1" placeholder="请输入文本内容">
<br>
<button id="reset">重置</button>
</form>
<script>
// 获取文本框对象
var ipt = document.getElementById("input1");
var a1 = document.getElementById("reset");
var f1 = document.getElementById("form1");
// 通过 event.target 获取 input 对象,再 .value 获取文本框中的内容
ipt.onchange = function(event){
console.log(event.target.value);
console.log("我输入完了,点击回车输入框失去焦点,触发失焦事件");
}
reset.onclick = function(){
form1.reset();
console.log("点击了重置按钮,清空表单内容");
}
</script>
</body>
</html>
6.5 submit 事件
submit 语法:<form action = "?" onsubmit = "submit事件名称"></form>
<script>
submit事件名称 = function(event){函数体......}
</script>
submit 事件,提交表单触发,也没啥难度,就是绑定表单的提交按钮,然后用户点击提交按钮之后触发 submit 事件。
6.6 小总结
OK,说了这么多,我们来简单总结一下。
(1)上面五个事件中,用的最多的就是 select 事件和 change 事件,其次是 input 事件,再然后是 reset 事件和 submit 事件。
(2)input 事件每当内容改变就会触发,触发频率较为频繁;而 change 则是失去焦点触发,出发频率较低但很好用;
(3)submit 提交事件通常会发送HTTP请求,然后调用后端 Save 保存方法,将用户填写的表单内容全部保存到数据库中。