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;事件名不加onoBox.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 ~ 数字9 | 48 ~ 57 |
| 大写字母A ~ Z | 65 ~ 90 |
| 小写字母a ~ z | 97 ~ 122 |
keyCode键码
| 按键 | 键码 |
|---|---|
| 数字0 ~ 数字9 | 48 ~ 57(同charCode键码完全相同) |
| 字母不分大小a ~ z | 65 ~ 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>
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!














