SOURCE

console 命令行工具 X clear

                    
>
console
((name, root) => {
    const { console, imports } = (function (root) {
        if (typeof root.imports === "undefined" || !Object.isSealed(root.imports)) {
            function imports(dependencies, callback) {
                const deps = [].concat(dependencies);
                addEventListener("import", _imports);
                function _imports() {
                    if (callback != null) {
                        if (deps.some(name => !root.imports.hasOwnProperty(name))) {
                            return;
                        }
                        removeEventListener("import", _imports);
                        const values = deps.map(name => root.imports[name]);
                        if (typeof requestIdleCallback === "function") {
                            requestIdleCallback(() => callback(...values), { timeout: 100 });
                        } else if (typeof setTimeout === "function") {
                            setTimeout(() => callback(...values));
                        } else {
                            callback(...values);
                        }
                    }
                }
                _imports();
            }
            root.imports = new Proxy(imports, {
                get: (target, prop) => target[prop],
                set: (target, prop, value) => {
                    if (target.hasOwnProperty(prop)) {
                        console.warn(`import: ${prop} has been replaced!`, { old: target[prop], new: value });
                    }
                    target[prop] = value;
                    if (typeof window !== "undefined") {
                        window[prop] = value;
                    }
                    dispatchEvent(new CustomEvent("import", { detail: { name, value } }));
                    return true;
                }
            });
            Object.defineProperty(root, "imports", {
                configurable: false,
                enumerable: false,
                writable: false,
            });

            Promise.createTask = function () {
                const task = {};
                const promise = new Promise((resolve, reject) => {
                    task.resolve = resolve;
                    task.reject = function (msg, unhandledrejection) {
                        const error = msg instanceof Error ? msg : new Error(msg || "promise rejected", { cause: false });
                        if (error.cause == null && unhandledrejection !== true) {
                            error.cause = false;
                        }
                        reject(error);
                    }
                });
                task.promise = promise;
                task.then = promise.then.bind(promise);
                task.catch = promise.catch && promise.catch.bind(promise);
                task.finally = promise.finally && promise.finally.bind(promise);
                return task;
            };
            addEventListener("unhandledrejection", e => e.reason && e.reason.cause === false && e.preventDefault());

        }
        return {
            console: new Proxy(root.console || console, {
                get: (target, prop) => target && target.debugs && target.debugs.some(x => x === name || x === "*") ? target[prop] : () => { },
            }),
            imports: root.imports,
        }
    })(root || this);

    if (imports[name]) {
        return;
    }

    // #region polyfill

    // 兼容 closest
    if (!Element.prototype.closest) {
        Element.prototype.closest = function (selector) {
            let elem = this;
            while (elem) {
                if (elem.matches(selector)) {
                    return elem;
                }
                elem = elem.parentElement;
            }
            return null;
        }
    }

    Element.prototype.isMouseover = function (e, rect) {
        if (e.clientX == null || e.clientY == null) {
            return false;
        }
        if (rect == null) {
            rect = this.getBoundingClientRect();
        }
        return e.clientX >= rect.left && e.clientX <= rect.right && e.clientY >= rect.top && e.clientY <= rect.bottom;
    };

    // setAttributes
    Element.prototype.setAttributes = function (attrs) {
        if (attrs == null) {
            return;
        }
        for (let key in attrs) {
            if (attrs[key] !== false) {
                this.setAttribute(key, attrs[key]);
            }
        }
    };

    // addEventListeners
    Element.prototype.addEventListeners = function (events, useCapture) {
        if (events == null) {
            return;
        }
        const controller = new AbortController();
        for (const key in events) {
            const handler = events[key];
            for (const ev of key.split(",")) {
                this.addEventListener(ev.trim(), handler, { signal: controller.signal, useCapture });
            }
        }
        return () => controller.abort();
    };

    // #endregion

    // #region prototype.methods

    // 触发对话框 确认/取消 事件
    (function (events) {
        for (const type of events) {
            HTMLDialogElement.prototype[type] = function (returnValue) {
                const event = new CustomEvent(type, { cancelable: true, detail: { returnValue } });
                this.dispatchEvent(event);
                if (event.defaultPrevented) {
                    return;
                }
                this.close(returnValue);
            };
        }
    })(["cancel", "confirm"])
    // #endregion

    // #region prototype.props

    // 是否处于模态状态
    Object.defineProperty(HTMLDialogElement.prototype, "isModal", {
        configurable: false,
        enumerable: false,
        get() { return this.matches("dialog:modal"); }
    });

    // 当前激活的对话框功能
    Object.defineProperty(root, "activeDialog", {
        configurable: false,
        enumerable: false,
        get() { return document.activeElement.closest("dialog"); }
    });


    // #endregion

    // #region prototype.events

    // 支持 open 事件
    addEventListener("DOMContentLoaded", e => {

        function redirectMouseEvent(dialog) {
            if (dialog.isModal) {
                const controller = new AbortController();
                let mouse = null;
                dialog.addEventListener("mousemove", e => {
                    const mouse2 = dialog.isMouseover(e) ? 0 : 1;
                    if (mouse === mouse2) {
                        return;
                    }
                    mouse = mouse2;
                    const events1 = ["mouseover", "mouseout"];
                    const events2 = ["mouseenter", "mouseleave"];
                    dialog.dispatchEvent(new MouseEvent(events1[mouse], e));
                    dialog.dispatchEvent(new MouseEvent(events2[mouse], e));
                    console.log("手动触发:" + events1[mouse]);
                    console.log("手动触发:" + events2[mouse]);
                }, { signal: controller.signal });
                dialog.addEventListener("close", e => controller.abort(), { signal: controller.signal, once: true });
                return controller;
            }
        }

        function dialogOpen(dialog) {
            // 如果是模态对话框,需要手动触发 mouseover/mouseout 与 mouseenter/mouseleave 事件
            let controller = redirectMouseEvent(dialog);
            const delayclose = dialog.getAttribute("delayclose");
            if (!delayclose) {
                return;
            }
            let [delay, refresh] = delayclose.split(",").map(x => parseInt(x));
            if (!Number.isFinite(delay) || delay <= 0) {
                return;
            }
            if (!Number.isFinite(refresh)) {
                refresh = delay;
            }
            const firstCloseTime = Date.now() + delay;
            let timer = setTimeout(() => dialog.cancel(), delay);
            if (controller == null) {
                controller = new AbortController();
                dialog.addEventListener("close", e => controller.abort(), { signal: controller.signal, once: true });
            }
            controller.signal.addEventListener("abort", e => clearTimeout(timer), { once: true });

            if (refresh >= 0) {
                dialog.addEventListeners({
                    "mouseenter": e => {
                        console.log(e.type, e);
                        if (dialog.isModal || !dialog.isMouseover(e)) {
                            return;
                        }
                        clearTimeout(timer);
                    },
                    "mouseleave": e => {
                        console.log(e.type, e);
                        clearTimeout(timer);
                        timer = setTimeout(() => dialog.cancel(), Math.max(refresh, firstCloseTime - Date.now()));
                    },
                }, { signal: controller.signal });
            }
        }

        const config = { subtree: true, attributeFilter: ["open"], attributeOldValue: true };
        const observer = new MutationObserver(r => callback(r));
        observer.observe(document.body, config);
        setInterval(() => callback(observer.takeRecords()), 100);
        dispatchEvent(document.querySelectorAll("dialog[open]"));
        function callback(records) {
            const elems = records.filter(x => x.type === "attributes" && x.oldValue == null && x.target.nodeName === 'DIALOG' && x.target.hasAttribute("open")).map(x => x.target);
            dispatchEvent(elems);
        }

        function dispatchEvent(elems) {
            for (const elem of elems) {
                console.log("dispatchEvent:open", elem);
                elem.setAttribute("open", "true");
                dialogOpen(elem);
                elem.dispatchEvent(new Event("open"), { cancelable: false, bubbles: false });
            }
        }
    });

    // 增加对话框移动功能
    (function (eventType) {
        addEventListener(eventType + "down", e => {
            if (e.button !== 0 || !e.target.matches || !e.target.matches("dialog[open][moveable] > header, dialog[open][moveable] ")) {
                return;
            }
            const dialog = e.target.closest("dialog[open][moveable]");
            if (!dialog) {
                return;
            }
            const rect = dialog.getBoundingClientRect();
            if (!dialog.isMouseover(e, rect)) {
                return;
            }
            console.log(e);
            e.preventDefault();


            const mover = (function () {
                const start = (() => {
                    const [left, top] = getComputedStyle(dialog).getPropertyValue("transform").split(", ").slice(-2).map(x => parseInt(x));
                    return { left: left || 0, top: top || 0, x: e.clientX, y: e.clientY };
                })();
                const little = 20;
                const limit = {

                    top: {
                        min: -rect.top + start.top - rect.height + little,
                        max: window.innerHeight - rect.top - rect.height + start.top + rect.height - little
                    },
                    left: {
                        min: -rect.left + start.left - rect.width + little,
                        max: window.innerWidth - rect.left - rect.width + start.left + rect.width - little
                    }
                };

                const result = { top: start.top, left: start.left };

                result.move = (x, y) => {
                    const to = {
                        top: start.top + y - start.y,
                        left: start.left + x - start.x,
                    }

                    // 不允许超出屏幕范围
                    if (to.top < limit.top.min) {
                        to.top = limit.top.min;
                    } else if (to.top > limit.top.max) {
                        to.top = limit.top.max;
                    }
                    if (to.left < limit.left.min) {
                        to.left = limit.left.min;
                    } else if (to.left > limit.left.max) {
                        to.left = limit.left.max;
                    }

                    result.top = to.top;
                    result.left = to.left;

                    console.log({ x, y, top: result.top, left: result.left, limit });
                }
                return result;
            })();

            const frame = 30;
            const transition = dialog.style.transition;
            dialog.style.setProperty("transition", `all ${(1 / frame).toFixed(2)}s linear`, "important");
            const timer = setInterval(() => moveDialog(dialog, mover.left, mover.top), 1000 / frame);
            const controller = new AbortController();
            controller.signal.addEventListener("abort", () => {
                clearInterval(timer);
                dialog.style.transition = transition;
                e.pointerId && dialog.releasePointerCapture(e.pointerId);
            });
            addEventListener(eventType + "move", e => mover.move(e.clientX, e.clientY), { signal: controller.signal });
            addEventListener(eventType + "up", e => controller.abort(), { signal: controller.signal });
            e.pointerId && dialog.setPointerCapture(e.pointerId);
        });
    })(typeof root.PointerEvent === "undefined" ? "mouse" : "pointer");

    // 对话框点击相关功能
    addEventListener("click", e => {
        // 所有点击事件
        const clickHandlers = {
            "dialog[open] button": (x, e) => {
                const method = x.getAttribute("formmethod") || (x.form && x.form.method);
                if (method !== "dialog") {
                    return;
                }
                e.preventDefault();
                const dialog = x.closest("dialog[open]");
                (x.type === "submit") ? dialog.confirm(x.value) : dialog.cancel(x.value);
            },
            "dialog[open][autoclose]": (x, e) => (!x.isMouseover(e)) && x.cancel(),
        };

        for (let selector in clickHandlers) {
            if (e.target.matches && e.target.matches(selector)) {
                clickHandlers[selector](e.target, e);
                return;
            }
        }
    });


    // 增加对话框取消确认功能
    addEventListener("cancel", e => {
        const dialog = e.target;
        if (!dialog.matches || !dialog.matches("dialog[open]")) {
            return;
        }
        if (!e.defaultPrevented) {
            dialog.setAttribute("open", "");
        }

        // 对话框是否有过渡动画
        const duration = getComputedStyle(dialog).getPropertyValue("transition-duration") || "";
        const ms = duration.slice(-2) === "ms" ? parseFloat(duration) : parseFloat(duration) * 1000;
        const resultValue = e.detail.returnValue;
        const animationClose = function () {
            dialog.style.transform = "";
            setTimeout(() => dialog.close(resultValue), ms);
        }

        const text = dialog.getAttribute("confirm");
        if (text != null) {
            e.preventDefault();
            msgbox(ebuilder.h3(text || "确定要关闭吗?"), "confirm", dialog).then(animationClose);
        }
        if (ms > 0) {
            e.preventDefault();
            animationClose();
        }
    }, true);

    // #endregion

    // 对话框队列
    function DialogQueue(change) {
        const dialogs = [];
        function active(dialog) {
            dialog && dialog.dispatchEvent(new Event("active"));
        }

        function inactive(dialog) {
            dialog && dialog.dispatchEvent(new Event("inactive"));
        }
        this.push = dialog => {
            dialog.addEventListener("close", e => {
                const index = dialogs.indexOf(dialog);
                if (index >= 0) {
                    dialogs.splice(index, 1);
                    if (index > 0 && index === dialogs.length) {
                        active(dialogs[index - 1]);
                    }
                    change && change(dialogs, { type: "remove", dialog });
                }
            });
            dialogs.push(dialog);
            if (dialogs.length > 1) {
                inactive(dialogs[dialogs.length - 2]);
            }
            change && change(dialogs, { type: "add", dialog });
        };
    }
    // 构建元素
    const ebuilder = (function () {
        // 构建元素
        function createElement(tag, children, attrs, events) {
            const elem = document.createElement(tag);
            if (children) {
                if (typeof children === "string") {
                    elem.innerHTML = children;
                } else {
                    for (const item of [].concat(children).map(x => typeof x === "function" ? x() : x)) {
                        if (item instanceof Node) {
                            elem.appendChild(item);
                        } else if (item) {
                            elem.appendChild(document.createTextNode(item));
                        }
                    }
                }
            }
            elem.setAttributes(attrs);
            elem.addEventListeners(events);
            return elem;
        }
        return new Proxy({}, {
            get(target, prop) {
                return (children, attrs, events) => createElement(prop, children, attrs, events);
            }
        });
    })();

    // 居中
    function moveDialog(dialog, x, y) {
        x = typeof x === "number" ? (x + "px") : (x || "0");
        y = typeof y === "number" ? (y + "px") : (y || "0");
        dialog.style.transform = `translate(${x}, ${y})`;
        // const regex = /translate\(([^,]+),([^,]+)\)/;
        // const transform = getComputedStyle(dialog).getPropertyValue("transform").replace(regex, `translate(${x}, ${y})`);
        // if (transform === "none") {
        //     dialog.style.transform = `translate(${x}, ${y})`;
        // } else if (regex.test(transform)) {
        //     dialog.style.transform = transform;
        // } else {
        //     dialog.style.transform = `${transform} translate(${x}, ${y})`;
        // }
    };

    const presetMsgBox = {
        "": {},
        default: {
            dialogQueue: new DialogQueue(),
            closeBtn: true,
            attrs: { style: "max-width: 40%;min-width: 280px;max-height:50%", "moveable": "" }
        },
    };


    /**
     * 合并所有选项, 如果选项是对象需要深度合并, 数组和值对象直接替换 
     */
    function mergeOptions(...options) {
        const merge = (target, source) => {
            if (source == null) {
                return target;
            }
            if (typeof source !== "object" || typeof target !== "object" ||
                target == null || Array.isArray(source) || Array.isArray(target)) {
                return source;
            }
            for (let key in source) {
                target[key] = merge(target[key], source[key]);
            }
            return target;
        };
        return options.reduce(merge);
    }


    /**
     * 显示对话框
     * @param {Element|Element[]|String} content 对话框内容
     * @param {(null|"alert"|"confirm"|"tips"|"duang"|"toast"|Object)} option 对话框类型
     * @param {Element} owner 对话框所有者 
     * @returns
     */
    function msgbox(content, option, owner) {
        // 合并选项
        (typeof option === "string") && (option = { type: option });
        const type = (option && option.type) || "";
        option = mergeOptions({}, presetMsgBox.default, presetMsgBox[type], { ...option }, { type });
        // 构建 <dialog>
        const dialog = ebuilder.dialog([
            option.closeBtn && ebuilder.button("×", { type: "button", formmethod: "dialog" }),
            option.title && ebuilder.header(option.title),
            ebuilder.main(content),
            option.buttons && ebuilder.footer(option.buttons),
        ], option.attrs);
        // 添加类样式
        type && dialog.classList.add(type);
        // 添加事件
        const task = Promise.createTask();
        dialog.addEventListeners({
            close(e) { this.remove(); },
            confirm(e) { task.resolve(e.detail && e.detail.returnValue); },
            cancel(e) { task.reject(e.detail && e.detail.returnValue); },
        });
        // 绑定 promise 属性
        dialog.then = task.then;
        dialog.catch = task.catch;
        dialog.finally = task.finally;
        // 将对话框添加到文档中
        (owner || document.body).appendChild(dialog);
        // 显示对话框
        option.show ? option.show(dialog, owner) : dialog.showModal(owner);
        // 添加到模态对话框队列
        if (dialog.isModal) {
            dialog.addEventListener("active", e => e.target.style.opacity = "");
            dialog.addEventListener("inactive", e => e.target.style.opacity = 0.2);
        }
        option.dialogQueue && option.dialogQueue.push(dialog);
        return dialog;
    }

    // 美化对话框样式
    function beautify() {
        const css = `
        dialog {
            border: 1px solid #ccc;
            border-radius: 5px;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
            min-width: 260px;
            left:0;
            top:0;
            opacity: 0;
        }
        dialog form[method=dialog]:empty {
            display: none;
        }
        dialog.tips * {
            cursor: pointer;
            padding: 0;
        }
        dialog :focus-visible ,dialog:focus-visible {
            outline: 0;
        }
        dialog[open]{
            display: flex;
            flex-direction: column;
            justify-content: space-between;
            opacity: 1;
        }
        dialog[moveable], dialog[moveable] > header {
            cursor: move;
        }
        
        dialog * {
            cursor: initial;
        }
        dialog > header {
            align-items: center;
            padding: 10px;
            padding-top: 0;
            border-bottom: 1px solid #ccc;
            font-size: .9em;
            color: #777;
            font-weight: bold;
        }
        dialog > main, dialog > article {
            padding: 10px;
            border-radius: 5px 5px 0 0;
            word-wrap: break-word;
            overflow-y: auto;
            flex-grow: 1;
        }
    
        dialog > footer {
            border-top: 1px solid #ccc;
            text-align: center;
            padding: 10px;
            padding-bottom: 0;
        }
    
        dialog > footer > button {
            border: none;
            color: white;
            padding: 10px 24px;
            text-align: center;
            text-decoration: none;
            display: inline-block;
            font-size: 16px;
            border-radius: 5px;
            margin-top: 10px;
            cursor: pointer;
        }
        dialog > footer > button ~ button {
            margin-left: 5%;
        }
        dialog > footer > button[type=submit] {
            background-color: #4CAF50;
        } 
        dialog > footer > button[type=reset] {
            background-color: #f44336;
        }
        dialog > button[formmethod=dialog],dialog > header > button[formmethod=dialog],dialog button.close {
            background-color: transparent;
            position: absolute;
            right: -0;
            top: -0;
            min-width: 15px;
            height: 15px;
            line-height: 12px;
            text-align: center;
            color: #777;
            border: 0;
            padding: 0;
            height: 1em;
            width: 1em;
            line-height: 1em;
            cursor: default;
        }
        dialog > button[formmethod=dialog]:hover,dialog > header > button[formmethod=dialog]:hover{
            background-color: #ddd;
        }
        dialog > button[formmethod=dialog]:active,dialog > header > button[formmethod=dialog]:active{
            background-color: #eee;
        }

        dialog[closeBtn]::before {
            pointer-events:none;
            content: "×";
            background-color: transparent;
            position: absolute;
            right: -0;
            top: -0;
            min-width: 15px;
            height: 15px;
            line-height: 12px;
            text-align: center;
            color: #777;
            border: 0;
            padding: 0;
            height: 1em;
            width: 1em;
            line-height: 1em;
            cursor: default;
        }
        dialog[nobackdrop]::backdrop {
            opacity: 0;
        }
        dialog.alert main, dialog.confirm main {
            display: flex;
            flex-direction: colum;
            align-content: center;
            justify-content: center;
            align-items: center;
            font-size: 1.2em;
        }

        dialog.tips {
            background-color: #f0f9eb;
            border-color: #e1f3d8;
            color: #67c23a;
            max-width: 40%;
            min-height: 0;
            transition: all 0.2s ease-in;
            cursor: pointer;
            margin-bottom: 10px;
            transform:translate(0, -100%);
        }
        dialog.tips[open=true]{
            transform:translate(0, 0);
        }
        dialog.duang {
            background-color: #fdf6ec;
            border-color: #faecd8;
            color: #e6a23c;
            max-width: 40%;
            min-height: 0;
            transform:scale(0.5);
            transition: all 0.05s cubic-bezier(0.68, -0.55, 0, 1.98);
        }
        dialog.duang[open=true]{
            transform:scale(1);
        }
        dialog.toast {
            background-color: #edf2fc;
            border-color: #ebeef5;
            color: #909399;
            max-width: 40%;
            min-height: 0;
            margin-right: 5px;
            margin-top: 5px;
            margin-bottom: 5px;
            transform-origin: center right;
            transition: all 0.3s ease-in;
            transform: scale(0, 1);
        }
        dialog.toast[open=true]{
            transform: scale(1, 1)
        }
        .toast-container {
            position: fixed;
            display: flex;
            flex-direction: column;
            flex-wrap: nowrap;
            align-items: flex-start;
            right: 0;
            top: 0;
        }
        `;
        const style = ebuilder.style(css, { type: "text/css", id: name });
        unbeautify();
        document.head.prepend(style);
    }

    function unbeautify() {
        document.querySelectorAll("head > style#" + name).forEach(x => x.remove());
    }


    Object.assign(presetMsgBox, {
        alert: {
            buttons: [
                () => ebuilder.button("确定", { type: "submit", formmethod: "dialog" }),
            ]
        },
        confirm: {
            buttons: [
                () => ebuilder.button("确定", { type: "submit", formmethod: "dialog" }),
                () => ebuilder.button("取消", { type: "reset", formmethod: "dialog" })
            ]
        },
        tips: {
            dialogQueue: new DialogQueue(function (dialogs) {
                let top = 0;
                for (const dialog of dialogs) {
                    moveDialog(dialog, 0, top);
                    const rect = dialog.getBoundingClientRect();
                    const marginBottom = parseInt(getComputedStyle(dialog).getPropertyValue("margin-bottom"));
                    top += rect.height + marginBottom;
                }
            }),
            closeBtn: false,
            show(dialog, owner) {
                dialog.addEventListener("click", e => dialog.cancel());
                dialog.show(owner);
            },
            attrs: {
                moveable: false,
                delayclose: "3000, 3000",
            }
        },
        toast: {
            dialogQueue: new DialogQueue(function (dialogs) {
                let top = 0;
                for (let i = dialogs.length - 1; i >= 0; i--) {
                    const dialog = dialogs[i];
                    dialog.style.top = `${top}px`;
                    const rect = dialog.getBoundingClientRect();
                    const marginBottom = parseInt(getComputedStyle(dialog).getPropertyValue("margin-bottom"));
                    top += rect.height + marginBottom;
                }
            }),
            closeBtn: false,
            show(dialog, owner) {
                dialog.addEventListener("click", e => dialog.cancel());
                dialog.show(owner);
            },
            attrs: {
                moveable: false,
                delayclose: "3000, 3000",
            }
        },
        duang: {
            dialogQueue: new DialogQueue(),
            closeBtn: false,
            show(dialog, owner) {
                dialog.addEventListener("click", e => dialog.cancel());
                dialog.addEventListener("inactive", e => e.target.close());
                dialog.showModal(owner);
            },
            attrs: {
                moveable: false,
                autoclose: "",
                nobackdrop: "",
                delayclose: "1000, 0",
            }
        }
    });

    imports[name] = {
        msgbox,
        beautify,
        unbeautify,
        presetMsgBox,
        alert(message) { return msgbox(message, "alert"); },
        confirm(message) { return msgbox(message, "confirm"); }
    };

    imports.msgbox = msgbox;
    beautify();
})("DialogExtensions", this);
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>html原生弹窗扩展</title>
</head>

