js 代码片段模拟 window.prompt 函数

本贴最后更新于 271 天前,其中的信息可能已经渤澥桑田

众所周知,思源不支持 prompt,确切的说 Electron 不支持。

但有时写脚本时确实需要一些交互式操作,如果使用思源 api 调用 Dialog 确实可以实现,但终究是麻烦了些。

就自己动手写了一个 prompt 模拟函数,放到 js 代码片段中即可,然后需要的时候就可以直接调用了。

效果

Snipaste20240816091141.png

Snipaste20240816091253.png

接口定义如下

// 弹出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);

调用示例

// 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);

完整代码如下

// 功能:模拟window.prompt函数 // 使用示例 /* // 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); */ (()=>{ // 样式变量 const dialogStyleText = ` .prompt-dialog { display: none; position: fixed; z-index: 9999; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: 20px; border: 1px solid var(--b3-theme-surface-lighter); border-radius: var(--b3-border-radius-b); box-shadow: var(--b3-dialog-shadow); width: 400px; /* 设置初始宽度 */ text-align: left; /* 左对齐 */ font-size: 100%; font-family: var(--b3-font-family); color: var(--b3-theme-on-background); background-color: var(--b3-theme-surface); } .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: var(--b3-mask-background); /*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: 4px 8px; line-height: 20px; margin-bottom: 10px; color: var(--b3-theme-on-background); transition: box-shadow 120ms 0ms cubic-bezier(0, 0, 0.2, 1); background-color: var(--b3-theme-background); border: 0; border-radius: var(--b3-border-radius); box-shadow: inset 0 0 0 .6px var(--b3-theme-on-surface); } .prompt-dialog .prompt-input-container input[type="text"]:hover{ box-shadow: inset 0 0 0 .6px var(--b3-theme-on-background); } .prompt-dialog .prompt-input-container input[type="text"]:focus{ box-shadow: inset 0 0 0 1px var(--b3-theme-primary), 0 0 0 3px var(--b3-theme-primary-lightest); } .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; line-height: 20px; padding: 4px 12px; color: var(--b3-theme-primary); box-shadow: inset 0 0 0 .6px var(--b3-theme-primary); background-color: rgba(0, 0, 0, 0); transition: box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1); border: 0; border-radius: var(--b3-border-radius); } .prompt-dialog button:focus, .prompt-dialog button:hover { background-color: var(--b3-theme-primary-lightest); box-shadow: inset 0 0 0 1px var(--b3-theme-primary); text-decoration: none; } @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) => { // 初始化窗口 dialog.style.width = (width || 400) + 'px'; 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) => { // 初始化窗口 dialog.style.width = (width || 400) + 'px'; 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); }); } }); } // 输出到全局变量 window.showPrompt = showPrompt; window.showPromptForm = showPromptForm; })();

注意事项

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

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    25503 引用 • 105457 回帖
  • 教程
    144 引用 • 626 回帖 • 8 关注
  • 代码
    469 引用 • 589 回帖 • 9 关注
  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    730 引用 • 1280 回帖
3 操作
wilsons 在 2024-08-16 13:36:41 更新了该帖
wilsons 在 2024-08-16 09:41:46 更新了该帖
wilsons 在 2024-08-15 18:58:57 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • vincents

    感谢大佬分享 👍 ,下面这样也可以,要安装 open-api 插件,不过简单调用还是你这个方便些。

    const dialog = new openAPI.siyuan.Dialog({ title: '这里是标题', content: ` <div class="b3-dialog__content"> <div class="ft__breakword"> <input type="text" id="ArticleTitle" class="b3-text-field fn__block" style="height: 100%;margin-bottom: 20px;" placeholder='输入标题'> <textarea id="ArticleContent" class="b3-text-field fn__block" style="height: 100%;" placeholder='输入内容'></textarea> </div> <div class="b3-dialog__action" style="padding-right: 0;padding-top: 12px;"> <button class="b3-button b3-button--cancel" id="CancelBtn">取消</button><div class="fn__space"></div> <button class="b3-button b3-button--text" id="ConfirmBtn">确定</button> </div> </div> `, width: "500px", height: "220px" }); dialog.element.querySelector("#ArticleTitle").focus(); dialog.element.querySelector("#CancelBtn").addEventListener("click", () => { console.log('cancel'); dialog.destroy(); }); dialog.element.querySelector("#ConfirmBtn").addEventListener("click", () => { console.log('confirm'); console.log(dialog.element.querySelector("#ArticleTitle").value); console.log(dialog.element.querySelector("#ArticleContent").value); dialog.destroy(); });

    dialogdemo.png

    1 回复
  • wilsons

    感谢分享!

    我应该把 prompt api 定义罗列下,这样看起来更清晰

    // 弹出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);
