Chrome扩展中Checkbox值为false时,开启状态对应函数仍执行如何解决
问题根源
- 最核心的问题是开关开启时给所有文本元素绑定了
mouseover、mouseout、click三个事件监听器,但关闭开关时仅清除了元素的背景色,没有移除绑定的事件监听器,这些监听器会一直留在DOM元素上,所以关闭开关后hover、点击文本还是会触发之前的逻辑。 - 另外你当前的代码每次打开开关都会重新给元素绑定一次监听器,反复开关会导致同一个元素绑定多份相同的监听器,额外占用性能。
修复方案
需要修改content.js的逻辑,核心是要把匿名事件监听器抽成具名函数,同时全局存储开关状态和绑定的监听器,方便关闭时统一移除。
第一步:新增全局存储变量
先在content.js最顶部添加以下代码:
// 全局存储开关状态 let isSwitchOn = false; // 存储每个元素绑定的监听器,移除时需要和绑定时的函数引用完全一致 const eventListenersMap = new WeakMap(); // 把不变的常量提出来,避免重复声明 const colors = ['#ffadad', '#ffd6a5', '#fdffb6', '#caffbf', '#bdb2ff', '#9fdfff']; const findFontModal = 'findfont.html'
第二步:抽离事件处理函数
把之前写在循环里的匿名事件处理函数抽成全局具名函数:
// hover高亮处理 function handleMouseOver(e) { e.target.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]; e.target.style.cursor = "pointer"; } // hover取消高亮处理 function handleMouseOut(e) { e.target.style.backgroundColor = null; } // 点击弹出modal处理 async function handleClick(e) { e.stopPropagation(); const showModal = async () => { const modal = document.createElement('dialog'); modal.setAttribute("style",` border: none; border-radius:20px; background-color:#fafafa; position: absolute; z-index:10000000000000; box-shadow: rgba(0, 0, 0, 0.2) 0px 18px 50px -10px; `); modal.innerHTML = `<iframe id="popup-content" scrolling="no" style="height:155px; width:347px; z-index:10000000000000;" frameBorder="0"></iframe>`; modal.style.top = e.pageY+"px"; modal.style.left = e.pageX+"px"; document.body.appendChild(modal); const dialog = document.querySelector("dialog"); dialog.show(); // 修复modal关闭事件重复绑定的问题 function closeModal() { document.body.removeChild(modal); dialog.close(); document.body.removeEventListener('click', closeModal); } document.body.addEventListener('click', closeModal); const iframe = document.getElementById("popup-content"); iframe.src = chrome.runtime.getURL("findfont.html") } fetch(chrome.runtime.getURL(findFontModal)) .then(r => r.text()) .then(html => { showModal(); }); }
第三步:修改开关开启逻辑
修改request.switchStatus === "on"对应的代码块,增加重复开启判断,绑定事件后存储到Map中:
if (request.switchStatus === "on") { // 开关已经开启的情况下直接返回,避免重复绑定 if (isSwitchOn) { sendResponse({status: "done"}); return; } isSwitchOn = true; const allText = document.querySelectorAll('h1, h2, h3, h4, h5, p, li, td, caption, span, a'); for (let i = 0; i < allText.length; i++) { const el = allText[i]; // 绑定监听器 el.addEventListener('mouseover', handleMouseOver); el.addEventListener('mouseout', handleMouseOut); el.addEventListener('click', handleClick); // 存入Map方便后续移除 eventListenersMap.set(el, { mouseover: handleMouseOver, mouseout: handleMouseOut, click: handleClick }); } sendResponse({status: "done"}); return; }
第四步:修改开关关闭逻辑
修改request.switchStatus === "off"对应的代码块,增加移除事件监听器的逻辑:
else if (request.switchStatus === "off") { // 开关已经关闭的情况下直接返回 if (!isSwitchOn) { sendResponse({status: "done"}); return; } isSwitchOn = false; const allText = document.querySelectorAll('h1, h2, h3, h4, h5, p, li, td, caption, span, a'); for (let i = 0; i < allText.length; i++) { const el = allText[i]; el.style.backgroundColor = null; // 移除之前绑定的监听器 const listeners = eventListenersMap.get(el); if (listeners) { el.removeEventListener('mouseover', listeners.mouseover); el.removeEventListener('mouseout', listeners.mouseout); el.removeEventListener('click', listeners.click); eventListenersMap.delete(el); } } sendResponse({status: "done"}); }
额外优化
你popup.js里声明的checkedAttr变量完全没有使用,可以直接删除,没有实际作用。
内容的提问来源于stack exchange,提问作者Hitman DD
相关产品推荐
相关产品推荐

