本文将介绍不通过框架(vue或者react等)创建一个弹窗组件。
首先先创建一个html文件,并包含以下弹窗组件的结构代码:
<div class="vanilla-modal">
<div class="modal">
<div class="modal-inner">
<div id="modal-content">div>
div>
div>
div>
并创建一个css文件为上面的html结构添加样式。
.modal{
display: block;
position: fixed;
content: "";
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
z-index: -1;
opacity: 0;
transition: opacity 0.2s, z-index 0s 0.2s;
text-align: center;
white-space: nowrap;
-webkit-overflow-scrolling: touch;
}
.modal > * {
display: inline-block;
white-space: normal;
vertical-align: middle;
text-align: left;
}
.modal:before {
display: inline-block;
overflow: hidden;
width: 0;
height: 100%;
vertical-align: middle;
content: "";
}
.modal-visible .modal {
z-index: 9999;
opacity: 1;
transition: opacity 0.2s;
}
.modal-inner {
position: relative;
overflow: hidden;
max-width: 90%;
max-height: 90%;
background: #fff;
z-index: -1;
opacity: 0;
transform: scale(0);
transition: opacity 0.2s, transform 0.2s, z-index 0s 0.2s;
min-width: 500px;
border-radius: 6px;
}
.modal-visible .modal-inner {
z-index: 100;
opacity: 1;
transform: scale(1);
transition: opacity 0.2s, transform 0.2s;
}
#modal-content{
padding: 50px 70px;
}
下面的代码允许我们初始化弹窗,打开它,然后关闭它。弹窗将监听模态外部的点击,点击关闭按钮,按下转义键,并在模态内部处理标签导航。
function outsideClick(e) {
if (e.target.closest(".modal-inner")) {
return;
}
const modalVisible = document.querySelector(".modal-visible");
if (modalVisible) {
closeModal();
}
}
function escKey(e) {
if (e.keyCode == 27) {
closeModal();
}
}
function closeClick(e) {
if (e.target.classList.contains("closeModal")) {
closeModal();
}
}
function trapTabKey(e) {
const vanillaModal = document.querySelector(".vanilla-modal");
const FOCUSABLE_ELEMENTS = [
"a[href]",
"area[href]",
'input:not([disabled]):not([type="hidden"]):not([aria-hidden])',
"select:not([disabled]):not([aria-hidden])",
"textarea:not([disabled]):not([aria-hidden])",
"button:not([disabled]):not([aria-hidden])",
"iframe",
"object",
"embed",
"[contenteditable]",
'[tabindex]:not([tabindex^="-"])',
];
const nodes = vanillaModal.querySelectorAll(FOCUSABLE_ELEMENTS);
let focusableNodes = Array(...nodes);
if (focusableNodes.length === 0) return;
focusableNodes = focusableNodes.filter((node) => {
return node.offsetParent !== null;
});
// if disableFocus is true
if (!vanillaModal.contains(document.activeElement)) {
focusableNodes[0].focus();
} else {
const focusedItemIndex = focusableNodes.indexOf(document.activeElement);
if (e.shiftKey && focusedItemIndex === 0) {
focusableNodes[focusableNodes.length - 1].focus();
e.preventDefault();
}
if (
!e.shiftKey &&
focusableNodes.length > 0 &&
focusedItemIndex === focusableNodes.length - 1
) {
focusableNodes[0].focus();
e.preventDefault();
}
}
}
function closeModal() {
const vanillaModal = document.querySelector(".vanilla-modal");
if (vanillaModal) {
vanillaModal.classList.remove("modal-visible");
document.getElementById("modal-content").innerHTML = "";
document.getElementById("modal-content").style = "";
}
document.removeEventListener("keydown", escKey);
document.removeEventListener("click", outsideClick, true);
document.removeEventListener("click", closeClick);
document.removeEventListener("keydown", trapTabKey);
}
const modal = {
init: function () {
const prerendredModal = document.createElement("div");
prerendredModal.classList.add("vanilla-modal");
const htmlModal = `
`;
prerendredModal.innerHTML = htmlModal;
document.body.appendChild(prerendredModal);
},
open: function (idContent, option = { default: null }) {
let vanillaModal = document.querySelector(".vanilla-modal");
if (!vanillaModal) {
console.log("there is no vanilla modal class");
modal.init();
vanillaModal = document.querySelector(".vanilla-modal");
}
const content = document.getElementById(idContent);
let currentModalContent = content.cloneNode(true);
currentModalContent.classList.add("current-modal");
currentModalContent.style = "";
document.getElementById("modal-content").appendChild(currentModalContent);
if (!option.default) {
if (option.width && option.height) {
document.getElementById("modal-content").style.width = option.width;
document.getElementById("modal-content").style.height = option.height;
}
}
vanillaModal.classList.add("modal-visible");
document.addEventListener("click", outsideClick, true);
document.addEventListener("keydown", escKey);
document.addEventListener("keydown", trapTabKey);
document
.getElementById("modal-content")
.addEventListener("click", closeClick);
},
close: function () {
closeModal();
},
};
我们来详细介绍以下上面的代码:
outsideClick 函数负责处理弹窗外的点击。如果点击目标不在弹窗内,它会触发closeModal 功能关闭弹窗。
escKey 函数检查是否按了转义键(ESC),如果按了,它会调用closeModal 函数。
closeClick 函数在弹窗中监听关闭按钮上的单击事件。如果点击目标具有类"闭式",它将触发closeModal 函数。
trapTabKey 函数用于在弹窗中捕获键盘焦点.它首先基于提供的数组检索弹窗内所有可聚焦的元素,然后过滤掉当前不可见的任何元素。
closeModal 函数负责关闭弹窗.它找到弹窗元素并删除与弹窗可见性相关的类和内容。它还删除了事件侦听器的键,点击外部的弹窗,并点击关闭按钮。modal 对象有三种方法:
init()这个方法负责初始化弹窗。它创建了弹窗的HTML结构并将其附加到文档体中。open(idContent, option)这个方法用来打开弹窗。需要两个参数:idContent(必须):包含要在模式中显示的内容的元素的ID。option (可选):允许指定附加选项的对象。目前它只支持设置width 和height。close() 这个方法直接调用closeModal 函数关闭弹窗。创建一个h1元素作为弹窗内容:
<h1 id="title">弹窗内容h1>
通过open方法打开弹窗:
modal.open("title");
