视频加载失败

JavaScript的DOM之事件

3040 字
15 分钟
JavaScript的DOM之事件

本章主要学习DOM里面的事件,包括事件监听、传播、对象和委托

事件监听#

什么是“事件监听”

  • DOM允许我们书写JavaScript代码以让HTML元素对事件作 出反应
  • 什么是“事件”:用户与网页的交互动作
  • “监听”,顾名思义,就是让计算机随时能够发现这个事件 发生了,从而执行程序员预先编写的一些程序
  • 设置事件监听的方法主要有onxxx和addEventListener() 两种,二者的区别将在“事件传播”一课中介绍

最简单的设置事件监听的方法

最简单的给元素设置事件监听的方法就是设置它们的onxxx 属性,像这样:

oBox.onclick = function () {
// 点击盒子时,将执行这里的语句
}

常见的鼠标事件监听

事件名事件描述
onclick当鼠标单击某个对象
ondblclick当鼠标双击某个对象
onmousedown当某个鼠标按键在某个对象上被按下
onmouseup当某个鼠标按键在某个对象上被松开
onmousemove当某个鼠标按键在某个对象上被移动
onmouseenter当鼠标进入某个对象(相似事件onmouseover)
onmouseleave当鼠标离开某个对象(相似事件onmouseout)

常见的键盘事件监听

事件名事件描述
onkeypress当某个键盘的键被按下(系统按钮如箭头键和 功能键无法得到识别)
onkeydown当某个键盘的键被按下(系统按钮可以识别, 并且会先于onkeypress发生)
onkeyup当某个键盘的键被松开

常见的表单事件监听

事件名事件描述
onchange当用户改变域的内容
onfocus当某元素获得焦点(比如tab键或鼠标点击)
onblur当某元素失去焦点
onsubmit当表单被提交
onreset当表单被重置

常见的页面事件监听

事件名事件描述
onload当页面或图像被完成加载
onunload当用户退出页面

事件传播#

思考一个问题

  • 如果一个div里面嵌套div,这个div里边又嵌套div,当点击div的时候,点击的是哪个div呢

研究:当盒子嵌套时事件监听的执行顺序

监听box1的 onclick事件;监听box2的 onclick事件;监听了box3的 onclick事件

<style>
#box1{
width: 202px;
height: 202px;
padding: 50px;
border: 1px solid red;
}
#box2{
width: 100px;
height: 100px;
padding: 50px;
border: 1px solid red;
}
#box3{
width: 100px;
height: 100px;
border: 1px solid red;
}
</style>
<body>
<div id="box1">
<div id="box2">
<div id="box3"></div>
</div>
</div>
<script>
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var box3 = document.getElementById("box3");
box1.onclick = function () {
console.log("点击了box1");
}
box2.onclick = function () {
console.log("点击了box2");
}
box3.onclick = function () {
console.log("点击了box3");
}
box1.addEventListener("click",function () {
console.log("点击了box1");
},true)
box2.addEventListener("click",function () {
console.log("点击了box2");
},true)
box3.addEventListener("click",function () {
console.log("点击了box3");
},true)
</script>
</body>
  • 输出:点击了box3;点击了box2;点击了box1
  • 通过测试:感觉事件的传播是从内到外的(不成熟)
  • 实际上,事件的传播是:先从外到内,然后再从内到外
  • 从外到内叫捕获阶段;从内到外叫冒泡阶段

onxxx写法只能监听冒泡阶段

  • onxxx这样的写法只能监听冒泡阶段(这样也证明了刚才为什么只监听到冒泡阶段)

addEventListener()方法(监听捕获阶段)

  • DOM0级事件监听:只能监听冒泡阶段

  • DOM2级事件监听

    //click;事件名不加on
    oBox.addEventListener('click', function () {
    // 这是事件处理函数
    }, true);//true监听捕获阶段,false监听冒泡阶段

注意事项

  • 最内部元素不再区分捕获和冒泡阶段,会先执行写在前面的 监听,然后执行后写的监听
  • 如果给元素设置相同的两个或多个同名事件,则DOM0级写法 后面写的会覆盖先写的;而DOM2级会按顺序执行

事件对象#

什么是事件对象

  • 事件处理函数提供一个形式参数,它是一个对象,封装了本 次事件的细节

  • 这个参数通常用单词event或字母e来表示

    oBox.onmousemove = function (e) {
    // 对象e就是这次事件的“事件对象”
    };

鼠标位置

