JavaScript:DOM事件

简介: JavaScript:DOM事件

事件监听

什么是事件监听

事件是编程语言中的术语,它是用来描述程序的行为或状态的,一旦行为或状态发生改变,便立即调用一个函数。

例如:用户使用【鼠标点击】网页中的一个按钮、用户使用【鼠标拖拽】网页中的一张图

其中,【鼠标点击】和【鼠标拖拽】就是事件,那么这些事情发生之后,为了让JavaScript可以知道,于是就有了事件监听。


事件监听的方式

addEventListener 是 DOM 对象专门用来添加事件监听的方法,它的两个参数分别为【事件类型】和【事件回调】。

语法:

元素对象.addEventListener("事件类型", 要执行的函数)

元素对象

也就是检测谁被执行了事件。只有我们指定的元素对象执行了指定的事件,事件监听才会执行。

事件类型

用在网页中的操作有非常多种,事件类型用于指定只有执行了哪一个事件,事件监听才会被触发。

要执行的函数

当我们的事件监听检测到目标事件发生,那么就需要做出相应的反应,此处的反应,就是执行这个函数。

完成事件监听分成以下步骤:

  1. 获取 DOM 对象
  2. 通过 addEventListener 方法为 DOM 对象添加事件监听
  3. 等待事件触发
  4. 事件触发后,执行相对应的函数

接下来我为大家展示一段代码(省略了CSS修饰):

<div class="skyblue"></div>
        <div class="pink"></div>
        <div class="gold"></div>
        <div class="palegreen"></div>
        <script>
            const gold = document.querySelector(".gold");
            gold.addEventListener("click", function () {
                gold.style.backgroundColor = "red";
            })
        </script>

在script中,就是一个完整的事件监听过程:

第一步:

const gold = document.querySelector(".gold");

这个过程是在获取gold颜色对应的盒子,即获取DOM对象

第二步:

gold.addEventListener(),这一步是在事件监听,监听的对象是gold颜色的盒子;

"click",代表点击事件,即这个事件监听的触发条件是点击,触发对象的gold颜色的盒子。即:点击gold颜色的盒子后,执行事件监听绑定的函数。

function () {gold.style.backgroundColor = "red";},这是被绑定的函数,事件监听条件满足后,就执行该函数。函数内部的代码表示,将gold颜色的盒子颜色改为red。

综上,以上事件监听完成的功能是:当点击gold颜色的盒子,将其变为red色。

效果展示:

在上图中,点击其它颜色盒子,颜色不会改变,只有被绑定了事件的盒子颜色才会改变。


事件类型

点击事件

click 译成中文是【点击】的意思,它的含义是监听用户鼠标的单击操作,除了【单击】还有【双击】dblclick

<script>
  // 双击事件类型
  btn.addEventListener('dblclick', function () {
    console.log('等待事件被触发...');
    // 改变 p 标签的文字颜色
    const text = document.querySelector('.text')
    text.style.color = 'red'
  })
</script>
鼠标事件

鼠标事件是指跟鼠标操作相关的事件,如单击、双击、移动等。

mouseenter 监听鼠标是否移入 DOM 元素

mouseleave 监听鼠标是否移出 DOM 元素

键盘事件

keydown 键盘按下触发时,触发事件

keyup 键盘抬起触发时,触发事件

焦点事件

focus 获得焦点时,触发事件

blur 失去焦点时,触发事件

文本框输入事件

input 当用户对文本框输入时,触发事件


事件对象

什么是事件对象

事件对象是事件监听函数自带的对象,其内部存储了事件触发时的相关信息。比如点击事件中,我们可以知道点击的位置;在键盘事件中,我们可以知道按下的是哪一个键位。


获取事件对象

事件对象的获取方式十分简单,事件对象就是调用的函数的第一个参数

语法:

元素.addEventListener("事件类型", function (e){})

在上述代码中,e就是我们的事件对象,你可以为这个参数取任何名字,不过我们一般取名为eventev或者e

接下来我们观察一下这个参数传进来了什么:

依然是刚才的代码,现在已经触发了事件,我们在控制台中可以看到e是一个对象,内部有非常多的属性,这些属性就是事件出发时的相关信息。

事件对象常用属性

属性 含义
type 触发的事件类型
clientX / clientY 获取光标相对于浏览器左上角的位置
offsetX / offsetY 获取光标相对于当前事件元素左上角的位置
key 用户按下的键盘的值

展示一下效果:


事件解绑

既然可以绑定事件,我们也可以在不需要执行相应事件的时候解除绑定。

语法:

解绑对象.removeEventListener("事件类型",函数)

由于一个对象可以绑定多个事件类型,而一个事件也可以绑定多个函数。所以在解绑的时候,一定要指定清楚哪一个对象,哪一个事件,哪一个函数。

以上就是基本语法,我们用一段代码展示效果:

const gold = document.querySelector(".gold");
const skyblue = document.querySelector(".skyblue");
function fn() {
  alert("正在点击gold盒子");
}
gold.addEventListener("click",fn)
skyblue.addEventListener("click", function () {
  gold.removeEventListener("click", fn);
  alert("gold事件已经解绑");
})

在上述代码中,我们先为gold盒子绑定了一个事件,点击的时候执行fn函数,然后为skyblue盒子绑定事件,当skyblue盒子被点击,就会提示"gold事件已经解绑",并且解绑gold的事件。

效果如下:

原先我们多次点击gold盒子,都会有响应,当我们点击完skyblue盒子后,gold的事件就被解绑了,此后再点击gold盒子,都不会发生事件监听了。

那么我们的代码可以优化成这样吗:

const gold = document.querySelector(".gold");
const skyblue = document.querySelector(".skyblue");
gold.addEventListener("click",function fn() {
  alert("正在点击gold盒子");
})
skyblue.addEventListener("click", function () {
  gold.removeEventListener("click", fn);
  alert("gold事件已经解绑");
})

首先,我们再gold盒子的事件监听中,定义了fn函数,那么fn函数的作用域就在addEventListener,此时sky blue的事件监听是无法找到fn函数的,系统就会报错:fn is undefined;即没有定义fn函数。所以在确定了要依靠其它函数来解绑事件,那么就要把事件监听的函数写在外面

此外,调用函数为匿名函数的事件监听无法解绑,因为在解绑的时候,无法找到对应函数名。


环境对象 this

this对象是函数内部的特殊对象,其代表着函数运行时所处的环境。

当不同对象调用同一个函数,此函数的this对象也会不同。

当我们直接调用某一个函数,其this是Windows,即JavaScript中的顶级对象。

我们用案例来说明:

function fn() {
  console.log(this);
}
fn();
gold.addEventListener("click", fn)
skyblue.addEventListener("click", fn)

fn是一个函数,其执行后,会向控制台输出当前的this对象,代码中共有三处调用了fn。我们看一下三个this的输出结果:

可以看到,不同地方调用同一个函数,其this也会不同。

this对象的指向准则就是:谁调用这个函数,这个函数的this就是谁

在实际应用中,我们可以用this代指一些复杂的对象名,比如以下代码:

const aVeryVeryVeryVeryVeryVeryVeryLongName = document.querySelector(".gold");
aVeryVeryVeryVeryVeryVeryVeryLongName.addEventListener("click", function () {
  aVeryVeryVeryVeryVeryVeryVeryLongName.innerHTML = "xxx";
  aVeryVeryVeryVeryVeryVeryVeryLongName.style.backgroundColor = "red";
  aVeryVeryVeryVeryVeryVeryVeryLongName.style.display = "blocck";
})

如果我们的事件对象名字非常长,比如此处的aVeryVeryVeryVeryVeryVeryVeryLongName,那么我们后续想要调用这个对象就会很麻烦,而在事件监听函数中,我们的this对象就是aVeryVeryVeryVeryVeryVeryVeryLongName,所以以上代码可以简化为:

const aVeryVeryVeryVeryVeryVeryVeryLongName = document.querySelector(".gold");
aVeryVeryVeryVeryVeryVeryVeryLongName.addEventListener("click", function () {
  this.innerHTML = "xxx";
  this.style.backgroundColor = "red";
  this.style.display = "blocck";
})

事件流

事件流是对事件执行过程的描述,事件流主要分为两个过程:事件捕获与事件冒泡

接下来我们讲解一下两个阶段:

事件捕获

所谓事件捕获,就是在DOM树中找到目标事件的过程。

比如一个外国人想买到正宗的中国陶瓷,那么他就需要“捕获”到陶瓷。这个过程中,需要先在中国寻找江西省,在江西省寻找景德镇,在景德镇寻找陶瓷店铺,最后才能找到陶瓷。这个过程叫做捕获,也就是一层一层往下寻找,直到找到目标为止。

事件冒泡

