记录chrome插件开发过程
chrome插件功能描述
功能:当用户在打开某个网站,当鼠标聚焦某个输入框时,此时在输入框旁注入用户配置好数据的下拉框,方便用户选择
插件目录结构
- images/
- background.js
- content.js
- manifest.json
- popup.html
- popup.js
各部分功能代码
-
images存放该插件的图标
-
manifest.json
用来描述该插件的一些清单
{
"name": "测试小工具",
"version": "1.0",
"description": "文本输入框聚焦时自动注入下拉列表",
"permissions": ["activeTab", "declarativeContent", "storage"],
"background": {
"service_worker": "background.js"
},
"action": {
"default_icon": "images/icon.png",
"default_popup": "popup.html"
},
"content_scripts": [
{
"matches": ["http://127.0.0.1:5500/*"],
"js": ["content.js"]
}
],
"manifest_version": 3
}
- popup.html
当点击插件图标时,会弹出一个页面,用户配置下拉框的选项
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>测试小工具配置</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<style type="text/css">
* {
margin: 0;
padding: 0;
}
body {
width: 300px;
padding: 20px;
font-size: 14px;
color: #666;
}
.icon {
color: #666;
cursor: pointer;
}
.icon:hover {
color: #fa8919;
cursor: pointer;
}
ul {
list-style: none;
}
input[type="text"] {
border: 1px solid #ccc;
border-radius: 5px;
padding: 8px;
width: 200px;
}
input[type="text"]:hover {
border-color: #fa8919;
/* 悬停时修改边框颜色 */
}
/* 去除输入框获得焦点时的默认样式 */
input[type="text"]:focus {
outline: none;
/* 去除默认的轮廓线 */
border-color: #fa8919;
}
li {
display: flex;
padding: 6px 0;
}
.text {
flex: 1;
}
.title {
color: #333;
font-weight: 400;
font-size: 18px;
padding-bottom: 20px;
}
.list {
min-height: 100px;
}
.form {
margin-top: 20px;
}
label {
padding-right: 20px;
font-size: 14px;
color: #666;
}
</style>
</head>
<body>
<h1 class="title">下拉框配置列表</h1>
<ul id="optionsList" class="list"></ul>
<form id="addOptionForm" class="form">
<label for="newOption">关键词:</label>
<input type="text" id="newOption" autocomplete="off">
<span class="icon icon-delete-fill"></span>
</form>
<script src="popup.js"></script>
</body>
</html>
- popup.js
主要用于处理popup一些操作处理
const createElement = (tag, text, className) => {
const element = document.createElement(`${tag}`);
if (text) {
element.textContent = text;
}
if (className) {
element.className = className;
}
return element;
};
const renderOptions = (options) => {
const html = options
.map((option) => {
return `
<li>
<div class="text">${option}</div>
<i class="icon fas fa-trash-alt delete-button"></i>
</li>
`;
})
.join("");
optionsList.innerHTML = html;
};
const newOptionInput = document.getElementById("newOption");
// 从存储中加载已有选项并渲染整个列表
chrome.storage.sync.get("dropdownOptions", function (data) {
const options = data.dropdownOptions || [];
renderOptions(options);
});
document.addEventListener("DOMContentLoaded", function () {
const optionsList = document.getElementById("optionsList");
const addOptionForm = document.getElementById("addOptionForm");
// 从存储中加载已有选项并渲染整个列表
chrome.storage.sync.get("dropdownOptions", function (data) {
const options = data.dropdownOptions || [];
const html = options
.map((option) => {
return `
<li>
<div class="text">${option}</div>
<i class="icon fas fa-trash-alt delete-button"></i>
</li>
`;
})
.join("");
optionsList.innerHTML = html;
});
// 添加新选项
addOptionForm.addEventListener("submit", function (event) {
event.preventDefault();
const newOption = newOptionInput.value.trim();
if (newOption) {
// 创建新节点
const li = createElement("li");
const textDom = createElement("div", newOption, "text");
const deleteButton = createElement(
"i",
undefined,
"fas fa-trash-alt delete-button"
);
deleteButton.addEventListener("click", function () {
li.remove();
chrome.storage.sync.get("dropdownOptions", function (data) {
const options = data.dropdownOptions || [];
const index = options.indexOf(newOption);
if (index !== -1) {
options.splice(index, 1);
chrome.storage.sync.set({ dropdownOptions: options });
}
});
});
li.appendChild(textDom);
li.appendChild(deleteButton);
optionsList.appendChild(li);
newOptionInput.value = "";
chrome.storage.sync.get("dropdownOptions", function (data) {
const options = data.dropdownOptions || [];
options.push(newOption);
chrome.storage.sync.set({ dropdownOptions: options });
});
}
});
});
// 添加新选项
addOptionForm.addEventListener("submit", function (event) {
event.preventDefault();
const newOption = newOptionInput.value.trim();
if (newOption) {
chrome.storage.sync.get("dropdownOptions", function (data) {
const options = data.dropdownOptions || [];
options.push(newOption);
chrome.storage.sync.set({ dropdownOptions: options }, function () {
renderOptions(options);
});
});
newOptionInput.value = "";
}
});
// 删除选项
optionsList.addEventListener("click", function (event) {
if (event.target.classList.contains("delete-button")) {
const optionText = event.target.parentElement.textContent.trim();
chrome.storage.sync.get("dropdownOptions", function (data) {
const options = data.dropdownOptions || [];
const index = options.indexOf(optionText);
if (index !== -1) {
options.splice(index, 1);
chrome.storage.sync.set({ dropdownOptions: options }, function () {
renderOptions(options);
chrome.runtime.sendMessage({ action: 'updateOptions'});
});
}
});
}
});
- content.js
处理网站输入框聚焦一些处理
let configs = [];
let dropdownTimeout;
// 向 background script 发送消息请求选项信息
const sendOptionsConfigMessage = () => {
chrome.runtime.sendMessage({ action: "getOptions" });
};
const removeDropdownTimeout = (dropdownDom) => {
dropdownTimeout = setTimeout(function () {
dropdownDom.remove();
}, 1000 * 10);
};
// 创建并注入下拉框
function createDropdown(inputField) {
const dropdownDom = document.createElement("select");
dropdownDom.classList.add("custom-dropdown");
const defaultHtml = `<option value="">请选择</option>`;
const html = (configs || [])
.map((option) => {
return `
<option value="${option}">${option}</option>
`;
})
.join("");
dropdownDom.innerHTML = `${defaultHtml} ${html}`;
// 当用户选择某一项时,填充信息到输入框中
dropdownDom.addEventListener("change", function (event) {
inputField.value = event.target.value;
inputField.defaultValue = event.target.value;
inputField.dispatchEvent(new Event('change', { bubbles: true }));
});
// 注入下拉框到输入框下方
inputField.insertAdjacentElement("afterend", dropdownDom);
// 创建样式元素
const styleElement = document.createElement("style");
// 添加样式内容
styleElement.textContent = `
.custom-dropdown {
position: absolute;
left: 0;
top: 32px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 14px;
color: #333;
height: 30px;
width: 200px;
z-index: 2000;
}
`;
// 将样式元素插入到文档头部
document.head.appendChild(styleElement);
// 监听用户选择值的操作
dropdownDom.addEventListener("mousedown", function (event) {
// 清除之前的延迟移除计时器
clearTimeout(dropdownTimeout);
removeDropdownTimeout(dropdownDom);
});
dropdownDom.addEventListener("click", function (event) {
// 清除之前的延迟移除计时器
clearTimeout(dropdownTimeout);
removeDropdownTimeout(dropdownDom);
});
// 设置延迟移除下拉框的计时器
inputField.addEventListener("focusout", function (event) {
// 延迟 1000 毫秒移除下拉框
removeDropdownTimeout(dropdownDom);
});
}
sendOptionsConfigMessage();
document.addEventListener("focusin", function (event) {
const inputField = event.target;
// 检查聚焦的元素是否是文本框
if (
inputField.tagName.toLowerCase() === "input" &&
inputField.type === "text"
) {
// 检查是否已经存在下拉框
if (
!inputField.nextElementSibling ||
inputField.nextElementSibling.tagName.toLowerCase() !== "select"
) {
createDropdown(inputField);
}
}
});
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
console.log("message", message, sender, sendResponse);
if (message.action === "sendOptions") {
// 从 Chrome 存储中获取选项信息
if (message.options) {
const options = message.options;
configs = options;
}
}
});
- background.js
监听popup页面数据变化,同步到content.js
// 监听来自 content script 的消息
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
if (message.action === 'getOptions' && sender.tab) {
// 从 Chrome 存储中获取选项信息
chrome.storage.sync.get('dropdownOptions', function(data) {
const options = data.dropdownOptions || [];
// 将选项信息发送给 content script
chrome.tabs.sendMessage(sender.tab.id, { action: 'sendOptions', options: options });
});
}
});
// 监听 popup 数据更新,并同步更新到 content
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
if (message.action === 'updateOptions' && sender.tab) {
// 从 Chrome 存储中获取选项信息
chrome.storage.sync.get('dropdownOptions', function(data) {
const options = data.dropdownOptions || [];
// 将选项信息发送给 content script
chrome.tabs.sendMessage(sender.tab.id, { action: 'sendOptions', options: options });
});
}
});
开发者模式使用插件
- 1.将该插件代码同步到本地目录
- 2.打开chrome扩展管理页面,点击左上方按钮 "加载已解压的扩展程序",选择该代码目录代码