如何在Chrome扩展中临时禁用Bootstrap模态框的enforceFocus属性
解决方案:解决Chrome扩展弹窗焦点被Bootstrap模态框抢占及Shadow DOM样式丢失问题
一、临时禁用Bootstrap模态框的enforceFocus
可以通过Chrome扩展的内容脚本实现临时修改,核心思路是在扩展弹窗打开时记录目标网站模态框的原始配置,关闭时恢复。
代码实现
// 保存原始enforceFocus状态的数组 let originalEnforceFocusStates = []; // 打开扩展弹窗时执行 function openExtensionModal() { // 定位当前页面所有处于激活状态的Bootstrap模态框 const activeModals = document.querySelectorAll('.modal.in, .modal.show'); activeModals.forEach(modalEl => { // 获取模态框实例(兼容Bootstrap 3/4/5) const modalInstance = window.$?.fn.modal.getInstance ? window.$.fn.modal.getInstance(modalEl) : modalEl.modal; if (modalInstance) { // 保存原始配置值 const originalValue = modalInstance.options?.enforceFocus ?? true; originalEnforceFocusStates.push({ instance: modalInstance, originalValue }); // 临时禁用enforceFocus if (modalInstance.options) { modalInstance.options.enforceFocus = false; } // 针对Bootstrap 3的特殊处理:若实例未自定义enforceFocus,需手动添加属性覆盖全局默认 if (window.$?.fn.modal.Constructor && !modalInstance.options.hasOwnProperty('enforceFocus')) { modalInstance.options.enforceFocus = false; } } }); // 此处添加你的扩展弹窗DOM注入逻辑... } // 关闭扩展弹窗时执行 function closeExtensionModal() { // 恢复每个模态框的原始配置 originalEnforceFocusStates.forEach(({ instance, originalValue }) => { if (instance.options) { instance.options.enforceFocus = originalValue; // Bootstrap 3场景:若之前是临时添加的属性,删除后恢复全局默认 if (window.$?.fn.modal.Constructor && !instance.options.hasOwnProperty('enforceFocus')) { delete instance.options.enforceFocus; } } }); originalEnforceFocusStates = []; // 此处添加你的扩展弹窗DOM移除逻辑... }
二、使用Shadow DOM解决焦点问题并保留样式
Shadow DOM的样式隔离特性可以彻底规避外部Bootstrap的焦点抢占,同时需要将弹窗样式注入到Shadow根内部以避免丢失。
代码实现
function createExtensionModal() { // 创建弹窗容器 const modalContainer = document.createElement('div'); // 附加Shadow根(open模式方便调试,close模式更彻底隔离) const shadowRoot = modalContainer.attachShadow({ mode: 'open' }); // 弹窗HTML结构 const modalHTML = ` <div class="extension-modal"> <div class="modal-inner"> <textarea placeholder="输入内容..."></textarea> <button id="modal-close">关闭</button> </div> </div> `; // 弹窗内联样式(也可从扩展内部CSS文件读取内容) const modalCSS = ` .extension-modal { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background-color: rgba(0, 0, 0, 0.6); display: flex; align-items: center; justify-content: center; z-index: 999999; } .modal-inner { background: #fff; padding: 24px; border-radius: 8px; width: 420px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); } textarea { width: 100%; height: 120px; padding: 10px; box-sizing: border-box; border: 1px solid #ddd; border-radius: 4px; margin-bottom: 16px; } #modal-close { padding: 8px 16px; background-color: #007bff; color: #fff; border: none; border-radius: 4px; cursor: pointer; } #modal-close:hover { background-color: #0056b3; } `; // 注入样式到Shadow根 const styleEl = document.createElement('style'); styleEl.textContent = modalCSS; shadowRoot.appendChild(styleEl); // 注入弹窗HTML到Shadow根 shadowRoot.innerHTML += modalHTML; // 添加关闭事件 const closeBtn = shadowRoot.getElementById('modal-close'); closeBtn.addEventListener('click', () => { document.body.removeChild(modalContainer); }); // 将容器添加到页面 document.body.appendChild(modalContainer); // 自动聚焦textarea const textarea = shadowRoot.querySelector('textarea'); textarea.focus(); }
补充:加载外部CSS到Shadow根
如果你的弹窗样式写在单独的CSS文件中,可以通过fetch读取内容后注入:
async function loadExtensionCSS(shadowRoot, cssPath) { const cssUrl = chrome.runtime.getURL(cssPath); const response = await fetch(cssUrl); const cssText = await response.text(); const styleEl = document.createElement('style'); styleEl.textContent = cssText; shadowRoot.appendChild(styleEl); } // 使用示例: // loadExtensionCSS(shadowRoot, 'styles/modal.css');
内容的提问来源于stack exchange,提问作者Samridhi Kumari
相关产品推荐
相关产品推荐