当寻找到目标后,那么就需要将目标带回,此时陶瓷就会离开陶瓷店,离开景德镇,离开江西省,离开中国,最后才能到达老外手里。这个过程叫做冒泡,即事件捕获后,往外一层一层冒出。

那么我们再解析一下上图:

当我们要寻找一个div,那么就要先事件捕获:进入document,进入html,进入body,找到div。然后再事件冒泡:离开body,离开html,离开document。

事件捕获与事件冒泡的影响

接下来讲解一下事件捕获和事件冒泡在事件监听中的作用:

现在我们有以下布局的盒子:

对其绑定以下事件:

const skyblue = document.querySelector(".skyblue");
const pink = document.querySelector(".pink");
const gold = document.querySelector(".gold");
const palegreen = document.querySelector(".palegreen");
skyblue.addEventListener("click", function () {
  console.log("skyblue");
})
pink.addEventListener("click", function () {
  console.log("pink");
})
gold.addEventListener("click", function () {
  console.log("gold");
})
palegreen.addEventListener("click", function () {
  console.log("palegreen");
})

即对每个盒子都绑定了点击事件,点击后会输出当前盒子的名字,接下来我们点击绿色盒子试试:

我们只点击了一个盒子,四个盒子相应的事件全部触发了!

这就是事件冒泡的影响,我们观察一下输出顺序:绿,黄,粉,蓝,刚好就是事件冒泡的顺序。

当一个元素的事件触发时,所有祖先元素绑定的相同类型事件监听都会被触发。

这个地方,四个元素全部绑定了”click“的事件监听,当绿色盒子的”click“事件监听触发时,其所有父级元素的”click“事件监听都会被触发。

而默认的事件监听执行顺序,是在冒泡阶段执行的,这涉及到了addEventListener的第三个参数,当第三个参数为默认值false时,addEventListener的函数效果会在冒泡阶段执行;当第三个参数为true时,addEventListener的函数效果会在捕获阶段执行。

现在我们将所有函数的第三个参数加上truea:

const skyblue = document.querySelector(".skyblue");
const pink = document.querySelector(".pink");
const gold = document.querySelector(".gold");
const palegreen = document.querySelector(".palegreen");
skyblue.addEventListener("click", function () {
  console.log("skyblue");
},true)
pink.addEventListener("click", function () {
  console.log("pink");
},true)
gold.addEventListener("click", function () {
  console.log("gold");
},true)
palegreen.addEventListener("click", function () {
  console.log("palegreen");
},true)

点击绿色盒子看看效果:

最后我们的执行顺序就是:蓝,粉,黄,绿,即事件捕获的顺序。

毫无疑问,事件监听和事件冒泡对网页交互的负面效果比较多,由于addEventListener默认在事件冒泡阶段执行,那么我们就需要阻止事件冒泡的发生,这就涉及到阻止冒泡

而这个也有一些好处,比如事件委托

接下来我们对两者一一讲解:


阻止冒泡

语法:

事件对象.stopPropagation()

此处的事件对象,就是我们之前讲解的e,即:e.stopPropagation()

这个函数可以阻断事件流动传播,对事件冒泡和事件捕获都有效果。

我们将阻止冒泡加到最里层的绿色盒子中:

const skyblue = document.querySelector(".skyblue");
const pink = document.querySelector(".pink");
const gold = document.querySelector(".gold");
const palegreen = document.querySelector(".palegreen");
skyblue.addEventListener("click", function () {
  console.log("skyblue");
})
pink.addEventListener("click", function () {
  console.log("pink");
})
gold.addEventListener("click", function () {
  console.log("gold");
})
palegreen.addEventListener("click", function (e) {
  console.log("palegreen");
  e.stopPropagation();
})

效果如下:

最后只有绿色盒子执行了事件监听,其它盒子由于事件冒泡被阻断了,没有接收到绿色盒子的冒泡。


事件委托

现在有一个父级盒子ul,和三个子级盒子li:

让三个子级盒子被点击后,都会变成白色,你会怎么做?

基础思路就是,对三个盒子分别”click“进行事件监听,然后将它们的颜色改变为白色。

但是如果你有10个子盒子,20个子盒子,一个一个去监听不经会浪费浏览器的效率,而且代码会很冗余,此时事件委托就出现了。

事件委托利用了事件冒泡的特性,当我们点击三个子级的盒子,父级也会接收到”click“事件的效果。那么此时我们只要监听父级盒子,当子级盒子被点击后,”click“冒泡到父级盒子,父级盒子的事件监听就触发了。

