js 模拟 Prompt 对话框

js模拟Prompt 对话框

效果

image

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>自定义 Prompt 对话框</title>
    <style>
    </style>
</head>

<body>
    <script>
    // 样式变量
    const dialogStyleText = `
    .prompt-dialog {
        display: none;
        position: fixed;
        z-index: 9999;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        padding: 20px;
        border: 1px solid #ccc;
        background-color: white;
        box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
        width: 400px; /* 设置初始宽度 */
        text-align: left; /* 左对齐 */
        font-size: 14px;
    }
    .prompt-overlay {
        display: none;
        position: fixed;
        z-index: 9998;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0);
        /*background-color: rgba(0, 0, 0, 0.5);
        backdrop-filter: blur(3px);*/
    }
    .prompt-dialog .prompt-text {
        margin-bottom: 10px;
    }
    .prompt-dialog .prompt-input-container {
        margin-bottom: 20px;
    }
    .prompt-dialog .prompt-input-container input[type="text"] {
        width: calc(100% - 16px); /* 输入框占满容器宽度 */
        padding: 5px; /* 增加填充 */
        margin-bottom: 10px;
    }
    .prompt-dialog .prompt-input-container input[type="text"]:last-child {
        margin-bottom: 0;
    }
    .prompt-dialog .prompt-button-container {
        display: flex;
        justify-content: flex-end; /* 按钮靠右 */
    }
    .prompt-dialog button {
        margin-left: 10px;
    }
    @keyframes prompt-shake {
        0% { transform: translate(-50%, -50%) rotate(0deg); }
        25% { transform: translate(-50%, -50%) rotate(2deg); }
        50% { transform: translate(-50%, -50%) rotate(0deg); }
        75% { transform: translate(-50%, -50%) rotate(-2deg); }
        100% { transform: translate(-50%, -50%) rotate(0deg); }
    }
    .prompt-dialog.prompt-shake {
        animation: prompt-shake 0.5s ease-in-out;
    }
  `;
    // HTML模板
    const dialogHtml = `
    <div class="prompt-text" id="promptText">请输入</div>
    <div class="prompt-input-container">
      <input type="text" id="promptInput" />
    </div>
    <div class="prompt-button-container">
      <button id="promptCancel">取消</button>
      <button id="promptSubmit">确认</button>
    </div>
  `;
    // 创建样式
    const dialogStyle = document.createElement('style');
    dialogStyle.textContent = dialogStyleText;
    document.head.appendChild(dialogStyle);
    // 创建对话框
    const dialog = document.createElement('div');
    dialog.className = 'prompt-dialog';
    dialog.id = 'promptDialog';
    dialog.innerHTML = dialogHtml;
    document.body.appendChild(dialog);
    // 创建遮罩层
    const overlay = document.createElement('div');
    overlay.className = 'prompt-overlay';
    document.body.appendChild(overlay);
    // 获取元素
    const promptContainer = document.querySelector(".prompt-input-container");
    const submitButton = document.getElementById('promptSubmit');
    const cancelButton = document.getElementById('promptCancel');
    const promptText = document.getElementById('promptText');
    let focusEl;
    // 弹出对话框
    function showPrompt(message = '', defaultValue = '', okName="确定", cancelName="取消", width=400, modalMode=true, clickOverlayClose=false) {
        return new Promise((resolve, reject) => {
            // 初始化窗口
            promptText.textContent = message;
            let input = document.getElementById('promptInput');
            if(!input) {
                promptContainer.innerHTML = `<input type="text" id="promptInput" />`;
                input = document.getElementById('promptInput');
            }
            input.value = defaultValue || '';
            submitButton.textContent = okName || '确定';
            cancelButton.textContent = cancelName || '取消';
            if(!cancelName) cancelButton.style.display = 'none';
            dialog.style.display = 'block';
            if(modalMode) overlay.style.display = 'block';
            input.focus();
            focusEl = promptContainer.querySelector(":focus");
            // 监听按钮点击事件
            submitButton.addEventListener('click', () => {
                dialog.style.display = 'none';
                if(modalMode) overlay.style.display = 'none';
                document.removeEventListener('keydown', handleKeydown);
                resolve(input.value);
            });
            cancelButton.addEventListener('click', () => {
                dialog.style.display = 'none';
                if(modalMode) overlay.style.display = 'none';
                document.removeEventListener('keydown', handleKeydown);
                resolve(null);
            });
            // 添加键盘事件监听器
            const handleKeydown = () => {
                if (event.key === 'Escape') {
                    cancelButton.click();
                } else if (event.key === 'Enter') {
                    submitButton.click();
                }
            };
            document.addEventListener('keydown', handleKeydown);
            // 监听焦点事件,focus冒泡必须第三参数是true
            promptContainer.addEventListener('focus', (event) => {
                focusEl = event.target;
            }, true);
            // 添加点击遮罩层关闭对话框的功能
            if(modalMode){
                overlay.addEventListener('click', () => {
                    // 点击遮罩层关闭弹窗
                    if(clickOverlayClose) cancelButton.click();
                    // 重新获取焦点
                    if(focusEl) focusEl.focus();
                    // 添加抖动类
                    dialog.classList.add('prompt-shake');
                    // 移除抖动类
                    setTimeout(() => {
                        dialog.classList.remove('prompt-shake');
                    }, 500);
                });
            }
        });
    }
    // 弹出对话框表单
    function showPromptForm(message = '', html = '', onSubmit = null, onOpen = null, okName="确定", cancelName="取消", width=400, modalMode=true, clickOverlayClose=false) {
        return new Promise((resolve, reject) => {
            // 初始化窗口
            if(html) promptContainer.innerHTML = html;
            promptText.textContent = message;
            submitButton.textContent = okName || '确定';
            cancelButton.textContent = cancelName || '取消';
            if(!cancelName) cancelButton.style.display = 'none';
            dialog.style.display = 'block';
            if(modalMode) overlay.style.display = 'block';
            if(typeof onOpen === 'function') onOpen(promptContainer);
            focusEl = promptContainer.querySelector(":focus");
            // 监听按钮点击事件
            submitButton.addEventListener('click', () => {
                dialog.style.display = 'none';
                if(modalMode) overlay.style.display = 'none';
                document.removeEventListener('keydown', handleKeydown);
                if(typeof onSubmit === 'function') {
                    resolve(onSubmit(promptContainer));
                } else {
                    resolve(undefined);
                }
            });
            cancelButton.addEventListener('click', () => {
                dialog.style.display = 'none';
                if(modalMode) overlay.style.display = 'none';
                document.removeEventListener('keydown', handleKeydown);
                resolve(null);
            });
            // 添加键盘事件监听器
            const handleKeydown = () => {
                if (event.key === 'Escape') {
                    cancelButton.click();
                } else if (event.key === 'Enter') {
                    submitButton.click();
                }
            };
            document.addEventListener('keydown', handleKeydown);
            // 监听焦点事件,focus冒泡必须第三参数是true
            promptContainer.addEventListener('focus', (event) => {
                focusEl = event.target;
            }, true);
            // 添加点击遮罩层关闭对话框的功能
            if(modalMode){
                overlay.addEventListener('click', () => {
                    // 点击遮罩层关闭弹窗
                    if(clickOverlayClose) cancelButton.click();
                    // 重新获取焦点
                    if(focusEl) focusEl.focus();
                    // 添加抖动类
                    dialog.classList.add('prompt-shake');
                    // 移除抖动类
                    setTimeout(() => {
                        dialog.classList.remove('prompt-shake');
                    }, 500);
                });
            }
        });
    }

    // 示例用法
    (async () => {
        // showPrompt 示例用法
        const result = await showPrompt('这里是提示文本', '');
        console.log(result);

        // showPromptForm 示例用法
        // const result = await showPromptForm('这里是提示文本', `
        //   <input type="text" id="promptName" placeholder="标题" />
        //   <input type="text" id="promptHref" placeholder="链接" />
        // `, (promptContainer) => {
        //     const inputs = promptContainer.querySelectorAll('input[type="text"]');
        //     const values = Array.from(inputs).map(input => input.value);
        //     return values;
        // }, ()=> {
        //     document.getElementById("promptName").focus();
        // });
        // console.log(result);
    })();

    </script>
