入门魔法 - Event 事件

前情提要

艾草:「好了,总算选取到树上的红色果实了,那我来教你一些简单的火属性魔法事件。」

「咦,我的魔力总量够了吗?总算能开始了吗!」

艾草:「我琢磨着你的成长,已经够了,你可以学初阶魔法了。来想像你的手指有一股魔力,好像你前方有隐形的按钮,去触碰它。」

「喔喔喔喔~~有感觉了呃啊啊啊啊啊。」

(手指尖端冒出了小小的火焰,与此同时手指也被灼伤了。)

艾草:「喔,成功了耶!帮你浇水熄灭唷。」

「呜呜呜,好痛。还会烫伤人!我学这个干嘛,还不如用打火机!」

艾草:「是你现在魔力总量不足啦,对魔法事件的掌握度不够,我们先来研读一下事件的机制吧!」

(艾草心里 os 当初就叫你不要选火属性吧(๑•́ ₃ •̀๑))


Event

透过 Event 我们可以让网站与使用者有更多的互动,但 Event 到底是什麽呢?

拿常见的弹跳式视窗来举例好了,我们在某些网站购物时,当点击到确认付款後,可能会跳出付款成功,而这个功能是如何实现的呢?

没错,就是透过 EventEvent 包含各式各样的类型,例如刚刚提到的滑鼠点击、又或是敲打键盘

等,通常都是使用者执行到某些特殊事件时 ,会去执行对应的程序码,而今天会介绍一些常见的 Event 类型。

事件传递流程 Event Flow

为什麽要谈到事件传递流程 Event Flow 呢?

今天当你开心的点击了某颗按钮时,你以为你只点击到它吗?这样想就太天真了,其实你不仅仅点击到它,如果延伸来看,你还点击到它的父层、更甚者你点击到的是整个 document

为什麽会这样说呢?因为按钮可能包覆在 div 内,而 div 可能包覆在 body 内,这样层层延伸下去,其实你点击到的是整个网页,也因此网页会有一定的事件传递流程。

而事件传递流程分为两种:

  • Event Capture 事件捕获
  • Event Bubble 事件冒泡

而 「Capture 捕获」、「Bubble 冒泡」分别指的是你的事件就怎麽触发的呢?让我们先来看一张 W3C 的图,会更清楚。

https://ithelp.ithome.com.tw/upload/images/20210929/20139066qZ0fhl1dkk.png

图片来源:W3C

Capture Phase 捕获

捕获阶段指得是由上而下,从最 window 到目标阶段 Target Phase 的过程。

Target Phase 目标阶段

目标阶段指得是找到目标 Target 时。

Bubble Phase 冒泡

冒泡阶段指得是由下而上,从目标阶段 Target Phasewindow 的过程。

那这其中会带来怎样的差异呢?让我们透过 addEventListener 监听来了解吧。


addEventListener()

可以透过 addEventListener() 来监听我们目前触发的事件类型後,去执行对应的功能。

addEventListener() 有三个参数:

  1. type : 事件类型
  2. listener :对应的事件处理函式
  3. useCaptureBoolean 值,决定事件是以 true「Capture 捕获」或 false「Bubble 冒泡」机制执行,预设为 false

事件类型要透过包覆单引号、双引号的方式去呈现,例如:'click'

事件处理函式代表如果使用者点击了我所监听的按钮时,应该会有什麽样的功能,通常程序码会包覆在函式内。

useCapture 设定 truefalse 的差异性,让我们透过以下程序码来了解:

Capture 捕获

HTML:

<table>
    <thead>
    </thead>
    <tbody>
        <tr>
            <td>123</td>
            <td>321</td>
        </tr>
    </tbody>
</table>

JavaScript:

const table = document.querySelector("table");
const tbody = document.querySelector("tbody");
const tr = document.querySelector("tr");
const td = document.querySelector("td");

table.addEventListener("click", function(e) {
  console.log('table click')
},true);
tbody.addEventListener("click", function(e) {
  console.log('tbody click')
},true);
tr.addEventListener("click", function(e) {
  console.log('tr click')
},true);
td.addEventListener("click", function(e) {
  console.log('td click')
},true);

执行後的结果:
https://ithelp.ithome.com.tw/upload/images/20210929/20139066qGp9FpuyIT.png

Bubble 冒泡

false 为预设情况。

HTML:

<table>
    <thead>
    </thead>
    <tbody>
        <tr>
            <td>123</td>
            <td>321</td>
        </tr>
    </tbody>
</table>

JavaScript:

const table = document.querySelector("table");
const tbody = document.querySelector("tbody");
const tr = document.querySelector("tr");
const td = document.querySelector("td");

table.addEventListener("click", function(e) {
  console.log('table click')
});
tbody.addEventListener("click", function(e) {
  console.log('tbody click')
});
tr.addEventListener("click", function(e) {
  console.log('tr click')
});
td.addEventListener("click", function(e) {
  console.log('td click')
});