那么当父级盒子事件监听触发后,要如何让子级元素产生效果?

在事件对象e中,有一个属性值target,它存储着此次事件触发的元素。

我们尝试执行以下代码:

const skyblue = document.querySelector(".skyblue");
const pink = document.querySelector(".pink");
const gold = document.querySelector(".gold");
const palegreen = document.querySelector(".palegreen");
skyblue.addEventListener("click", function (e) {
  console.log(e.target);
})

每次点击时,输出当前的e.target

可以看到,我们点击哪一个盒子,e.target就是谁。

此外e.target.tagName存储着触发元素的元素名,不过是大写的:

由于我们的父子级元素的标签类型不同,可以使用这种方式来区分我们点击了子级盒子还是父级盒子:

skyblue.addEventListener("click", function (e) {
  if (e.target.tagName === "LI")
  {
    e.target.style.backgroundColor = "#ffffff";
  }
})

这样只有点击的盒子是LI时,才能触发变白的效果,最后我们看看结果:

点击小盒子的时候,会变为白色,点击大盒子则不会。

这就是事件委托:将多个子级的事件委托给父级,利用冒泡特性让父级触发效果,再用e.target找到触发对象,来执行效果

事件委托可以有效减少代码量,和事件监听的数目。

相关文章
|
19天前
|
JavaScript 算法
原生JS完成“一对一、一对多”矩形DIV碰撞检测、碰撞检查,通过计算接触面积(重叠覆盖面积)大小来判断接触对象DOM
原生JS完成“一对一、一对多”矩形DIV碰撞检测、碰撞检查,通过计算接触面积(重叠覆盖面积)大小来判断接触对象DOM
|
24天前
|
JavaScript
事件触发、事件捕获与事件冒泡(js的问题)
事件触发、事件捕获与事件冒泡(js的问题)
12 0
|
5天前
|
JavaScript 前端开发
js开发:请解释事件冒泡和事件捕获。
JavaScript中的事件处理有冒泡和捕获两种方式。事件冒泡是从子元素向上级元素传递,而事件捕获则从外层元素向内层传递。`addEventListener`的第三个参数可设定事件模式,`false`或不设为冒泡,`true`为捕获。示例代码展示了如何设置。
19 2
|
5天前
|
JavaScript 前端开发 UED
深入解析JavaScript原生操作DOM技术
【4月更文挑战第22天】本文深入探讨JavaScript原生DOM操作技术,包括使用`getElement*`方法和CSS选择器获取元素,借助`createElement`与`appendChild`动态创建及插入元素,修改元素内容、属性和样式,以及删除元素。通过掌握这些技术,开发者能实现页面动态交互,但应注意避免过度操作DOM以优化性能和用户体验。
|
13天前
|
存储 JavaScript 前端开发
JavaScript DOM 操作:解释一下 cookie、sessionStorage 和 localStorage 的区别。
Cookie是服务器发送至客户端的文本信息,会随每个请求发送回服务器,适合控制会话状态但可能暴露隐私。SessionStorage仅在当前会话中存储数据,关闭浏览器后清除,适合临时存储如登录状态。LocalStorage则持久保存数据,即使关闭浏览器也不会清除,适用于存储长期设置。三种方式各有侧重,应按需求选择。
16 0
|
13天前
|
JavaScript 前端开发 安全
JavaScript DOM 操作:解释一下浏览器的同源策略。
**同源策略**是浏览器安全基石,它阻止脚本跨不同协议、域名或端口访问资源,防止恶意行为。例如,HTTP页面无法直接用JS获取HTTPS页面内容。**CORS**允许跨域请求,但需服务器配合设置,通过`document.domain`属性可配置,但仍受限于服务器配置。
14 4
|
24天前
|
JavaScript
理解DOM树的加载过程(js的问题)
理解DOM树的加载过程(js的问题)
10 0
|
26天前
|
JavaScript 前端开发
深入了解 JavaScript 中的 DOM 和 BOM
深入了解 JavaScript 中的 DOM 和 BOM
18 4
|
26天前
|
JavaScript 前端开发 流计算
JS:oninput和onchange事件的区别
JS:oninput和onchange事件的区别
21 1
|
1月前
|
JavaScript 前端开发 算法
深入探讨前端框架Vue.js中的虚拟DOM机制
本文将深入探讨前端框架Vue.js中的虚拟DOM机制,分析其原理、优势以及在实际开发中的应用场景,帮助读者更好地理解Vue.js框架的核心特性。