wilsons
正在努力开发 wilsons 工具箱中 🛠️ 目前已正式入驻爱发电啦!💖 想催更、提需求?欢迎访问 👉 https://afdian.com/a/wilsons

推荐标签 标签

  • 新人

    让我们欢迎这对新人。哦,不好意思说错了,让我们欢迎这位新人!
    新手上路,请谨慎驾驶!

    52 引用 • 228 回帖
  • 运维

    互联网运维工作,以服务为中心,以稳定、安全、高效为三个基本点,确保公司的互联网业务能够 7×24 小时为用户提供高质量的服务。

    150 引用 • 257 回帖
  • HHKB

    HHKB 是富士通的 Happy Hacking 系列电容键盘。电容键盘即无接点静电电容式键盘(Capacitive Keyboard)。

    5 引用 • 74 回帖 • 505 关注
  • OneDrive
    2 引用
  • Google

    Google(Google Inc.,NASDAQ:GOOG)是一家美国上市公司(公有股份公司),于 1998 年 9 月 7 日以私有股份公司的形式创立,设计并管理一个互联网搜索引擎。Google 公司的总部称作“Googleplex”,它位于加利福尼亚山景城。Google 目前被公认为是全球规模最大的搜索引擎,它提供了简单易用的免费服务。不作恶(Don't be evil)是谷歌公司的一项非正式的公司口号。

    49 引用 • 192 回帖 • 2 关注
  • 浅吟主题

    Jeffrey Chen 制作的思源笔记主题,项目仓库:https://github.com/TCOTC/Whisper

    1 引用 • 28 回帖 • 1 关注
  • PWL

    组织简介

    用爱发电 (Programming With Love) 是一个以开源精神为核心的民间开源爱好者技术组织,“用爱发电”象征开源与贡献精神,加入组织,代表你将遵守组织的“个人开源爱好者”的各项条款。申请加入:用爱发电组织邀请帖
    用爱发电组织官网:https://programmingwithlove.stackoverflow.wiki/

    用爱发电组织的核心驱动力:

    • 遵守开源守则,体现开源&贡献精神:以分享为目的,拒绝非法牟利。
    • 自我保护:使用适当的 License 保护自己的原创作品。
    • 尊重他人:不以各种理由、各种漏洞进行未经允许的抄袭、散播、洩露;以礼相待,尊重所有对社区做出贡献的开发者;通过他人的分享习得知识,要留下足迹,表示感谢。
    • 热爱编程、热爱学习:加入组织,热爱编程是首当其要的。我们欢迎热爱讨论、分享、提问的朋友,也同样欢迎默默成就的朋友。
    • 倾听:正确并恳切对待、处理问题与建议,及时修复开源项目的 Bug ,及时与反馈者沟通。不抬杠、不无视、不辱骂。
    • 平视:不诋毁、轻视、嘲讽其他开发者,主动提出建议、施以帮助,以和谐为本。只要他人肯努力,你也可能会被昔日小看的人所超越,所以请保持谦虚。
    • 乐观且活跃:你的努力决定了你的高度。不要放弃,多年后回头俯瞰,才会发现自己已经成就往日所仰望的水平。积极地将项目开源,帮助他人学习、改进,自己也会获得相应的提升、成就与成就感。
    1 引用 • 487 回帖 • 5 关注
  • SVN

    SVN 是 Subversion 的简称,是一个开放源代码的版本控制系统,相较于 RCS、CVS,它采用了分支管理系统,它的设计目标就是取代 CVS。

    29 引用 • 98 回帖 • 688 关注
  • DevOps

    DevOps(Development 和 Operations 的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。

    58 引用 • 25 回帖 • 3 关注
  • 安装

    你若安好,便是晴天。

    132 引用 • 1184 回帖 • 3 关注
  • Quicker

    Quicker 您的指尖工具箱!操作更少,收获更多!

    37 引用 • 157 回帖 • 1 关注
  • Sandbox

    如果帖子标签含有 Sandbox ,则该帖子会被视为“测试帖”,主要用于测试社区功能,排查 bug 等,该标签下内容不定期进行清理。

    434 引用 • 1250 回帖 • 594 关注
  • DNSPod

    DNSPod 建立于 2006 年 3 月份,是一款免费智能 DNS 产品。 DNSPod 可以为同时有电信、网通、教育网服务器的网站提供智能的解析,让电信用户访问电信的服务器,网通的用户访问网通的服务器,教育网的用户访问教育网的服务器,达到互联互通的效果。

    6 引用 • 26 回帖 • 530 关注
  • jQuery

    jQuery 是一套跨浏览器的 JavaScript 库,强化 HTML 与 JavaScript 之间的操作。由 John Resig 在 2006 年 1 月的 BarCamp NYC 上释出第一个版本。全球约有 28% 的网站使用 jQuery,是非常受欢迎的 JavaScript 库。

    63 引用 • 134 回帖 • 733 关注
  • Ruby

    Ruby 是一种开源的面向对象程序设计的服务器端脚本语言,在 20 世纪 90 年代中期由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)设计并开发。在 Ruby 社区,松本也被称为马茨(Matz)。

    7 引用 • 31 回帖 • 248 关注
  • Latke

    Latke 是一款以 JSON 为主的 Java Web 框架。

    71 引用 • 535 回帖 • 833 关注
  • Mac

    Mac 是苹果公司自 1984 年起以“Macintosh”开始开发的个人消费型计算机,如:iMac、Mac mini、Macbook Air、Macbook Pro、Macbook、Mac Pro 等计算机。

    168 引用 • 597 回帖
  • RIP

    愿逝者安息!

    8 引用 • 92 回帖 • 399 关注
  • Elasticsearch

    Elasticsearch 是一个基于 Lucene 的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于 RESTful 接口。Elasticsearch 是用 Java 开发的,并作为 Apache 许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

    117 引用 • 99 回帖 • 200 关注
  • Bootstrap

    Bootstrap 是 Twitter 推出的一个用于前端开发的开源工具包。它由 Twitter 的设计师 Mark Otto 和 Jacob Thornton 合作开发,是一个 CSS / HTML 框架。

    18 引用 • 33 回帖 • 649 关注
  • 心情

    心是产生任何想法的源泉,心本体会陷入到对自己本体不能理解的状态中,因为心能产生任何想法,不能分出对错,不能分出自己。

    59 引用 • 369 回帖 • 1 关注
  • SEO

    发布对别人有帮助的原创内容是最好的 SEO 方式。

    35 引用 • 200 回帖 • 30 关注
  • IDEA

    IDEA 全称 IntelliJ IDEA,是一款 Java 语言开发的集成环境,在业界被公认为最好的 Java 开发工具之一。IDEA 是 JetBrains 公司的产品,这家公司总部位于捷克共和国的首都布拉格,开发人员以严谨著称的东欧程序员为主。

    181 引用 • 400 回帖
  • wolai

    我来 wolai:不仅仅是未来的云端笔记!

    2 引用 • 14 回帖 • 6 关注
  • Wide

    Wide 是一款基于 Web 的 Go 语言 IDE。通过浏览器就可以进行 Go 开发,并有代码自动完成、查看表达式、编译反馈、Lint、实时结果输出等功能。

    欢迎访问我们运维的实例: https://wide.b3log.org

    30 引用 • 218 回帖 • 636 关注
  • Node.js

    Node.js 是一个基于 Chrome JavaScript 运行时建立的平台, 用于方便地搭建响应速度快、易于扩展的网络应用。Node.js 使用事件驱动, 非阻塞 I/O 模型而得以轻量和高效。

    139 引用 • 269 回帖 • 1 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3201 引用 • 8216 回帖