我正在为我们基于Web的产品的内部用户编写Google Chrome扩展程序,他们可以在其中管理功能标记。它是一个简单的UI,其中包括需要接受的通知,文本区域,人们可以在其中输入自己的新功能标志,以及带有删除按钮和复选框的这些标志的列表。然后,只要您访问该产品,该扩展程序就会自动将标志添加到查询参数中,以便将其启用。
这很好用!至少在大多数时候,..我还不能弄清楚为什么,但是在某些情况下,用户单击图标,弹出窗口打开,并且弹出窗口的html无法正确响应。单击切换按钮不会取消选中该复选框,单击删除按钮不会删除功能标志,并且在开始键入后,聚焦于文本框不会显示光标或任何文本。但是,如果您进行更改并关闭并重新打开弹出窗口,则更改确实会保存。
任何帮助将不胜感激。
下面是代码。无关的部分已替换为SNIP
。
manifest.json
{
"name": "SNIP",
"version": "1.0",
"description": "SNIP",
"browser_action": {
"default_title": "SNIP",
"default_icon": "SNIP.png",
"default_popup": "popup.html"
},
"background": {
"scripts": ["background.js"],
"persistent": false
},
"manifest_version": 2,
"permissions": [
"storage",
"webNavigation",
"tabs"
]
}
popup.html
<html>
<head>
<style>
SNIP
</style>
</head>
<body>
<h3>Important notice:</h3>
<ol>
SNIP (just some li's with text in here)
</ol>
<button id="accept-notice">Accept</button>
<div id="feature-flag-container" class="hidden">
<h3>Feature flags:</h3>
<div id="feature-flag-list"></div>
<input id="new-feature-flag" type="text" />
</div>
<script type="text/javascript" src="popup.js"></script>
</body>
</html>
popup.js
const acceptNoticeButton = document.getElementById("accept-notice");
const featureFlagContainer = document.getElementById("feature-flag-container");
const featureFlagList = document.getElementById("feature-flag-list");
const newFeatureFlagInput = document.getElementById("new-feature-flag");
let existingFlags = [];
chrome.runtime.sendMessage({ msg: "open_popup" });
chrome.runtime.onMessage.addListener(request => {
Object.keys(request.featureFlags).forEach(flag => {
addFeatureFlagNode(flag, !!request.featureFlags[flag]);
});
if (request.acceptedNotice) {
acceptNoticeButton.click();
}
});
acceptNoticeButton.onclick = () => {
chrome.runtime.sendMessage({ msg: "accept_notice" });
acceptNoticeButton.classList.add("hidden");
featureFlagContainer.classList.remove("hidden");
}
function removeFeatureFlag(e) {
const parent = e.target.parentNode;
const flag = e.target.parentNode.children[1].innerText;
existingFlags = existingFlags.filter(f => f !== flag);
parent.remove();
chrome.runtime.sendMessage({ msg: "remove_flag", flag });
}
function toggleFeatureFlag(e) {
const flag = e.target.parentNode.children[1].innerText;
const value = e.target.checked === true;
chrome.runtime.sendMessage({ msg: "toggle_flag", flag, value });
}
function addFeatureFlagNode(flag, checked) {
existingFlags.push(flag);
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.checked = checked === false ? false : true;
checkbox.onchange = toggleFeatureFlag;
const text = document.createElement("p");
text.innerText = flag;
const del = document.createElement("button");
del.innerText = "Delete";
del.onclick = removeFeatureFlag;
const container = document.createElement("div");
container.appendChild(checkbox);
container.appendChild(text);
container.appendChild(del);
featureFlagList.appendChild(container);
}
newFeatureFlagInput.onkeydown = e => {
const flag = e.target.value;
if (e.key === "Enter" && flag && !existingFlags.includes(flag)) {
chrome.runtime.sendMessage({ msg: "add_flag", flag });
addFeatureFlagNode(flag);
e.target.value = "";
}
}
background.js
const urls = [SNIP];
const environments = [SNIP];
const worksOn = [SNIP];
const options = [];
urls.forEach(url => {
environments.forEach(environment => {
worksOn.forEach(link => {
options.push(`https://${url}${environment}.SNIP.com/${link}/`);
});
});
});
const state = { acceptedNotice: false, featureFlags: {} };
chrome.storage.sync.get(["acceptedNotice", "featureFlags"], result => {
state.acceptedNotice = result.acceptedNotice;
state.featureFlags = result.featureFlags || {};
});
chrome.runtime.onMessage.addListener(({ msg, flag, value }) => {
if (msg === "open_popup") {
chrome.runtime.sendMessage(state);
return
}
if (msg === "accept_notice") {
state.acceptedNotice = true;
} else if (msg === "add_flag") {
state.featureFlags[flag] = true;
} else if (msg === "remove_flag") {
delete state.featureFlags[flag];
} else if (msg === "toggle_flag") {
state.featureFlags[flag] = value;
}
chrome.storage.sync.set(state);
});
chrome.webNavigation.onBeforeNavigate.addListener(event => {
if (options.find(option => event.url.indexOf(option) === 0)) {
const flags = Object.keys(state.featureFlags).filter(flag => state.featureFlags[flag] === true)
const url = event.url.split("?");
const queryParams = (url[1] || "").split("&").filter(param => !!param).map(param => {
const split = param.split("=");
return { key: split[0], value: split[1] };
});
const initialLength = queryParams.length;
flags.forEach(flag => {
if (!queryParams.find(param => param.key === flag)) {
queryParams.push({ key: flag });
}
});
if (queryParams.length !== initialLength) {
chrome.tabs.update(event.tabId, {
url: `${url[0]}?${queryParams.map(param =>
param.value ? `${param.key}=${param.value}` : param.key
).join("&")}`
});
}
}
});