执行後的结果:
https://ithelp.ithome.com.tw/upload/images/20210929/20139066pcwaAixlwL.png

透过此方式可以看到 Capture 捕获、Bubble 冒泡的差异, Capture 捕获是从父层一层一层下来时就触发动作,Bubble 冒泡则是从子层一层一层上去时触发动作。

接下来让我们实作看看注册监听点击按钮事件吧!

HTML:

<button type = "button">按钮</button>

JavaScript:

//透过 querySelector 选取按钮
const button = document.querySelector("button");
//为按钮注册监听函式      //监听点击 //e 代表 event 可自定义参数
button.addEventListener("click", function(e) {
  console.log('button click')
});

执行後的结果:

https://ithelp.ithome.com.tw/upload/images/20210929/20139066uifEBns6Ky.png

我们透过上方程序码 console.log 印出 event 看看长怎样吧!

可以发现是一大包的物件,而将它展开後有很多的属性:
https://ithelp.ithome.com.tw/upload/images/20210929/201390669zMfBxHpI7.png
https://ithelp.ithome.com.tw/upload/images/20210929/20139066rmmFxmaH78.png

接下来让我们针对常用的属性 e.target 做介绍吧!


e.target

e.target 表示触发事件的该元素本身,什麽意思呢?

一样透过刚刚的程序码 console.log 出来就知道了!

JavaScript:

//透过 querySelector 选取按钮
const button = document.querySelector("button");
//为按钮注册监听函式      //监听点击 //e 代表 event 可自定义参数
button.addEventListener("click", function(e) {
  console.log(e.target)
});

印出结果:
https://ithelp.ithome.com.tw/upload/images/20210929/20139066hJBgJWbjWt.png

用这样可能看不太出来它好用在哪边,但 e.target 常常拿来取值,或是拿来做流程判断,以下举例:

例如当大范围监听时,搭配 e.target 底下的属性 nodeName 判断点击到的是不是按钮:

HTML:

<ul class="list">
	<button type="button">送出</button>
</ul>

JavaScript:

const list = document.querySelector(".list");
list.addEventListener("click", function (e) {
    if(e.target.nodeName === "BUTTON"){
        console.log("我点击到的是按钮!")
    }
});

透过 e.target 搭配 nodeName 的方式可以确认当点击到的是按钮时,才开始撰写对应的效果!


event.preventDefault()

event.preventDefault() 可以拿来阻挡预设行为,例如 a 连结点击会转址的行为,又或者是表单的

submit 效果,以下来示范该如何实作!

HTML:

<a href="https://www.google.com.tw/">连结</a>

JavaScript:

const link = document.querySelector("a");

link.addEventListener("click", function (e) {
		//阻挡 a 连结转址效果
    e.preventDefault();
    console.log("我被点了")
});

像这样直接在 link 的注册监听函式内放入 event.preventDefault() a 连结就不会有转址的效果罗!

另外如果使用 button 时需要特别注意,如果没有指定 buttontype 属性,预设就会是 submit ,要特别留意。


小练习

请问以下叙述何者错误?

A addEventListener() useCapture 参数设定 true 时,会透过 Capture 捕获机制执行
B Capture 捕获机制会由父层到子层触发,且 addEventListener() useCapture 参数预设就是 true
C e.target 表示触发事件的元素本身
D event.preventDefault() 可以阻挡预设行为,例如表单的 submit 效果

解答:选项 B 错误,因为useCapture 参数预设为 false ,预设是透过 Bubble 冒泡执行。


参考文献

JavaScript 必修篇 - 前端修练全攻略(六角学院)
https://ithelp.ithome.com.tw/articles/10192015
https://medium.com/itsems-frontend/javascript-event-bubbling-capturing-794cd2d01e61


<<:  EP 21: Custom Launch Screen for iOS

>>:  Day 14 - Spring Boot & Thymeleaf

Day 26【Deploy NFT - Lazy-Minting & Smart Contract】Right Click and Save Image As

【前言】 接下来我们要进到整个 Project 重头戏中的重头戏啦,当我们都具备好图档以及 Met...

Day 5 追加测试

为了方便之後丢多点图进行测试,我将图片放进了 img 资料夹,并使用 glob 获得图片列表。 同时...

Day 26-制作购物车之设定Redux: reducers&store

主要呈现实作成果 以下内容有参考教学影片,底下有附网址。 (内容包括我的不专业解说分析及在实作过程中...

D3JsDay04一同来见识 D3起手式—用D3写Helloworld

如何开始D3js 方法一 使用CDN 请google搜寻D3Js到D3Js的官方网站。 滑鼠滚轮到下...

[02] 建立服务器

首先建立一个服务器方便之後 telegram 的 hook 来挂载 这边先从 github 建立一个...