事件监听
- 一、事件监听
- 1.1 什么是事件、事件监听
- 1.1.1 事件监听的基本流程
- 1.1.2 重点关注以下三个核心要素
- 1.2 Event handling models 事件处理模型
- 1.2.1 DOM Level 版本
- (1)DOM Level 0
- (2)DOM Level 2
- 1.2.2 事件类型
- (1)焦点事件(表单)
- (2)键盘事件
- (3)文本
- 1.2.3 事件对象
- (1)常用事件对象属性
- 1. `type`
- 2. `clientX` / `clientY`
- 3. `offsetX` / `offsetY`
- 4. `key`
- (2)环境对象
- 1. 全局上下文
- 2. 函数上下文
- 3. 对象方法
- 4. 构造函数
- 5. 事件处理程序
- 1.3 回调函数
- 二、事件流、委托
- 2.1 事件流(Event Stream)
- 2.1.1 事件流
- 2.1.2 两个阶段
- (1)事件捕获
- (2)事件冒泡
- (3)阻止冒泡
- 主要区别:
- 2.1.3 解绑事件
- 2.1.4 两种注册事件方式的区别
- 1. 传统的注册 (L0)
- 2. 事件监听注册 (L2)
- 总结:
- 2.2 事件委托
- 2.3 其他
- 2.3.1 页面加载事件
- 1. 页面加载事件的种类及其用途
- load事件
- DOMContentLoaded事件
- 2. 使用场景和选择
- 2.3.2 元素滚动事件
- (1)获取位置
- (2)注意
- (3)将页面滚动到指定的坐标。
- 2.3.3 页面尺寸事件
- (1)页面尺寸事件
- (2)获取元素宽高
- (3)获取元素相对于其最近的已定位父级元素
- (4)flexible.js
一、事件监听
DOM(文档对象模型)事件是某事已经发生或正在发生的信号,可以由用户交互或浏览器触发。
1.1 什么是事件、事件监听
(1)事件(Event)是指在计算机程序中发生的某种特定的操作或变化,通常会触发一些特定的反应。在编程和计算机科学中,事件可以是用户的操作(如点击按钮、输入文字等)、系统的变化(如文件被修改、网络连接状态改变等),或者程序内部的状态变化。
(2)事件监听(Event Listening)是指在程序中设置一个“监听器”来监视特定的事件。一旦事件发生,监听器会触发相应的代码或函数,处理事件并做出响应。事件监听是一种常见的编程模式,广泛用于用户界面编程、网络编程等领域。
1.1.1 事件监听的基本流程
- 注册监听器:程序中设置一个事件监听器,指定要监听的事件类型及其对应的处理函数。
- 等待事件发生:程序处于等待状态,当指定的事件发生时,事件监听器被激活。
- 触发处理函数:事件监听器触发与事件相关联的处理函数,执行相应的操作。
// 选择一个按钮元素
const button = document.getElementById('myButton');
// 定义事件处理函数
function handleClick(event) {
alert('按钮被点击了!');
}
// 注册点击事件监听器
button.addEventListener('click', handleClick);
在这个示例中,addEventListener
方法用来将 handleClick
函数注册为 button
元素的点击事件监听器。当用户点击按钮时,handleClick
函数会被调用,并弹出一个提示框。
事件监听是现代编程中不可或缺的一部分,能够帮助程序对用户交互和系统变化作出实时反应。
1.1.2 重点关注以下三个核心要素
-
事件源(Event Source):
- 事件源是发出事件的对象或元素。例如,在网页上,这可能是一个按钮、一个输入框或者任何其他可交互的元素。了解事件源是关键,因为事件处理函数是绑定到这些源上的。
-
事件类型(Event Type):
- 事件类型定义了事件的种类,如“click”(点击)、“submit”(提交)、“keydown”(按键按下)等。明确事件类型是选择正确的事件处理函数和制定相应逻辑的基础。
-
事件处理函数(Event Handler):
- 事件处理函数是执行具体逻辑的代码。它会在指定的事件发生时被调用。事件处理函数通常接受一个事件对象作为参数,提供关于事件的详细信息。
// 1. 事件源
var button = document.getElementById('myButton');
// 2. 事件类型
var eventType = 'click';
// 3. 事件处理函数
function handleClick(event) {
alert('按钮被点击了!');
}
// 注册事件监听器
button.addEventListener(eventType, handleClick);
- 事件源 是
button
元素。 - 事件类型 是
click
。 - 事件处理函数 是
handleClick
,当点击事件发生时,它会弹出提示框。
1.2 Event handling models 事件处理模型
1.2.1 DOM Level 版本
(1)DOM Level 0
- 事件处理方式:DOM Level 0 是最早的 DOM 规范,事件处理方法相对简单,主要通过 HTML 属性(如
onclick
、onmouseover
)直接在 HTML 元素中定义事件处理函数。 -
<button onclick="alert('按钮被点击了!')">点击我</button>
(2)DOM Level 2
- 事件处理方式:DOM Level 2 引入了
addEventListener
和removeEventListener
方法,这些方法提供了更强大的事件管理功能,比如支持事件冒泡和捕获,允许对同一事件类型绑定多个处理函数。 -
<button id="myButton">点击我</button> <script> var button = document.getElementById('myButton'); button.addEventListener('click', function() { alert('按钮被点击了!'); }); </script>
1.2.2 事件类型
-
鼠标事件(Mouse Events):
- Click:点击事件,当用户点击鼠标时触发。
- Double Click:双击事件,当用户双击鼠标时触发。
- Mouse Down:鼠标按下事件,当用户按下鼠标按钮时触发。
- Mouse Up:鼠标释放事件,当用户释放鼠标按钮时触发。
- Mouse Enter:鼠标进入事件,当鼠标指针进入元素时触发。
- Mouse Leave:鼠标离开事件,当鼠标指针离开元素时触发。
-
键盘事件(Keyboard Events):
- Key Down:按键按下事件,当用户按下键盘上的键时触发。
- Key Up:按键释放事件,当用户释放键盘上的键时触发。
- Key Press:按键事件,该事件在某些情况下会触发,但在现代浏览器中通常已不再推荐使用。
-
表单事件(Form Events):
- Focus:焦点事件,当表单元素获得焦点时触发。
- Blur:失焦事件,当表单元素失去焦点时触发。
- Change:变化事件,当表单元素的值发生改变时触发。
- Submit:提交事件,当用户提交表单时触发。
-
窗口事件(Window Events):
- Load:加载事件,当页面或资源加载完成时触发。
- Resize:调整大小事件,当浏览器窗口大小改变时触发。
- Scroll:滚动事件,当页面滚动时触发。
-
其它事件:
- Error:错误事件,当资源加载失败或发生其他类型的错误时触发。
- Context Menu:上下文菜单事件,当用户右键点击时触发。
(1)焦点事件(表单)
<input type="text" value="134223">
<script>
const input = document.querySelector('input');
input.addEventListener('focus', function () {
console.log("focus");
})
input.addEventListener('blur', function () {
console.log("blur");
})
</script>
(2)键盘事件
(3)文本
1.2.3 事件对象
事件对象是事件处理函数的一个参数,它包含了有关事件的信息。事件对象提供了事件发生时的各种细节,例如事件的类型、目标元素、鼠标位置等。
通常,当你为一个事件(比如点击事件、键盘事件等)添加一个事件处理函数时,事件对象会自动作为参数传递给这个处理函数。
document.getElementById("myButton").addEventListener("click", function(event) {
console.log(event.type); // 事件类型,比如 "click"
console.log(event.target); // 事件目标元素
console.log(event.clientX, event.clientY); // 鼠标点击的坐标
});
在这个例子中,event
就是事件对象。它包含了很多属性和方法,比如:
event.type
:事件的类型(如 “click”、“keydown”)event.target
:触发事件的目标元素event.currentTarget
:事件处理函数绑定的元素event.preventDefault()
:阻止事件的默认行为event.stopPropagation()
:阻止事件的进一步传播
事件对象的具体属性和方法可以根据不同的事件类型而有所不同。
是的,以下是一些常用的事件对象属性和它们的描述:
(1)常用事件对象属性
1. type
- 描述:获取当前的事件类型,例如 “click”、“mouseover”、“keydown” 等。
- 示例:
document.addEventListener("click", function(event) { console.log("Event Type: " + event.type); // 输出 "click" });
2. clientX
/ clientY
- 描述:获取光标相对于浏览器可见窗口左上角的位置。
- 示例:
document.addEventListener("click", function(event) { console.log("Mouse X: " + event.clientX); console.log("Mouse Y: " + event.clientY); });
3. offsetX
/ offsetY
- 描述:获取光标相对于当前 DOM 元素左上角的位置。
- 示例:
document.getElementById("myElement").addEventListener("click", function(event) { console.log("Offset X: " + event.offsetX); console.log("Offset Y: " + event.offsetY); });
4. key
- 描述:获取用户按下的键盘键的值。这是当前推荐的属性,取代了过去常用的
keyCode
。 - 示例:
document.addEventListener("keydown", function(event) { console.log("Key Pressed: " + event.key); });
这些属性帮助我们处理各种用户交互事件,通过获取事件的具体信息,可以更精确地响应用户的操作。
(2)环境对象
在js中,this
关键字的值取决于它所在的执行环境或上下文。this
可以表示不同的对象,具体取决于它在何处以及如何被调用。
1. 全局上下文
在全局上下文中(即在任何函数之外),this
指的是全局对象。在浏览器中,全局对象是window
。
console.log(this); // window
2. 函数上下文
在非严格模式下,普通函数中的this
指向全局对象。在严格模式下,this
为undefined
。
function foo() {
console.log(this); // 非严格模式下是 window,严格模式下是 undefined
}
foo();
3. 对象方法
当一个函数作为对象的方法调用时,this
指向调用该方法的对象。
const obj = {
name: 'Alice',
greet: function() {
console.log(this.name); // 'Alice'
}
};
obj.greet();
4. 构造函数
当使用new
关键字调用一个构造函数时,this
指向新创建的实例对象。
function Person(name) {
this.name = name;
}
const person = new Person('Bob');
console.log(person.name); // 'Bob'
5. 事件处理程序
在事件处理程序中,this
通常指向绑定事件的元素。
document.getElementById('myButton').addEventListener('click', function() {
console.log(this); // <button id="myButton">
});
1.3 回调函数
回调函数(callback function)是一个函数,它作为参数传递给另一个函数,以便在某个操作完成后被调用。回调函数在异步编程和事件处理等场景中非常常见。
function greet(name, callback) {
console.log('Hello, ' + name + '!');
callback();
}
function sayGoodbye() {
console.log('Goodbye!');
}
greet('Alice', sayGoodbye);
// 输出:
// Hello, Alice!
// Goodbye!
二、事件流、委托
2.1 事件流(Event Stream)
2.1.1 事件流
事件流描述了事件从最外层元素到目标元素(触发事件的元素)的传播过程。在DOM中,事件流包括三个阶段:
- 捕获阶段:事件从文档根节点向下传播到目标元素
- 目标阶段:事件到达目标元素
- 冒泡阶段:事件从目标元素向上冒泡到文档根节点
2.1.2 两个阶段
通常我们讨论事件处理时,主要关注两个阶段:
- 捕获阶段:事件从外向内传播
- 冒泡阶段:事件从内向外传播
这两个阶段允许我们在不同的层级上处理事件。默认情况下,大多数事件处理程序在冒泡阶段执行。
使用addEventListener
方法时,可以通过第三个参数指定是否在捕获阶段处理事件:
element.addEventListener('click', handler, true); // 捕获阶段
element.addEventListener('click', handler, false); // 冒泡阶段(默认)
(1)事件捕获
从Dom的根元素开始执行操作对应的事件(事件从外向内传播)
Dom.addEventListener('事件类型','事件处理函数','是否使用捕获机制')
L0事件监听只有冒泡无捕获
(2)事件冒泡
(3)阻止冒泡
-
阻止冒泡 (stopPropagation):
-
stopPropagation()
用于阻止事件在捕获和冒泡阶段继续传播。换句话说,它会阻止事件继续向上传递,因此不会触发父元素上的事件处理程序。son.addEventListener('click', function (e) { e.stopPropagation(); });
-
当您希望事件只影响触发它的元素,并且不希望触发任何其他元素的事件监听器时,此方法非常有用。
-
-
阻止元素默认行为 (preventDefault):
preventDefault()
用于阻止与事件关联的默认动作的执行。例如,它可以用于阻止表单提交或阻止链接导航到新页面。form.addEventListener('click', function (e) { e.preventDefault(); });
- 当您希望控制元素的行为,比如阻止链接跳转或停止表单提交以通过js处理时,通常使用此方法。
主要区别:
-
目的:
stopPropagation()
控制事件在DOM树中的传播,而preventDefault()
则控制触发事件的元素的默认行为。
-
使用场景:
- 当您希望阻止事件传播到父元素时,使用
stopPropagation()
。 - 当您希望阻止事件的默认动作(如停止表单提交)时,使用
preventDefault()
。
- 当您希望阻止事件传播到父元素时,使用
2.1.3 解绑事件
-
使用
onclick
属性解除事件:-
如果使用
onclick
属性绑定事件,可以通过将onclick
设为null
来解除事件绑定。 -
示例:
btn.onclick = function() { alert('点击了'); }; // 解除事件绑定 btn.onclick = null;
-
这种方式简单直观,适用于通过
onclick
属性绑定的事件。
-
-
使用
removeEventListener
解除事件:-
如果使用
addEventListener
方法绑定事件,则需要使用removeEventListener
方法解除事件绑定。 -
removeEventListener
的参数需要与addEventListener
的参数完全匹配,包括事件类型和事件处理函数的引用。 -
示例:
function fn() { alert('点击了'); } // 绑定事件 btn.addEventListener('click', fn); // 解除事件绑定 btn.removeEventListener('click', fn);
-
注意:匿名函数无法被解除绑定,因为没有引用可供
removeEventListener
使用。
-
总结:
onclick
方式 适用于简单的事件绑定和解除绑定。addEventListener
和removeEventListener
提供了更灵活的事件绑定和解除绑定方法,适合更复杂的场景,但需要注意函数引用的管理。
如果你想要使用更加现代的、支持更多事件类型的方式,建议使用 addEventListener
进行事件绑定,并使用 removeEventListener
解除事件绑定。
2.1.4 两种注册事件方式的区别
1. 传统的注册 (L0)
-
特点:
- 对于同一个对象,后面注册的事件会覆盖前面注册的事件(如果是同一个事件类型)。
- 可以直接通过将
onclick
设为null
来实现事件的解除。 - 事件是在冒泡阶段执行的。
-
使用场景:
- 这种方式通常通过 HTML 属性(如
onclick
)或直接在 js 中设置 DOM 元素的事件属性来实现。 - 示例:
btn.onclick = function() { console.log('第一次事件'); }; // 第二次注册的事件会覆盖第一次 btn.onclick = function() { console.log('第二次事件'); }; // 解除事件 btn.onclick = null;
- 这种方式通常通过 HTML 属性(如
2. 事件监听注册 (L2)
-
特点:
- 使用
addEventListener
方法注册事件。 - 后面注册的事件不会覆盖前面注册的事件(即使是同一个事件类型),会按顺序执行。
- 可以通过第三个参数来指定事件是在冒泡阶段还是在捕获阶段执行。
- 必须使用
removeEventListener
来解除事件绑定,且函数引用必须一致。 - 匿名函数无法解除绑定。
- 使用
-
使用场景:
- 适用于需要同时绑定多个事件处理函数的场景,并且这些处理函数可以独立解除。
- 示例:
function firstHandler() { console.log('第一次事件'); } function secondHandler() { console.log('第二次事件'); } btn.addEventListener('click', firstHandler); btn.addEventListener('click', secondHandler); // 解除某个特定的事件处理函数 btn.removeEventListener('click', firstHandler);
总结:
- 传统的注册 (L0) 适用于简单场景,且会被后面的同类型事件覆盖,并且可以通过设置为
null
轻松解除。 - 事件监听注册 (L2) 提供了更强的灵活性和控制力,允许多个处理函数共存且不会互相覆盖,同时需要更精确的管理解除操作。
2.2 事件委托
Js中的事件委托是一种利用事件冒泡的机制,将事件处理程序添加到父元素而不是每个子元素上。这样可以提高性能并简化代码,特别是当有大量子元素需要绑定相同类型的事件时。
<div id="parentElement">
<button class="childElement">Button 1</button>
<button class="childElement">Button 2</button>
<button class="childElement">Button 3</button>
</div>
document.getElementById('parentElement').addEventListener('click', function(event) {
if (event.target.classList.contains('childElement')) {
console.log('Button clicked: ' + event.target.textContent);
}
});
要找到真正处理事件的对象,可以通过event.target
属性访问到触发事件的最深层元素。
事件对象.target.tagName
2.3 其他
2.3.1 页面加载事件
如果脚本在DOM完全加载之前执行,可能会因为无法找到相关的DOM元素而导致脚本错误。
建议将脚本放在HTML文档的底部或者使用
load
事件确保DOM完全加载后再执行脚本。
1. 页面加载事件的种类及其用途
load事件
- 触发时机:当整个页面及所有依赖资源如样式表、图片和子框架完全加载后触发。
- 监听对象:通常添加到
window
对象。 -
window.addEventListener('load', function () { // 页面及所有资源完全加载后执行的代码 })
DOMContentLoaded事件
- 触发时机:当初始的HTML文档被完全加载和解析完成后触发,不需等待样式表、图像和子框架的加载。
- 监听对象:添加到
document
对象。 -
document.addEventListener('DOMContentLoaded', function () { // DOM完全加载后执行的代码 })
2. 使用场景和选择
- load事件:适用于需要依赖页面上所有资源都加载完成后才能执行的操作,例如处理图片的尺寸或者是视频的加载。
- DOMContentLoaded事件:适用于只依赖于DOM结构的脚本初始化,如操作DOM元素。这可以加快脚本执行的速度,因为不必等待所有资源加载完成。
通过理解这两种事件的区别和适用场景,可以更有效地控制Js代码的执行时机,从而优化用户体验和页面响应速度。
2.3.2 元素滚动事件
滚动条在滚动的时候持续触发的事件
很多网页需要检测用户把页面滚动到某个区域后做一些处理, 比如固定导航栏,比如返回顶部
-
示例代码:
// 页面滚动事件 window.addEventListener('scroll', function () { // 执行的操作 })
这段代码展示了如何在window对象上添加一个监听事件来捕捉页面的滚动动作。当页面滚动时,可以在这个函数中执行需要的操作。
-
说明:
- 可以在
window
或document
对象上监听scroll
事件。 - 监听器中的函数可以根据实际需求来定义具体的操作。
- 可以在
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body {
padding-top: 100px;
height: 2000px;
}
div {
margin: 100px;
overflow: scroll;
width: 100px;
height: 70px;
border: 1px solid #000000;
}
</style>
</head>
<body>
<div>
31
3212
312
31231
32
12312
3123
</div>
<script>
const div = document.querySelector('div');
div.addEventListener('scroll', function () {
console.log(div.scrollTop);
})
</script>
</body>
</html>
(1)获取位置
-
scrollLeft 和 scrollTop(属性):
scrollLeft
:表示元素水平滚动条的当前位置。scrollTop
:表示元素垂直滚动条的当前位置。- 这些属性可以告诉你用户已经滚动了多少距离,也可以通过设置这些属性来控制滚动位置。
-
scrollWidth 和 scrollHeight(属性):
scrollWidth
:元素内容的总宽度,包括因超出显示范围而不可见的部分。scrollHeight
:元素内容的总高度,同样包括不可见的部分。- 这些属性用于获取元素的完整内容大小,即使部分内容当前不可见。
-
添加滚动事件监听器(代码示例):
div.addEventListener('scroll', function () { console.log(this.scrollTop); })
- 这段代码演示了如何为一个元素添加
scroll
事件监听器。当元素滚动时,它会输出当前的scrollTop
值,即垂直滚动的距离。
- 这段代码演示了如何为一个元素添加
(2)注意
-
滚动的对象或者位置到底有何不同?是否可以读取和修改?
scrollTop
/scrollLeft
:这些属性通常用于获取或设置元素(如div
等)的滚动位置。它们可以读取也可以修改,用于控制或响应元素的滚动状态。
-
检测页面滚动的全局变量(整体页面的滚动)用哪个属性?
document.documentElement.scrollTop
:这个属性用于获取或设置整个文档的垂直滚动位置。在标准模式下,document.documentElement
返回的是html
元素,它代表了整个页面的滚动位置。
-
示例代码解释:
window.addEventListener('scroll', function () { const n = document.documentElement.scrollTop; console.log(n); })
- 这段代码演示了如何监听整个窗口的滚动事件,并获取当前文档的垂直滚动位置。当用户滚动页面时,当前的滚动位置会被输出到控制台。
- 这是监控全页面滚动的常用方法,特别是当你需要根据页面滚动位置来调整UI元素或执行某些动作时。
(3)将页面滚动到指定的坐标。
- 说明:讨论如何使用 Js 的
scrollTo()
方法来滚动页面到指定的坐标。
-
scrollTo() 方法:
- 描述:
scrollTo()
方法可以把内容滚动到指定的坐标。 - 语法:
元素.scrollTo(x, y)
x
: 水平坐标(以像素为单位)。y
: 垂直坐标(以像素为单位)。
- 描述:
-
示例:
- 代码:
window.scrollTo(0, 1000)
- 解释:将页面滚动到距离顶部 1000 像素的位置。
- 代码:
2.3.3 页面尺寸事件
(1)页面尺寸事件
在网页开发中,处理页面尺寸变化是一个常见的需求,特别是为了创建响应式网页。当窗口尺寸改变时,可以触发resize
事件,并据此执行相应的代码来调整页面布局或样式。
// 为window对象添加resize事件监听器
window.addEventListener('resize', function() {
// 在事件处理函数中,检测屏幕宽度
let w = document.documentElement.clientWidth;
// 在控制台输出当前屏幕宽度
console.log(w);
});
在这段代码中,addEventListener
方法用于给window
对象添加一个resize
事件监听器。每当窗口尺寸发生变化时,就会执行传递给addEventListener
的回调函数。在这个回调函数内部,通过document.documentElement.clientWidth
获取当前屏幕的宽度(不包括滚动条),并使用console.log
方法将宽度值输出到浏览器的控制台中。
(2)获取元素宽高
clientWidth
和clientHeight
的属性,这些属性可以用来获取元素的可见内容区域的宽度和高度。
此外,还提到了padding
、border
和dientHeight
等属性,它们可能会影响最终测量的结果。最后,图中还显示了一个示例代码片段,用于演示如何通过clientWidth
和clientHeight
属性来获取一个元素的宽度和高度。
(3)获取元素相对于其最近的已定位父级元素
offsetLeft
和 offsetTop
是 Js 中的两个只读属性。
- 功能:这两个属性用于获取元素相对于其最近的已定位父级元素(例如,使用
position: relative
或position: absolute
)的左侧和顶部距离。 - 注意:这些属性是只读的,不能直接修改它们的值。
- 方法名称:
element.getBoundingClientRect()
- 功能: 该方法返回元素的大小及其相对于视口的位置。
getBoundingClientRect()
方法非常有用,当你需要精确测量元素在页面上的位置和尺寸时,可以使用这个方法。它返回一个 DOMRect
对象,该对象包含元素的 left
、top
、right
、bottom
、width
和 height
属性,相对于视口的位置。
-
offsetWidth
和offsetHeight
是什么?- 这两个属性返回元素的宽度和高度。
- 它们的值包括:内容(content) + 内边距(padding) + 边框(border)。
-
offsetTop
和offsetLeft
的位置参考是什么?- 如果元素的父级元素是带有定位的父级(使用
position
属性设置为relative
、absolute
等),那么offsetTop
和offsetLeft
就是相对于这个父级元素计算的。 - 如果没有带定位的父级元素,则这些属性值是相对于文档的左上角计算的。
- 如果元素的父级元素是带有定位的父级(使用
总结
-
scrollLeft 和 scrollTop
- 作用: 获取元素被卷去的左部和顶部距离。
- 说明: 配合页面滚动来使用,属性是可读写的。
-
clientWidth 和 clientHeight
- 作用: 获取元素的宽度和高度。
- 说明: 不包括
border
、margin
和滚动条。通常用于 Js 获取元素大小,仅可读。
-
offsetWidth 和 offsetHeight
- 作用: 获取元素的宽度和高度。
- 说明: 包括
border
、padding
和滚动条,仅可读。
-
offsetLeft 和 offsetTop
- 作用: 获取元素相对于其定位父级元素的左侧和顶部距离。
- 说明: 在获取元素位置时使用,仅可读。
(4)flexible.js
flexible.js通过将页面划分为10份,每一份为1rem来解决的