必须用 shown.bs.modal 事件 + 原生 dom 元素的 focus(),且模态框根节点不能带 tabindex="-1";ios 上 focus() 必须放在用户点击后的同步调用链中,否则键盘不弹。
直接结论:必须用 shown.bs.modal 事件 + 原生 dom 元素的 focus(),且模态框根节点不能带 tabindex="-1";ios 上还得把 focus() 放在用户点击后的同步调用链里,否则键盘不弹。
为什么 show.bs.modal 里调 focus() 失败
这个事件触发时模态框 DOM 已插入但尚未渲染完成、不可见、不可交互。此时调 focus(),浏览器会静默忽略——尤其 iOS Safari 直接无视。常见现象是控制台无报错、输入框没亮、光标不出现、键盘不弹出。
-
show.bs.modal是“即将显示”阶段,DOM 还在 transition 中,focus()没有生效条件 - 即使你用了
$('#input')[0].focus(),时机不对照样白搭 - 部分老版本 jQuery(如 1.x)对
.focus()的封装不触发原生焦点行为,必须显式取[0]
为什么 shown.bs.modal 也常失效
根本卡点不在事件名,而在两个隐藏约束:
- Bootstrap 默认给
.modal容器加了tabindex="-1"—— 这会让它“可聚焦但阻断子元素获得焦点”,必须手动移除或覆盖 - jQuery 对象不是 DOM 元素:
$('#myInput').focus()调的是 jQuery 方法,不等价于原生focus();得用$('#myInput')[0].focus()或document.getElementById('myInput').focus() - 输入框本身若被
disabled、readonly、或 CSS 设置了visibility: hidden/display: none,也会拒绝聚焦
iOS Safari 的硬性限制怎么绕过
iOS 要求焦点必须由用户手势(click/tap)直接、同步触发,任何异步回调(包括 shown.bs.modal)都被视为“非用户驱动”,键盘死活不弹。
- 唯一可靠做法:在按钮 click 回调里先
.modal('show'),紧接着立刻调focus()(确保此时输入框 DOM 已存在且可见) - 示例:
$('#openBtn').click(function() { $('#myModal').modal('show'); // 紧跟其后,不等事件,不加 setTimeout const input = document.getElementById('searchInput'); if (input) input.focus(); }); - 如果模态框是动态生成(比如 ajax 加载内容),需确认
focus()执行前输入框已挂载到 DOM,否则getElementById返回 null
实际部署时容易漏掉的细节
多数人写完代码以为搞定,结果上线后安卓正常、iOS 不行,或者首次打开有效、二次打开失效——问题往往藏在这些地方:
- 模态框 HTML 里写了
tabindex="-1"却没删:检查<div class="modal"> 标签是否含该属性,删掉或设为 <code>tabindex="0" - 输入框被 Bootstrap 的
.d-none或.invisible类临时隐藏过,focus()时还没恢复可见状态 - 多个模态框共用同一个 ID,
getElementById取到的是第一个而非当前弹出的那个 - 用了第三方 modal 插件(如 bootstrap-modalmanager),其事件名和生命周期与原生不同,不能照搬
shown.bs.modal
真正起作用的从来不是“加个事件监听”,而是确保焦点调用发生在正确的上下文里:DOM 就位、无障碍属性清空、用户交互链未中断。iOS 上那句“必须同步”不是建议,是铁律。











