JavaScript:DOM-事件

JavaScript:DOM - 事件

    • 事件监听
      • 什么是事件监听
      • 事件监听的方式
      • 事件类型
        • 点击事件
        • 鼠标事件
        • 键盘事件
        • 焦点事件
        • 文本框输入事件
    • 事件对象
      • 什么是事件对象
      • 获取事件对象
      • 事件对象常用属性
      • 事件解绑
    • 环境对象 this
    • 事件流
      • 事件捕获
      • 事件冒泡
      • 事件捕获与事件冒泡的影响
      • 阻止冒泡
      • 事件委托


事件监听

什么是事件监听

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

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

其中,【鼠标点击】和【鼠标拖拽】就是事件,那么这些事情发生之后,为了让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

接下来我们观察一下这个参数传进来了什么:
JavaScript:DOM-事件_第1张图片
依然是刚才的代码,现在已经触发了事件,我们在控制台中可以看到e是一个对象,内部有非常多的属性,这些属性就是事件出发时的相关信息。

事件对象常用属性

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

展示一下效果:
JavaScript:DOM-事件_第2张图片


事件解绑

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

解绑对象.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的输出结果:
JavaScript:DOM-事件_第3张图片
可以看到,不同地方调用同一个函数,其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";
})

事件流

事件流是对事件执行过程的描述,事件流主要分为两个过程:事件捕获与事件冒泡JavaScript:DOM-事件_第4张图片
接下来我们讲解一下两个阶段:

事件捕获

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

事件冒泡

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

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

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

接下来讲解一下事件捕获和事件冒泡在事件监听中的作用:
现在我们有以下布局的盒子:
JavaScript:DOM-事件_第5张图片
对其绑定以下事件:

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");
})

即对每个盒子都绑定了点击事件,点击后会输出当前盒子的名字,接下来我们点击绿色盒子试试:
JavaScript:DOM-事件_第6张图片
我们只点击了一个盒子,四个盒子相应的事件全部触发了!
这就是事件冒泡的影响,我们观察一下输出顺序:绿,黄,粉,蓝,刚好就是事件冒泡的顺序。

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

这个地方,四个元素全部绑定了”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)

点击绿色盒子看看效果:
JavaScript:DOM-事件_第7张图片
最后我们的执行顺序就是:蓝,粉,黄,绿,即事件捕获的顺序。

毫无疑问,事件监听和事件冒泡对网页交互的负面效果比较多,由于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();
})

效果如下:
JavaScript:DOM-事件_第8张图片
最后只有绿色盒子执行了事件监听,其它盒子由于事件冒泡被阻断了,没有接收到绿色盒子的冒泡。


事件委托

现在有一个父级盒子ul,和三个子级盒子li:
JavaScript:DOM-事件_第9张图片

让三个子级盒子被点击后,都会变成白色,你会怎么做?
基础思路就是,对三个盒子分别”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存储着触发元素的元素名,不过是大写的:
JavaScript:DOM-事件_第10张图片

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

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

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

点击小盒子的时候,会变为白色,点击大盒子则不会。
这就是事件委托:将多个子级的事件委托给父级,利用冒泡特性让父级触发效果,再用e.target找到触发对象,来执行效果
事件委托可以有效减少代码量,和事件监听的数目。


你可能感兴趣的:(前端开发,javascript,开发语言,ecmascript)