<body>
    <dialog id="d1">
        <button type="submit" form="form1" formnovalidate="true" class="close">x</button>
        <main>
            <form id="form1" action="/post.html" method="dialog" enctype="application/json" target="_ajax" alert
                onerror="console.log(event)">
                <input type="text" name="username" id="username" placeholder="用户名" required> <br />
                <input type="password" name="password" id="password" placeholder="密码" required> <br />
                <input type="text" name="code" id="code" placeholder="验证码" required> <br />
            </form>
        </main>
        <footer>
            <button type="submit" form="form1">确定</button>
            <button type="reset" formmethod="dialog">取消</button>
        </footer>
    </dialog>
    <dialog id="d2" autoclose confirm moveable>
        <button type="reset" formmethod="dialog">x</button>
        <header>标题</header>
        <main>
            点击对话框以外内容触发自动关闭 <br />
            关闭前有2次确认 <br />
            可以使用鼠标拖动对话框
        </main>
        <footer>
            <button type="submit" formmethod="dialog">确定</button>
            <button type="reset" formmethod="dialog">取消</button>
        </footer>
    </dialog>

    <dialog id="d3" delayclose="3000">
        3秒自动关闭
    </dialog>

    <script>
        let duang = 0;
        let tips = 0;
        let toast = 0;
    </script>
    <button onclick="d1.showModal()">弹窗1</button> <br />
    <button onclick="d2.showModal()">弹窗2</button> <br />
    <button onclick="msgbox('这是一个提示!' + (++tips),'tips')">tips</button> <br />
    <button onclick="msgbox('这是一个警告!' + (++duang),'duang')">duang</button> <br />
    <button onclick="msgbox('这是一个横幅!' + (++toast),'toast')">toast</button> <br />
    <button onclick="openModal()">一直弹</button> <br />
    <button onclick="d3.showModal()">3秒自动关闭</button> <br />
    <script>
        // console.debugs = ["*"]
        let num = 0;
        function openModal() {
            num++;
            const dialog = msgbox("点确定打开一个新弹窗,<br /> 当前会暂时隐藏, <br />新弹窗关闭后再显示", { type: "confirm", title: num + ') 这是一个弹窗:' + new Date().toLocaleString(), });
            dialog.addEventListener('confirm', e => {
                e.preventDefault();
                openModal();
            });
        }
    </script>
</body>

</html>