属性属性描述
clientX鼠标指针相对于浏览器的水平坐标
clientY鼠标指针相对于浏览器的垂直坐标
pageX鼠标指针相对于整张网页的水平坐标
pageY鼠标指针相对于整张网页的垂直坐标
offsetX鼠标指针相对于事件源元素的水平坐标
offsetY鼠标指针相对于事件源元素的垂直坐标
<style>
body{
height: 1000px;
}
#app{
width: 200px;
height: 200px;
background-color: pink;
}
#info{
font-size: 30px;
}
</style>
</head>
<body>
<div id="app"></div>
<div id="info"></div>
<script>
var info = document.getElementById("info");
//事件对象
document.getElementById("app").onmousemove = function (e) {
//获取鼠标移动到的位置
info.innerHTML = "x相对于浏览器窗口:" + e.clientX + " y相对于浏览器窗口:" + e.clientY+"<br>";
info.innerHTML += "x相对于元素:" + e.offsetX + " y相对于元素:" + e.offsetY+"<br>";
info.innerHTML += "x相对于文档:" + e.pageX + " y相对于文档:" + e.pageY;
}
</script>
</body>

e.charCode和e.keyCode属性

  • e.charCode属性通常用于onkeypress事件中,表示用户输 入的字符的“字符码”
  • e.keyCode属性通常用于onkeydown事件和onkeyup中,表 示用户按下的按键的“键码”

charCode字符码

字符字符码
数字0 ~ 数字948 ~ 57
大写字母A ~ Z65 ~ 90
小写字母a ~ z97 ~ 122

keyCode键码

按键键码
数字0 ~ 数字948 ~ 57(同charCode键码完全相同)
字母不分大小a ~ z65 ~ 90(同charCode键码的大写字母A ~ Z,而 keyCode不分大小写,一律为65 ~ 90)
四个方向键← ↑ → ↓37、38、39、40
回车键13
空格键32
<body>
<input type="text" name="" id="field">
<h1 id="info"></h1>
<input type="text" name="" id="field1">
<h1 id="info1"></h1>
<script>
var field = document.getElementById("field");
var info = document.getElementById("info");
var field1 = document.getElementById("field1");
var info1 = document.getElementById("info1");
//事件对象
field.onkeydown = function (e) {
info.innerHTML += "keyCode:" + e.keyCode;
}
field1.onkeypress = function (e) {
info1.innerHTML += "charCode:" + e.charCode;
}
</script>
</body>

小案例

制作一个特效:按方向键可以控制页面上的盒子移动

<style>
#box{
position: absolute;
width: 100px;
height: 100px;
top: 200px;
left: 200px;
background-color: pink;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
var box = document.getElementById("box");
var t = 200;
var l = 200;
document.onkeydown = function (e) {
switch (e.keyCode) {
case 38:
t -= 10;
break;
case 40:
t += 10;
break;
case 37:
l -= 10;
break;
case 39:
l += 10;
break;
}
box.style.left = l + "px";
box.style.top = t + "px";
}
</script>
</body>

e.preventDefault()方法

  • e.preventDefault()方法用来阻止事件产生的“默认动作”
  • 一些特殊的业务需求,需要阻止事件的“默认动作”

小案例1

制作一个文本框,只能让用户在其中输入小写字母和数字,其他字符输入没有效果

<body>
<input type="text" id="inp">
<script>
var inp = document.getElementById("inp");
inp.onkeypress = function (e) {
console.log(e.charCode);
// 限制只能输入数字和字母
// 48-57 0-9
// 97-122 a-z
if(!(e.charCode >= 48 && e.charCode <= 57||e.charCode >= 97 && e.charCode <= 122)){
e.preventDefault();
}
}
</script>
</body>

小案例2

制作鼠标滚轮事件:当鼠标在盒子中向下滚动时,数字加1; 反之,数字减1

鼠标滚轮事件是onmousewheel,它的事件对象e提供deltaY 属性表示鼠标滚动方向,向下滚动时返回正值,向上滚动时返 回负值

<style>
#box{
width: 500px;
height: 500px;
background-color: pink;
}
body{
height: 1000px;
}
</style>
</head>
<body>
<div id="box"></div>
<h1 id="num">0</h1>
<script>
//制作鼠标滚轮事件:当鼠标在盒子中向下滚动时,数字加1; 反之,数字减1
var box = document.getElementById("box");
var num = document.getElementById("num");
var a = 0;
box.onmousewheel = function (e) {
console.log(e.deltaY);
// 阻止默认事件,当在盒子里面滚动的时候不会引发页面的滚动
e.preventDefault();
if(e.deltaY > 0){
a--;
}else{
a++;
}
num.innerText = a;
}
</script>
</body>

e. stopPropagation()方法

  • e.stopPropagation()方法用来阻止事件继续传播
  • 在一些场合,非常有必要切断事件继续传播,否则会造成页 面特效显示出bug

小案例:

制作一个弹出层:点击按钮显示弹出层,点击网页任意地方, 弹出层关闭

<style>
#menu{
width: 400px;
height: 140px;
background-color: pink;
position: absolute;
top:50%;
left:50%;
transform: translate(-50%,-50%);
margin: 0 auto;
display: none;
}
</style>
</head>
<body>
<button id="btn">点击弹出菜单</button>
<div id="menu"></div>
<script>
var btn = document.getElementById("btn");
var menu = document.getElementById("menu");
// 点击按钮弹出菜单
btn.onclick = function (e) {
// 阻止事件冒泡,如果不阻止事件冒泡,点击按钮会触发点击其他地方隐藏菜单的事件,导致菜单显示后立即隐藏
e.stopPropagation();
menu.style.display = "block";
}
// 点击其他地方隐藏菜单
document.onclick = function (e) {
menu.style.display = "none";
}
// 点击菜单不隐藏菜单
menu.onclick = function (e) {
e.stopPropagation();
}
</script>
</body>

事件委托#

批量添加事件监听

  • 题目:页面上有一个无序列表ul,它内部共有20个li元素,请批量给它们添加点击事件监听,实现效果:点击 哪个li元素,哪个li元素就变红

    <body>
    <ul id="list">
    </ul>
    <script>
    var list = document.getElementById("list");
    //创建20个li元素
    for(var i=0;i<20;i++){
    var li = document.createElement("li");
    li.style.cursor = "pointer";//小手指针
    li.innerHTML = "列表项"+i;
    list.appendChild(li);
    }
    var lis = list.getElementsByTagName("li");
    // 给每个li添加点击事件监听
    //再次点击列表项,背景颜色变回白色
    for(var i=0;i<lis.length;i++){
    lis[i].onclick = function () {
    if(this.style.backgroundColor == "red"){
    this.style.backgroundColor = "white";
    }else{
    this.style.backgroundColor = "red";
    }
    }
    }
    </script>
    </body>

批量添加事件监听的性能问题

  • 每一个事件监听注册都会消耗一定的系统内存,而批量添 加事件会导致监听数量太多,内存消耗会非常大
  • 实际上,每个li的事件处理函数都是不同的函数,这些 函数本身也会占用内存

新增元素动态绑定事件

  • 题目:页面上有一个无序列表ul,它内部没有li元素, 请制作一个按钮,点击这个按钮就能增加一个li元素。并 且要求每个增加的li元素也要有点击事件监听,实现效果 点击哪个li元素,哪个li元素就变红

    <body>
    <button>点击添加li项</button>
    <ul id="list"></ul>
    <script>
    var btn = document.querySelector("button");
    var list = document.getElementById("list");
    btn.onclick = function () {
    var li = document.createElement("li");
    li.innerHTML = "列表项"+list.children.length;
    list.appendChild(li);
    // 给li添加点击事件监听 性能问题
    li.onclick = function () {
    this.style.backgroundColor = "red";
    }
    }
    </script>
    </body>

动态绑定事件的问题

  • 新增元素必须分别添加事件监听,不能自动获得事件监听
  • 大量事件监听、大量事件处理函数都会产生大量消耗内存

什么是事件委托

  • 利用事件冒泡机制,将后代元素事件委托给祖先元素

e.target和e.currentTarget属性

事件委托通常需要结合使用e.target属性

属性属性描述
target实际触发事件的元素,即事件源元素
currentTarget当前正在执行事件处理程序的元素,即绑定该事件处理程序的元素
<body>
<button>点击添加li项</button>
<ul id="list"></ul>
<script>
var btn = document.querySelector("button");
var list = document.getElementById("list");
// 点击按钮,添加li项
btn.onclick = function () {
var li = document.createElement("li");
li.innerHTML = "列表项"+list.children.length;
list.appendChild(li);
// 给li添加点击事件监听 性能问题
// li.onclick = function () {
// this.style.backgroundColor = "red";
// }
}
// 事件委托,给ul添加点击事件监听,点击li项,背景颜色变红
list.onclick = function (e) {
////只有点击了li项,才变红
if(e.target.tagName === "LI"){
e.target.style.backgroundColor = "red";
}
};
</script>
</body>

事件委托的使用场景

  • 当有大量类似元素需要批量添加事件监听时,使用事件委 托可以减少内存开销
  • 当有动态元素节点上树时,使用事件委托可以让新上树的 元素具有事件监听

使用事件委托时需要注意的事项

  • onmouseenter和onmouseover都表示“鼠标进入”,它 们有什么区别呢?

    onmouseenter不冒泡,onmouseover冒泡。

  • 使用事件委托时要注意:不能委托不冒泡的事件给祖先元素

  • 最内层元素不能再有额外的内层元素了,比如:

    <ul id="list">
    <li><span>我是span</span>列表项</li>
    <li><span>我是span</span>列表项</li>
    <li><span>我是span</span>列表项</li>
    </ul>

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

JavaScript的DOM之事件
https://f.youzi177.cn/posts/2026102003javascript-de-dom-zhi-shi-jian/
作者
小封鸭
发布于
2026-10-20
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
小封鸭
小楼一夜听春雨,深巷明朝卖杏花
公告
欢迎来到我的博客。这里主要记录前端全栈和日常内容
分类
标签
最新动态
站点统计
文章
60
分类
3
标签
12
总字数
107,734
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
EdgeOne Pages
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录