文章目录
1.认识事件处理
1.1 认识事件
-
Web页面需要经常和用户之间进行交互,而交互的过程中我们可能想要捕捉这个交互的过程:
- 比如用户点击了某个按钮、用户在输入框里面输入了某个文本、用户鼠标经过了某个位置;
- 浏览器需要搭建一条JavaScript代码和事件之间的桥梁;
- 当某个事件发生时,让JavaScript可以响应, 执行某个函数,所以我们需要针对事件编写处理程序(handler);
-
如何进行事件监听呢?
-
事件监听方式一:直接在HTML中编写JavaScript代码监听(很少使用);
-
<button onclick="console.log('按钮发生了点击')">按钮</button>
-
-
事件监听方式二:DOM属性,通过元素的on来监听事件;
-
<button>按钮</button> <script> var btnEl = document.querySelector("button"); btnEl.onclick = function () { console.log("按钮发生了点击"); }; </script>
-
缺点: 无法对同一个事件绑定多个函数
-
-
事件监听方式三:通过EventTarget中的addEventListener来监听;
-
<button>按钮</button> <script> var btnEl = document.querySelector("button"); btnEl.addEventListener("click", function () { console.log("按钮事件监听的第一个函数"); }); btnEl.addEventListener("click", function () { console.log("按钮事件监听的第二个函数"); }); btnEl.addEventListener("click", function () { console.log("按钮事件监听的第三个函数"); }); </script>
-
addEventListener的方式可以同时绑定多个函数
-
-
1.2 常见的事件列表
- 鼠标事件:
- click: 当鼠标点击一个元素时(触摸屏设备会在点击时生成)。
- mouseover / mouseout: 当鼠标指针移入/离开一个元素时。
- mousedown / mouseup: 当在元素上按下/释放鼠标按钮时。
- mousemove: 当鼠标移动时。
- 键盘事件:
- keydown 和 keyup: 当按下和松开一个按键时。
- 表单元素事件:
- submit: 当访问者提交了一个 <form> 时。
- focus: 当访问者聚焦于一个元素时,例如聚焦于一个 <input>。
- Document 事件:
- DOMContentLoaded: 当 HTML 的加载和处理均完成,DOM 被完全构建完成时。
- CSS 事件:
- transitionend: 当一个 CSS 动画完成时。
2.事件冒泡捕获
2.1 认识事件流
-
事实上对于事件有一个概念叫做事件流,为什么会产生事件流呢?
- 我们可以想到一个问题:当我们在浏览器上对着一个元素点击时,你点击的不仅仅是这个元素本身;
- 这是因为我们的HTML元素是存在父子元素叠加层级的;
- 比如一个span元素是放在div元素上的,div元素是放在body元素上的,body元素是放在html元素上的;
-
示例代码:
-
<div class="box"> <span class="word">哈哈哈哈</span> </div> <script> var spanEl = document.querySelector(".word"); var divEl = document.querySelector(".box"); var bodyEl = document.body; spanEl.addEventListener("click", function () { console.log("span元素发生了点击"); }); divEl.addEventListener("click", function () { console.log("div元素发生了点击"); }); bodyEl.addEventListener("click", function () { console.log("body元素发生了点击"); }); </script>
-
此时当我们点击span元素时, div和body元素的事件也会触发
- 触发顺序为:
- 1.span元素发生了点击
- 2.div元素发生了点击
- 3.body元素发生了点击
-
这种事件流也被称为事件冒泡
-
2.2 事件冒泡和事件捕获
-
我们会发现默认情况下事件是从最内层的span向外依次传递的顺序,这个顺序我们称之为事件冒泡(Event Bubble);
-
事实上,还有另外一种监听事件流的方式就是从外层到内层(body -> span),这种称之为事件捕获(Event Capture);
-
为什么会产生两种不同的处理流呢?
- 这是因为早期浏览器开发时,不管是IE还是Netscape公司都发现了这个问题;
- 但是他们采用了完全相反的事件流来对事件进行了传递;
- IE采用了事件冒泡的方式,Netscape采用了事件捕获的方式;
-
那么我们如何去监听事件捕获的过程呢?
-
示例代码:
-
<div class="box"> <span class="word">哈哈哈哈</span> </div> <script> var spanEl = document.querySelector(".word"); var divEl = document.querySelector(".box"); var bodyEl = document.body; spanEl.addEventListener("click", function () { console.log("span元素发生了点击"); }, true); divEl.addEventListener("click", function () { console.log("div元素发生了点击"); }, true); bodyEl.addEventListener("click", function () { console.log("body元素发生了点击"); }, true); </script>
-
在addEventListener中传入第三个参数为true, 可以监听事件捕获
-
此时当我们点击span元素时, div和body元素的事件也会触发
- 触发顺序为:
- 1.body元素发生了点击
- 2.div元素发生了点击
- 3.span元素发生了点击
-
2.3 事件捕获和冒泡的过程
- 如果我们事件捕获和事件冒泡都监听,那么会按照如下顺序来执行:
- 捕获阶段(Capturing phase):
- 事件(从 Window)向下走近元素。
- 目标阶段(Target phase):
- 事件到达目标元素。
- 冒泡阶段(Bubbling phase):
- 事件从元素上开始冒泡。
- 捕获阶段(Capturing phase):
- 事实上,我们可以通过event对象来获取当前的阶段:
- eventPhase
- 开发中通常会使用事件冒泡,所以事件捕获了解即可。
3.事件对象
3.1 事件对象介绍
- 当一个事件发生时,就会有和这个事件相关的很多信息:
- 比如事件的类型是什么,你点击的是哪一个元素,点击的位置是哪里等等相关的信息;
- 那么这些信息会被封装到一个Event对象中,这个对象由浏览器创建,称之为event对象;
- 该对象给我们提供了想要的一些属性,以及可以通过该对象进行某些操作;
- 如何获取这个event对象呢?
- event对象会在传入的事件处理(event handler)函数回调时,被系统传入;
- 我们可以在回调函数中拿到这个event对象;
3.2 event的属性解析
-
常见的属性:
- type:事件的类型;
- target:事件发生的元素;
- currentTarget:当前处理事件的元素;
- eventPhase:事件所处的阶段;
- offsetX、offsetY:事件发生在元素内的位置;
- clientX、clientY:事件发生在客户端内的位置;
- pageX、pageY:事件发生在客户端相对于document的位置;
- screenX、screenY:事件发生相对于屏幕的位置;
-
示例代码:
-
<div class="box"> <span class="btn"> </span> </div> <script> var divEl = document.querySelector("div") var btnEl = document.querySelector(".btn") divEl.onclick = function(event) { // 1.偶尔会使用 console.log("事件类型:", event.type) console.log("事件阶段:", event.eventPhase) // 2.比较少使用 console.log("事件元素中位置", event.offsetX, event.offsetY) console.log("事件客户端中位置", event.clientX, event.clientY) console.log("事件页面中位置", event.pageX, event.pageY) console.log("事件在屏幕中位置", event.screenX, event.screenY) } </script>
-
-
重点区分target/currentTarget
-
CSS代码
-
<style> .box { display: flex; width: 200px; height: 200px; background-color: orange; } span { width: 100px; height: 100px; background-color: #f00; } </style>
-
<div class="box"> <span class="btn"> </span> </div> <script> var divEl = document.querySelector("div") var btnEl = document.querySelector(".btn") divEl.onclick = function(event) { // 1.偶尔会使用 console.log("事件类型:", event.type) console.log("事件阶段:", event.eventPhase) // 2.比较少使用 console.log("事件元素中位置", event.offsetX, event.offsetY) console.log("事件客户端中位置", event.clientX, event.clientY) console.log("事件页面中位置", event.pageX, event.pageY) console.log("事件在屏幕中位置", event.screenX, event.screenY) // 3.target/currentTarget //此时点击span时 span是事件发生的对象 div是事件处理的对象 console.log(event.target) //事件发生的对象 <span class="btn"></span> console.log(event.currentTarget) //事件处理的对象 <div class="box">...</div> } </script>
-
此时点击span时 span是事件发生的对象 div是事件处理的对象
-
3.3 event的方法解析
-
常见的方法:
-
preventDefault:取消事件的默认行为;
-
<a href="http://www.baidu.com">百度</a> <script> var aEl = document.querySelector("a"); aEl.onclick = function (event) { event.preventDefault(); }; </script>
-
默认情况下, 上面代码中, 点击a标签会跳转到百度, 当我们使用preventDefault()方法取消默认行为后, 点击a标签就不会跳转
-
-
stopPropagation:阻止事件的进一步传递(冒泡或者捕获都可以阻止);
-
<style> .box { width: 200px; height: 200px; background-color: skyblue; } .box span { display: block; width: 100px; height: 100px; background-color: purple; } </style>
-
<div class="box"> <span> <button>按钮</button> </span> </div> <script> var btnEl = document.querySelector("button"); var spanEl = document.querySelector("span"); var divEl = document.querySelector(".box"); // 监听捕获阶段 divEl.addEventListener("click", function () { console.log("div的事件捕获监听"); }, true); spanEl.addEventListener("click", function () { console.log("span的事件捕获监听"); }, true); btnEl.addEventListener("click", function (event) { console.log("buttom的事件捕获监听"); event.stopPropagation() }, true); // 监听冒泡阶段 divEl.addEventListener("click", function () { console.log(""); }); spanEl.addEventListener("click", function () { console.log("span的事件冒泡监听"); }); btnEl.addEventListener("click", function () { console.log("buttom的事件冒泡监听"); }); </script>
-
上面代码中默认情况下的执行顺序为:
- div的事件捕获监听
- span的事件捕获监听
- buttom的事件捕获监听
- buttom的事件冒泡监听
- span的事件冒泡监听
- div的事件冒泡监听
-
当我们通过stopPropagation()方法将某一个元素的事件传递阻止时, 那么执行到该元素后, 不会在继续往下捕获或者冒泡
-
-
3.4 事件处理函数中this
- 在函数中,我们也可以通过this来获取当前的发生元素
- 这是因为在浏览器内部,调用event handler是绑定到当前的target上的
- 事件处理函数中的this指向事件的绑定对象
4.EventTarget类
-
我们会发现,所有的节点、元素都继承自EventTarget
- 事实上Window也继承自EventTarget;
-
那么这个EventTarget是什么呢?
- EventTarget是一个DOM接口,主要用于添加、删除、派发Event事件;
-
EventTarget常见的方法:
-
addEventListener:注册某个事件类型以及事件处理函数;
-
removeEventListener:移除某个事件类型以及事件处理函数;
-
removeEventListener的注意事项:
-
错误做法: btn函数并没有移除, 因为这种写法, 移除方法中相当于创建了一个新函数, 并不是移除添加事件方法中的函数
-
<button>按钮</button> <script> var btnEl = document.querySelector("button"); // 错误做法 这种做法是无法移除的 btnEl.addEventListener("click", function () { console.log("btn的监听函数"); }); btnEl.removeEventListener("click", function () {}); </script>
-
正确做法: 在事件绑定时, 不能传入一个匿名函数, 需要传入一个有名称的函数才可以移除
-
<button>按钮</button> <script> var btnEl = document.querySelector("button"); var foo = function () { console.log("btn的监听函数"); }; btnEl.addEventListener("click", foo); btnEl.removeEventListener("click", foo); </script>
-
-
dispatchEvent:派发某个事件类型到EventTarget上;
-
示例代码:
-
// 使用一个自己的的事件 window.addEventListener("mine", function () { console.log("监听到我自己的事件了"); }); // 隔五秒钟后 派发一个事件类型 setTimeout(function () { window.dispatchEvent(new Event("mine")); }, 5000);
-
-
5.事件委托
-
事件冒泡在某种情况下可以帮助我们实现强大的事件处理模式 – 事件委托模式(也是一种设计模式)
-
那么这个模式是怎么样的呢?
- 因为当子元素被点击时,父元素可以通过冒泡可以监听到子元素的点击;
- 并且可以通过event.target获取到当前监听的元素;
-
案例:一个ul中存放多个li,点击某一个li会变成红色, 其他的恢复默认色
-
方案一:监听每一个li的点击,并且做出响应;
-
<ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ul> <script> var liEl = document.querySelectorAll("li"); // 循环为li绑定事件 for (var i = 0; i < liEl.length; i++) { // 将所有按钮样式全部清除 liEl[i].onclick = function () { for (var i = 0; i < liEl.length; i++) { liEl[i].style.color = ""; } // 在为当前按钮单独设置样式 this.style.color = "red"; }; } </script>
-
-
方案二:在ul中监听点击,并且通过event.target拿到对应的li进行处理;(事件委托的模式)
-
因为这种方案并不需要遍历后给每一个li上添加事件监听,所以它更加高效;
-
并解决点击ul时会添加所有样式的小bug
-
<ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ul> <script> var ulEl = document.querySelector("ul"); // 给ul添加事件 ulEl.onclick = function (event) { //判断当前点击元素是否是ulEl 如果不是移除其他li的样式 for (var i = 0; i < ulEl.children.length; i++) { if (event.target !== ulEl) { ulEl.children[i].style.color = ""; } } // 当前点击元素不为ul时 单独为当前点击的li元素添加样式 if (event.target !== ulEl) { event.target.style.color = "red"; } }; </script>
-
-
上面代码中采用for循环将所有的样式移除了一遍, 而有些是原本就没有样式的, 这样做性能并不高
-
上面代码中提高性能的方法
-
<ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ul> <script> var ulEl = document.querySelector("ul"); // 用一个变量记录哪一个元素添加了样式 var colorEl = null; // 给ul添加事件 ulEl.onclick = function (event) { // 将设置了样式的元素 移除样式 并且判断当前点击元素是否是ulEl 如果不是才移除 if (colorEl && event.target !== ulEl) { colorEl.style.color = ""; } // 为当前点击的li元素添加样式 并且判断当前点击元素是否是ulEl 如果不是才添加 if (event.target !== ulEl) { event.target.style.color = "red"; } // 并将添加了样式的元素保存在colorEl中 colorEl = event.target; }; </script>
-
-
6.常见的事件
6.1 常见鼠标事件
-
接下来我们来看一下常见的鼠标事件(不仅仅是鼠标设备,也包括模拟鼠标的设备,比如手机、平板电脑)
-
常见的鼠标事件:
-
属性 描述 click 当用户点击某个对象时调用的事件句柄。 contextmenu 在用户点击鼠标右键打开上下文菜单时触发 dblclick 当用户双击某个对象时调用的事件句柄。 mousedown 鼠标按钮被按下。 mouseup 鼠标按键被松开。 mouseover 鼠标移到某元素之上。(支持冒泡) mouseout 鼠标从某元素移开。(支持冒泡) mouseenter 当鼠标指针移动到元素上时触发。(不支持冒泡) mouseleave 当鼠标指针移出元素时触发。(不支持冒泡) mousemove 鼠标被移动。
-
-
mouseover和mouseenter的区别
- mouseenter和mouseleave
- 不支持冒泡
- 进入子元素依然属于在该元素内,没有任何反应
- mouseover和mouseout
- 支持冒泡
- 进入元素的子元素时
- 先调用父元素的mouseout
- 再调用子元素的mouseover
- 因为支持冒泡,所以会将mouseover传递到父元素中;
- mouseenter和mouseleave
6.2 常见键盘事件
-
常见的键盘事件:
-
属性 描述 onkeydown 某个键盘按键被按下。 onkeypress 某个键盘按键被按下。 onkeyup 某个键盘按键被松开。
-
-
键盘事件的执行顺序是: onkeydown、onkeypress、onkeyup
- down当键盘按下去的那一刻, 事件发生;
- press在文本被输入的时候, 事件发生;
- up发生在文本输入完成(按键抬起);
-
我们可以通过event对象的key和code来区分按下的键:
- code:“按键代码”(“KeyA”,“ArrowLeft” 等),特定于键盘上按键的物理位置。
- key:字符(“A”,“a” 等),对于非字符(non-character)的按键,通常具有与 code 相同的值。)
-
示例代码:
-
<input type="text" /> <button>搜索</button> <script> var inputEl = document.querySelector("input"); var buttonEl = document.querySelector("button"); // 点击按钮实现搜索功能 buttonEl.onclick = function () { console.log("搜索", inputEl.value); }; // 点击键盘Enter键 实现搜索功能 inputEl.onkeyup = function (event) { if (event.key == "Enter") { console.log("搜索", inputEl.value); } }; // 按下s自动获取焦点 document.onkeyup = function (event) { if (event.code === "KeyS") { inputEl.focus(); } }; </script>
-
6.3 常见表单事件
-
针对表单也有常见的事件:
-
属性 onchange 该事件在表单元素的内容改变时触发( <input>, <keygen>, <br/><select>, 和 <textarea>) oninput 元素获取用户输入时触发 onfocus 元素获取焦点时触发 onblur 元素失去焦点时触发 onreset 表单重置时触发 onsubmit 表单提交时触发
-
-
示例代码:
-
获取焦点失去焦点
-
<input type="text" /> <script> var inputEl = document.querySelector("input"); // 获取焦点 inputEl.onfocus = function () { console.log("用户获取焦点"); }; // 失去焦点 inputEl.onblur = function () { console.log("用户失去焦点"); }; </script>
-
input和change注意事项
-
<input type="text" /> <script> var inputEl = document.querySelector("input"); // input在用户输入的过程中就会触发事件 inputEl.oninput = function () { console.log("用户正在输入内容"); }; // change在确定内容发生改变时(比如失去焦点时)才会触发 inputEl.onchange = function () { console.log("用户输入内容改变"); }; </script>
-
重置和提交事件要监听form表单域
-
<form action=""> <input type="text" /> <button type="reset">重置</button> <button type="submit">提交</button> </form> <script> var formEl = document.querySelector("form"); // 虽然点击的是重置或提交按钮 但是事件发生在form上 formEl.onreset = function () { console.log("发生了重置事件"); }; formEl.onsubmit = function () { console.log("发生了提交事件"); }; </script>
-
6.4 文档加载事件
-
DOMContentLoaded:浏览器已完全加载 HTML,并构建了 DOM 树,但像 <img> 和样式表之类的外部资源可能尚未加载
完成。 -
load:浏览器不仅加载完成了 HTML,还加载完成了所有外部资源:图片,样式等。
-
示例代码: 当我们js代码写在html结构上方时获取元素
-
<script> // DOMContentLoaded事件, 内部资源加载完成 window.addEventListener("DOMContentLoaded", function () { // 1.这里可以操作box, 因为内部资源已经加载完毕 var boxEl = document.querySelector(".box") boxEl.style.backgroundColor = "orange" // 2.但是不能获取img对应的图片的宽度和高度, 外部资源没有加载完成 // var imgEl = document.querySelector("img"); // console.log("图片的宽度和高度:", imgEl.offsetWidth, imgEl.offsetHeight); }); // load事件, 所有资源加载完成 window.onload = function () { // 这里可以获取图片的宽度和高度 var imgEl = document.querySelector("img") console.log("图片的宽度和高度:", imgEl.offsetWidth, imgEl.offsetHeight) }; </script> <div class="box"> <p>哈哈哈啊</p> </div> <a href="#">百度一下</a> <img src="../images/kobe01.jpg" alt="" />
-
-
resize: 窗口大小发生改变的事件
-
window.onresize = function () { console.log("窗口大小发生改变时"); };
-
-
更多事件类型:https://developer.mozilla.org/zh-CN/docs/Web/Events
7.window定时器方法
7.1 定时器简介
- 有时我们并不想立即执行一个函数,而是等待特定一段时间之后再执行,我们称之为**“计划调用(scheduling a call)”。**
- 目前有两种方式可以实现:
- setTimeout 允许我们将函数推迟到一段时间间隔之后再执行。
- setInterval 允许我们重复运行一个函数,从一段时间间隔之后开始运行,之后以该时间间隔连续重复运行该函数。
- 并且通常情况下有提供对应的取消方法:
- clearTimeout:取消setTimeout的定时器;
- clearInterval:取消setInterval的定时器;
- 大多数运行环境都有内置的调度程序,并且提供了这些方法:
- 目前来讲,所有浏览器以及 Node.js 都支持这两个方法;
- 所以我们后续学习Node的时候,也可以在Node中使用它们;
7.2 setTimeout的使用
-
setTimeout的语法如下:
-
var timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...)
-
func|code:想要执行的函数或代码字符串。
-
一般传入的都是函数,由于某些历史原因,支持传入代码字符串,但是不建议这样做;
-
delay:执行前的延迟时间,以毫秒为单位(1000 毫秒 = 1 秒),默认值是 0;
-
arg1,arg2…:要传入被执行函数(或代码字符串)的参数列表;
-
-
clearTimeout方法:
-
setTimeout 在调用时会返回一个“定时器标识符(timer identifier)”,我们可以使用它来取消执行。
-
// 可以传额外参数arg1,arg2… 很少用 var timerId = setTimeout(function (name, age) { console.log("定时器触发", name, age) }, 3000, "coder", 18) // 利用返回的定时器标识符(timerId)取消定时器 clearTimeout(timerId)
-
7.3 setInterval的使用
-
setInterval 方法和 setTimeout 的语法相同:
-
var timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...)
-
所有参数的意义也是相同的;
-
不过与 setTimeout 只执行一次不同,setInterval 是每间隔给定的时间周期性执行;
-
-
clearInterval方法:
-
setInterval也会返回一个“定时器标识符(timer identifier)”,我们可以通过clearInterval来取消这个定时器。
-
// 可以传额外参数arg1,arg2… 很少用 var timerId = setInterval(function (name, age) { console.log("定时器重复触发", name, age) }, 3000, "coder", 18) // 利用返回的定时器标识符(timerId)取消定时器 clearTimeout(timerId)
-
-
关于定时器还有一些宏任务相关的概念,我们会在JavaScript高级中讲解。
on (name, age) {
console.log(“定时器触发”, name, age)
}, 3000, “coder”, 18)
// 利用返回的定时器标识符(timerId)取消定时器
clearTimeout(timerId)
7.3 setInterval的使用
-
setInterval 方法和 setTimeout 的语法相同:
-
var timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...)
-
所有参数的意义也是相同的;
-
不过与 setTimeout 只执行一次不同,setInterval 是每间隔给定的时间周期性执行;
-
-
clearInterval方法:
-
setInterval也会返回一个“定时器标识符(timer identifier)”,我们可以通过clearInterval来取消这个定时器。
-
// 可以传额外参数arg1,arg2… 很少用 var timerId = setInterval(function (name, age) { console.log("定时器重复触发", name, age) }, 3000, "coder", 18) // 利用返回的定时器标识符(timerId)取消定时器 clearTimeout(timerId)
-
-
关于定时器还有一些宏任务相关的概念,我们会在JavaScript高级中讲解。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/120151.html