在我的Chrome扩展,我有一个拨动开关,本质上是一个复选框。当我打开它并使选中的值为真时,它执行一个特定的功能,当我关闭它时,它执行另一个功能,但问题是,当它关闭时,它的功能仍然工作,当它打开时,我希望能够关闭它。我做错了什么?
popup.js(其中开关被检查为true或false)
const toggle = document.getElementById('switch');
const checkedAttr = document.createAttribute('checked');
toggle.addEventListener('click', () => {
toggle.checked ? toggleChecked() : toggleUnchecked();
});
function toggleChecked() {
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {switchStatus: "on"}, function(response) {
if(response.switchStatus === "on"){
//alert("Success")
}
})
})
}
function toggleUnchecked() {
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {switchStatus: "off"}, function(response) {
if(response.switchStatus === "off"){
//alert("Success")
}
})
})
}
content.js(函数发生的地方)
// Receiving message from popup.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.switchStatus === "on") {
// Getting all text elements
const allText = document.querySelectorAll('h1, h2, h3, h4, h5, p, li, td, caption, span, a');
const colors = ['#ffadad', '#ffd6a5', '#fdffb6', '#caffbf', '#bdb2ff', '#9fdfff'];
const findFontModal = 'findfont.html'
// Loop through all text elements-
// to be able to manipulate each individual one
for (let i = 0; i < allText.length; i++) {
//ON HOVER
allText[i].addEventListener('mouseover', async () => {
allText[i].style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
allText[i].style.cursor = "pointer";
});
allText[i].addEventListener('mouseout', async () => {
allText[i].style.backgroundColor = null;
});
//ON MOUSE CLICK
allText[i].addEventListener('click', async (event) => {
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 = event.pageY+"px";
modal.style.left = event.pageX+"px";
document.body.appendChild(modal);
const dialog = document.querySelector("dialog");
dialog.show();
document.body.addEventListener('click', () => {
document.body.removeChild(modal);
dialog.close();
});
const iframe = document.getElementById("popup-content");
iframe.src = chrome.extension.getURL("findfont.html")
}
// Load in HTML file
fetch(chrome.runtime.getURL(findFontModal))
.then(r => r.text())
.then(html => {
showModal();
});
});
}
sendResponse({status: "done"});
return;
}
// When toggle switch is unchecked
else if (request.switchStatus === "off") {
const allText = document.querySelectorAll('h1, h2, h3, h4, h5, p, li, td, caption, span, a');
for (let i = 0; i < allText.length; i++) {
allText[i].style.backgroundColor = null;
}
sendResponse({status: "done"});
}
}
);
切换状态应该存储在chrome存储中,因此当弹出窗口反复打开/关闭时,复选框状态将持续存在。假设是这种情况,弹出窗口应该按给定的正常工作。
但是,我建议在内容脚本中解决两个单独的问题:- 当(以及每次)收到消息时,当前会添加mouseover/out/click的事件处理程序,但不会在"关闭"时删除。动作,这就是为什么它们出现在&;off&;之后;消息已收到。
解决这个问题的一种方法是将事件侦听器注册与接收消息的时间点分开。我在下面添加了一个示例,其中我使用布尔变量作为知道事件是否应该执行的方法("on"或"关闭"),然后根据收到的消息翻转该变量。事件侦听器只向每个目标元素添加一次,并且永远不会删除,但是当flag位于"off"时,每个处理程序什么也不做。状态(将在下一个代码块中解释最后一部分)。
let toggleIsOn = false;
const elementIsReady = "special-flag-xyz-put-something-unique-here"
// Receiving message from popup.js
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
// if there are other messages, review this
toggleIsOn = request.switchStatus === "on";
}
);
function registerEventHandlers() {
const allText = document.querySelectorAll('h1, h2, h3, h4, h5, p, li, td, caption, span, a');
for (let i = 0; i < allText.length; i++) {
const element = allText[i];
// mark each element with attribute to know the event
// actions have been applied, to prevent registering
// these handlers multiple times
if (!element.hasAttribute(elementIsReady)) {
element.addEventListener('mouseover', onMouseOver);
element.addEventListener('mouseout', onMouseOut);
element.addEventListener('click', onClick);
element.setAttribute(elementIsReady, true);
}
}
}
注意,在注册事件处理程序时,我还添加了一个特殊属性来指示已添加的处理程序,以防止在同一元素上多次注册相同的处理程序。该值可以是任何足够唯一的值,但不太可能作为DOM节点属性自然出现。
在各自的函数中为每个事件指定事件行为。注意当toggle处于"关闭"状态时状态时,处理程序只是返回,即"什么都不做",并在"on"状态下返回。如果事件行为将执行:
async function onMouseOver(event) {
if (!toggleIsOn) return;
console.log(`mouse over: ${event.target.innerText}`);
}
async function onMouseOut(event) {
if (!toggleIsOn) return;
console.log(`mouse out: ${event.target.innerText}`);
}
async function onClick(event) {
if (!toggleIsOn) return;
console.log(`element click! ${event.target.innerText}`);
// put the open modal logic here, etc.
}
在内容脚本中,当查询页面中所有text节点时,第二个要处理的点是,因为它不再连接到接收消息时的时间点:
- 如果目标是只处理服务器端呈现的页面,那么在页面加载时调用
registerEventHandlers()
就足够了。这种情况不太可能发生。对于动态修改DOM的单页应用程序,事件侦听器在DOM更改时丢失,并且不会添加到页面加载后生成的新节点中,除非在DOM更改时再次调用registerEventHandlers()
。
解决这个问题的方法是使用MutationObserver,它允许观察DOM中的动态变化。下面是一个例子:
// run once when script is loaded to setup the observer
// wrapping this in a "constructor" to illustrate that point
(function constructor() {
const observer = new MutationObserver(registerEventHandlers);
observer.observe(document.body, {subtree: true, childList: true});
}())
使用此策略将允许扩展也在单页应用程序上工作。由于节点被标记为不允许多次重新注册事件侦听器,因此即使在使用突变观察器时也不会发生这种情况:脚本迭代地查找尚未具有侦听器的文本节点,并在发现它们时添加它们一次。