使用原生 JavaScript 创建一个简单的确认模态框
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
你以前试过用这个window.confirm()功能吗?它非常实用,尤其是在你想让用户真正清楚自己在做什么的时候。但是,你试过给它设置样式吗?就像其他功能一样,window.alert()这是不可能的,所以我们需要创建自己的确认模态框。我来教你!
解决什么问题
首先,明确我们要解决的问题很有帮助。我们的模态框必须能够做到以下三点:
- 询问用户应该回答的问题(例如“您真的要删除您的用户帐户吗?”)
- 让用户说“是”。
- 允许用户说“不”
此外,对于开发者来说,window.confirm()它非常易于使用。我们不想让开发者在使用我们自定义的确认功能时比使用其他功能更加困难const theyAreSure = window.confirm('Are you sure');。
原生模态框自带的另一个功能就是模态框本身。我们不希望开发者在使用我们的组件时,每次需要用户确认某些内容时都编写大量的标记,这意味着我们的自定义模态框需要自动生成这些标记。
最终,它应该
- 易于使用
- 在用户确认“是”之前,不要运行任何代码。
如何解决
标记
在本教程中,我们不需要指定复杂的标记,所以就使用这段简单的代码作为我们的 HTML 基础:
<dialog class="confirm-dialog">
<div class="confirm-dialog-question">Do you really want to delete your user account?</div>
<div class="confirm-dialog-button-group">
<button class="confirm-dialog-button confirm-dialog-button--false" type="button">Noo</button>
<button class="confirm-dialog-button confirm-dialog-button--true" type="button">Yes!</button>
</div>
</dialog>
如果您不熟悉这个<dialog>元素,请前往MDN 查看相关文档!简单来说,这是一个 Chrome、Firefox 和 Opera 都支持的原生元素(也有相应的 polyfill),您可以使用它通过以下方法显示模态框showModal():
function createDialog() {
const dialog = document.createElement('dialog');
dialog.textContent = '✨✨✨';
document.body.appendChild(dialog);
dialog.showModal();
}
JavaScript API
通过使用Promise API和async/await,我们可以解决我们之前列出的两个问题:我们可以使代码易于使用,并且可以等待信号来确定何时(或是否)实际运行删除数据库中每个用户的代码。
最终,我们希望组件的使用方式如下所示:
async function deleteUsers() {
const dialog = new ConfirmModal({
questionText: 'Are you sure you want to delete every user?'
});
const deleteEveryUser = await dialog.confirm();
if (deleteEveryUser) {
// ...
}
}
这样就形成了一个易于使用的组件,但这真的可行吗?
JavaScript 会await暂停代码执行,直到它等待的 Promise 被解析或拒绝。Promise 可以通过事件触发的函数进行解析,这也是我们代码的结构。创建新的 Promise 时,我们会为两个按钮添加事件监听器,并根据点击的按钮类型,将 Promise 解析为解析结果(解析为已解决true或false已拒绝),无论用户是否确认。
解决它
我们先来ConfirmDialog为组件创建一个类。它的构造函数需要三个要素:
- 问题文本
- “是”按钮的文本
- “否”按钮的文字
class ConfirmDialog {
constructor({
questionText,
trueButtonText,
falseButtonText
}) {
this.questionText = questionText || 'Are you sure?';
this.trueButtonText = trueButtonText || 'Yes';
this.falseButtonText = falseButtonText || 'No';
this.dialog = undefined;
this.trueButton = undefined;
this.falseButton = undefined;
this.parent = document.body;
this._createDialog();
this._appendDialog();
}
}
我创建了一个方法来创建<dialog>元素及其子元素,一个方法将其添加到元素中<body>,还有一个方法将其从元素主体中移除并删除我们的ConfirmDialog对象。它们如下所示:
_createDialog() {
this.dialog = document.createElement("dialog");
this.dialog.classList.add("confirm-dialog");
const question = document.createElement("div");
question.textContent = this.questionText;
question.classList.add("confirm-dialog-question");
this.dialog.appendChild(question);
const buttonGroup = document.createElement("div");
buttonGroup.classList.add("confirm-dialog-button-group");
this.dialog.appendChild(buttonGroup);
this.falseButton = document.createElement("button");
this.falseButton.classList.add(
"confirm-dialog-button",
"confirm-dialog-button--false"
);
this.falseButton.type = "button";
this.falseButton.textContent = this.falseButtonText;
buttonGroup.appendChild(this.falseButton);
this.trueButton = document.createElement("button");
this.trueButton.classList.add(
"confirm-dialog-button",
"confirm-dialog-button--true"
);
this.trueButton.type = "button";
this.trueButton.textContent = this.trueButtonText;
buttonGroup.appendChild(this.trueButton);
}
_appendDialog() {
this.parent.appendChild(this.dialog);
}
_destroy() {
this.parent.removeChild(this.dialog);
delete this;
}
现在,进入最后一部分。我们来创建confirm()方法。在方法内部,我们需要显示模态框,并为两个“是/否”按钮创建事件监听器,使它们分别解析为“是”true或“否”,false然后移除组件本身的所有痕迹。
confirm() {
return new Promise((resolve, reject) => {
const somethingWentWrongUponCreation =
!this.dialog || !this.trueButton || !this.falseButton;
if (somethingWentWrongUponCreation) {
reject("Something went wrong upon modal creation");
}
this.dialog.showModal();
this.trueButton.addEventListener("click", () => {
resolve(true);
this._destroy();
});
this.falseButton.addEventListener("click", () => {
resolve(false);
this._destroy();
});
});
}
不错!我在这里测试过了: