必须同时设置 tabindex="-1"、backdrop: 'static' 和 keyboard: false,且确保模态框仅初始化一次;否则 esc 或点击背景仍可关闭。vue/react 中需在模板硬写 tabindex,禁用后须提供显式关闭入口。

只设 data-keyboard="false" 或只调用 .modal({ keyboard: false }),ESC 依然能关模态框——根本原因不是配置没生效,而是 Bootstrap 的键盘事件逻辑依赖模态框容器是否可聚焦,而聚焦能力由 tabindex 控制。
必须给 .modal 元素加 tabindex="-1"
Bootstrap 内部用 document.activeElement 和 focusable 判断模态框是否处于“可交互状态”。如果 div.modal 没有 tabindex 属性,哪怕你写了 data-keyboard="false",整个键盘事件监听压根不会挂载。
-
tabindex="-1"是最常用值,它让元素可通过 JS 聚焦,但不进入自然 tab 流 - Vue/React 动态渲染时容易漏掉这个属性,建议在模板里硬写:
<div class="modal fade" tabindex="-1" ...> <li>检查方式:打开控制台,选中模态框根节点,看 DOM 上是否存在 <code>tabindex属性 - HTML 中写
data-bs-backdrop="static"+data-bs-keyboard="false"(注意 Bootstrap 5 的bs-前缀) - 但更推荐统一用 JS 初始化:
$('#myModal').modal({ backdrop: 'static', keyboard: false }); - 避免混用:比如 HTML 里写了
data-bs-backdrop="true",JS 又传backdrop: 'static',后者会被忽略 - 控制台报
Modal is already initialized:说明模态框被重复初始化,后一次覆盖前一次配置;确保只调用一次.modal() - 引入的是
bootstrap.min.js而非bootstrap.bundle.min.js:缺少 Popper 和 Modal 插件,keyboard配置不生效 - 多个模态框共用同一 ID 或使用相同类名批量初始化:导致事件绑定错乱,建议每个 modal 用唯一 ID
- 保留默认关闭按钮:
<button type="button" class="btn-close" data-dismiss="modal"></button>(Bootstrap 5) - 自定义关闭行为(如表单校验):用
$('#myModal').modal('hide')主动触发,别等用户找出口 - 移动端无 ESC,更要确认触摸区域(尤其是 header/footer)有可点的关闭控件
backdrop: 'static' 和 keyboard: false 必须同时设置
用户反馈“ESC 还是关了”,大概率是点了背景关闭的——keyboard: false 只管按键,backdrop: 'static' 才管点击遮罩层。二者缺一不可,且不能靠 HTML 属性单独搞定。
为什么加了还失效?排查这三处
常见失效不是代码写错,而是环境或生命周期干扰:
禁用 ESC 后,必须显式提供关闭入口
禁用 ESC 和背景点击后,模态框就真锁死了。如果没留关闭按钮或没绑定关闭逻辑,用户只能刷新页面。
禁用 ESC 不是加个属性就完事——tabindex 是开关,backdrop 和 keyboard 是双保险,初始化时机和关闭出口是兜底。四个点串起来,才算真正可控。











