Chrome扩展内容脚本如何绕过页面原有focusin事件捕获的阻止传播逻辑?
方案1:预拦截页面的事件注册逻辑(优先推荐)
这个方案需要你把扩展的内容脚本运行时机设置为document_start,在页面所有脚本执行之前先覆写document.addEventListener方法,对页面要注册的focusin捕获阶段监听器做一层过滤:
// 先保存原生的事件注册方法 const originalAddEventListener = Document.prototype.addEventListener; Document.prototype.addEventListener = function (type, handler, options) { // 匹配页面注册的focusin捕获阶段监听器 if (type === 'focusin' && (options === true || (typeof options === 'object' && options.capture))) { // 包裹原handler const wrappedHandler = function (event) { // 所有属于你扩展的元素都加上特殊标记属性,比如data-extension-owned if (event.target.closest('[data-extension-owned]')) { // 是你自己的元素触发的事件,直接跳过原页面的拦截逻辑 return; } // 非你的元素的事件,正常执行原逻辑 return handler.apply(this, arguments); }; // 用包裹后的handler注册 return originalAddEventListener.call(this, type, wrappedHandler, options); } // 其他类型的事件正常注册 return originalAddEventListener.call(this, type, handler, options); };
这个方案完全无感,页面原有逻辑不会受到任何影响,只有你的元素的focusin事件不会被拦截。
方案2:更早注册捕获阶段监听器,主动触发你的逻辑
同样需要内容脚本运行时机为document_start,在页面注册它的监听器之前,先在document上注册捕获阶段的focusin监听器:
document.addEventListener('focusin', event => { const targetEl = event.target.closest('[data-extension-owned]'); if (targetEl) { // 直接在这里执行你本来要写在元素focusin监听器里的逻辑 console.log('my logic', targetEl); // 调用stopImmediatePropagation可以阻止后续页面注册的同阶段监听器执行,也就不会触发页面的stopPropagation逻辑 event.stopImmediatePropagation(); } }, true);
这个方案不需要篡改页面的监听器逻辑,只需要自己提前接管事件即可。
方案3:规避事件监听,主动执行逻辑
如果你不方便修改内容脚本的运行时机,可以直接在你调用元素focus()方法之后,同步执行你本来要在focusin监听器里跑的逻辑:
const myInput = document.querySelector('#my_div input'); myInput.focus(); // 直接主动执行逻辑,不需要等事件触发 console.log('my logic');
如果担心焦点被页面脚本抢回去,可以加个setTimeout延迟几十毫秒再执行一次校验,确认焦点在你的元素上。
方案4:使用Shadow DOM隔离
把你扩展注入的所有元素都包裹在Shadow DOM中,默认情况下Shadow DOM内部触发的focusin事件不会被外部的页面监听器直接捕获到目标元素,你可以在Shadow根节点上直接绑定focusin监听器处理内部元素的焦点逻辑。
const container = document.createElement('div'); const shadowRoot = container.attachShadow({ mode: 'open' }); // 把你的元素都append到shadowRoot下 shadowRoot.innerHTML = `<div id="my_div"><input /></div>`; shadowRoot.querySelector('#my_div').addEventListener('focusin', () => { console.log('my logic'); }); document.body.appendChild(container);
内容的提问来源于stack exchange,提问作者Virtuoz
相关产品推荐
相关产品推荐

