原生 需手动实现焦点管理:showModal() 后用 requestAnimationFrame 延迟聚焦首个可聚焦元素(跳过 tabindex="-1"),Tab 循环应监听 focusin 并校验可聚焦项,关闭时通过 dataset 存储并验证触发源再回焦。

原生 元素不能自动维持焦点循环,必须手动补全初始聚焦、Tab 折返和关闭回焦三步,否则键盘用户一按 Tab 就逃逸到地址栏。
dialog.showModal() 后为什么焦点没进弹窗
因为 监听 不是没存触发源,而是存的方式让 把任意文章或文档内容转换成精美的全屏翻页 HTML 演示文件(幻灯片风格)。当用户发来一篇文章、文档链接、Markdown 内容、或粘贴的文本,并且希望生成演示用的 HTML 时触发。支持任意内容来源:iWiki 链接、KM 链接、本地文件路径、直接粘贴的文本。生成的 HTML 文件保存到本地工作区,并通过企微直接发送给用户。触发词示例:帮我把这篇文章做成PPT、转成演示文档、生成HTML幻灯片、做个演示、转成可以演示的格式、article to deck、make a slide。 真正难的不是写循环逻辑,而是每次 DOM 更新后重新校验可聚焦列表;异步内容、条件渲染、CSS 隐藏状态都会让预收集的结果失效,必须配合 showModal() 只在打开瞬间尝试聚焦第一个可聚焦子元素,不保证该元素存在或已渲染。常见失败场景包括:弹窗内容由 JS 异步插入、首个子元素是 display: none 隐藏。
showModal() 后用 requestAnimationFrame() 延迟执行聚焦,确保 DOM 已就绪button、input、select、textarea 或 [tabindex="0"],跳过 [tabindex="-1"]
dialog 自身并设 tabindex="-1",再调用 .focus()
如何实现安全的 Tab 循环而不破坏屏幕阅读器
keydown 拦 Tab 是错的——NVDA 的 Insert+F7、VoiceOver 的方向键都会失效。正确做法是监听 focusin,只在焦点即将离开容器时干预。
Array.from(dialog.querySelectorAll('button:not([disabled]), input:not([disabled]), [tabindex="0"]'))
el.offsetParent !== null && getComputedStyle(el).visibility !== 'hidden'
focusin 回调中判断:if (event.target === last && !event.shiftKey) { first.focus(); event.preventDefault(); }
dialog.close() 前移除该监听器,避免多个弹窗叠加时互相干扰关闭 dialog 时焦点回不到触发按钮的根源
.focus() 静默失败:闭包里直接引用的 DOM 节点可能已被移除,或新路由下节点已不存在。
dialog.dataset.triggerId = triggerBtn.id
const el = document.getElementById(dialog.dataset.triggerId); if (el && el.offsetParent) el.focus();
document.activeElement 获取触发源——它在 close 瞬间常是 body
dialog:open 事件或 MutationObserver 主动刷新。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