</body>

</html>

注意事项

  1. showPrompt​ 和 showPromptForm​ 的区别,见上面的效果图,上面一个是 showPrompt,这个是对 window.prompt 的模拟,下面一个是 showPromptForm,是对 showPrompt 的扩展,可以用于实现复杂的表单功能。
  2. 默认使用模态模式,如果不使用模态(即不使用遮罩层),调用时把 modalMode 参数设置为 false 即可。
  3. 模态窗口的背景色默认透明,和window.prompt保持一致,如果你想修改背景色,可以修改样式.prompt-overlay 的 background-color: rgba(0, 0, 0, 0);​ 这行代码。
  4. 模态窗口的宽度默认是 400px,如果想自定义宽度可通过 width 参数设置。
  5. 如果想支持 点击遮罩层关闭对话框的功能​,把 clickOverlayClose 参数设置为 true 即可。
  6. 点确认按钮返回值是字符串,点取消返回值是 null,但 showPromptForm,点确定的返回值是自己定义的。
  7. 按钮文字可通过 cancelName​ 和 okName​ 参数设置。
  8. 更多功能请参考源码。

接口定义及说明如下

// 弹出prompt对话框
// 参数说明
// message 提示消息,类似标题
// defaultValue 输入框默认值
// okName 确认按钮名字
// cancelName 取消按钮名字
// width对话框宽度,默认400,高度根据内容自适应
// modalMode,是否模态窗口,模态窗口,除了当前对话框,其他地方无法点击,false非模态窗,true模态窗
// clickOverlayClose 是否点击遮罩层关闭对话框,仅在模态窗口下有效
function showPrompt(message = '', defaultValue = '', okName="确定", cancelName="取消", width=400, modalMode=true, clickOverlayClose=false);


// 弹出自定义prompt对话框(可自定义定义复杂表单)
// 参数说明
// message 提示消息,类似标题
// html 对话框自定义表单内容的HTML代码
// onSubmit 提交时的回调函数,对话框的返回值依赖于这个回调函数的返回值
// onOpen 打开窗口时的回调函数,可以在打开窗口话进行一些初始化操作,比如某个输入框设为焦点
// okName 确认按钮名字
// cancelName 取消按钮名字
// width 对话框宽度,默认400px,高度根据内容自适应
// modalMode,是否模态窗口,模态窗口,除了当前对话框,其他地方无法点击,false非模态窗,true模态窗
// clickOverlayClose 是否点击遮罩层关闭对话框,仅在模态窗口下有效
function showPromptForm(message = '', html = '', onSubmit = null, onOpen = null, okName="确定", cancelName="取消", width=400, modalMode=true, clickOverlayClose=false);

思源版prompt

https://ld246.com/article/1723719060687

image.png

留下你的脚步
推荐阅读