自己的事件自己决定。
网页最重要的两件事,资讯显示与使用者交互,而使用者交互在页面中所代表的行为就是「监听事件」与「触发事件」,相信这是大家在熟悉不过的了,click
、input
、blur
、scroll
...等等,几乎充斥在我们的网站中,但除了这些常见的、预设的事件之外,其实我们也可以自己创造出全新的事件。
CustomEvent 本身是一个建构函式,也就是我们常讲的 class
,当我们想要建立自订事件时,就透过 new
关键字来呼叫它即可,并且要记得传入代表事件名称的字串,另外还可以传入第二个参数来设定事件触发时传递的资料。
要注意的是,用来设定资料的第二个物件必须要是一个物件,且要传递的资料必须设定在该物件的 detail
属性底下。
const customEvent = new CustomEvent("myEvent", {
detail: { customData: "maxLee" },
});
而当有 DOM 元素需要绑定这个事件时,一样使用 addEventListener
来处理即可,而其中事件 Callback 会拿到的 Event 物件就会多一个 detail
的属性,该属性就会是我们当初设定的事件传递资料。
const customEvent = new CustomEvent("myEvent", {
detail: { customData: "maxLee" },
});
document.querySelector("#element").addEventListener("myEvent", function (event) {
console.log(event.detail); // { customData: "maxLee" }
});
与 CustomEvent 最极其相关的 API 就是 DispatchEvent 了,它是一个可以让我们主动触发事件的方法,当我们创建并绑定了一个事件後,就必须要倚靠它来帮我们启动事件了。
其中 EventTarget 是一个代称,它所指的是绑定事件的 DOM 对象,例如以下程序码中,div
就是 EventTarget:
const customEvent = new CustomEvent("myEvent", {
detail: { customData: "maxLee" },
});
const div = document.querySelector("div");
div.addEventListener("myEvent", function (e) {
console.log(event.detail);
});
此时上面的 div
已经被绑上了我们自订的 myEvent
事件,这时候我们就可以使用 dispatchEvent
来主动触发事件,只要在呼叫它时传入 CustomEvent 物件即可:
const customEvent = new CustomEvent("myEvent", {
detail: { customData: "maxLee" },
});
const div = document.querySelector("div");
div.addEventListener("myEvent", function (e) {
console.log(event.detail);
});
div.dispatchEvent(customEvent);
认识了 CustomEvent 後,我们来假设一个需求:「今天有个页面,在进入时会向後端 request 资料,当资料回来後,我们要更改页面的标题及一个 list 的内容」,当然了,如果使用前端框架的话,这是一个非常简单的事情,但我们先假如这次专案不允许使用框架,那一般的写法可能会是这样:
function updateTitle(title) {
const title = document.querySelector("h1");
title.textContent = title;
}
function updateList(list) {
const ul = document.querySelector("ul");
ul.innerHtml = "";
list.forEach((item) => {
const li = document.createElement("li");
li.textContent = item;
ul.appendChild(li);
});
}
function onDataFetch(res) {
updateTitle(res.data.title);
updateList(res.data.list);
}
// 如果不认识 axios,可以把它当成一个请求资料的 Promise 即可
axios.get("https://backend/data").then(onDataFetch);
以上这样的写法其实已经算是尽量避免耦合了,因为还额外包装了一支 onDataFetch
函式来独立处理取得资料後的事情,但如果未来还有其他的事情要处理,就必须再加进这个函式中,而且其他人在阅读时,可能会误以为里面执行内容可能有顺序性。那接下来我们看看使用 CustomEvent 可以怎麽写:
let dataFetchEventTarget = [];
function addDataFetchEvent(element, callback) {
dataFetchEventTarget.push(element);
element.addEventListener("dataFetch", callback);
}
addDataFetchEvent(document.querySelector("h1"), function (e) {
this.textContent = e.detail.title;
});
addDataFetchEvent(document.querySelector("ul"), function (e) {
this.innerHtml = "";
e.detail.list.forEach((item) => {
const li = document.createElement("li");
li.textContent = item;
this.appendChild(li);
});
});
// 用 setTimeout 来模拟请求资料
setTimeout(() => {
const dataFetchEvent = new CustomEvent("dataFetch", {
detail: res.data,
});
dataFetchEventTarget.forEach((target) => {
target.dispatchEvent(dataFetchEvent);
});
}, 3000);
首先我们先宣告了一个阵列 dataFetchEventTarget
,打算来存放所有有注册事件的元素,然後写了一个函式 addDataFetchEvent
来注册事件,并且同时将元素丢进阵列中,直到我们将资料请求回来後开始建立自订事件,并且把 dataFetchEventTarget
中的元素一一取出并 dispatchEvent
事件。
这样写法的好处在於,「取得资料」跟「後续行为」完全没有耦合,「注册事件」与「触发事件」完全是独立的两件事,所以未来如果有其他地方注册了这个事件,我们也不需要额外处理任何事,等到事件触发了,Callback 自然会去执行。
一般来说,没有特别去设计的话,大家都会使用第一种方式吧?但其实使用 CustomEvent 的话,会很接近 Design Patterns 中的观察者模式(Observer Pattern),其实是一个非常不错的撰写方式,大家可以在未来的开发中尝试看看。
<<: VoK 系统功能权责划分 ( II ) - day14
上网不须要浏览器 Webbrowser模组 用地址查询地图的程序设计 下载网页资讯使用reques...
介绍Jenkins的章节即将进入尾声了。事实上你可能会想Jenkins默认介面这麽老气,怎麽就成为全...
盐水豆签羹 地点:台南市盐水区朝琴路19号 时间:14:00~19:00 这一家的照片忘了拍 但是还...
前言 在 Windows 10 底下的 Ubuntu 18.04 LTS 执行 Ruby on Ra...
强型闯入DenoLand[27] - Web API 介绍 终於来到本系列文的最终阶段 - Web...