记录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扩展管理页面,点击左上方按钮 "加载已解压的扩展程序",选择该代码目录代码

打包插件

posted @ 2024-04-01 15:29  晚安喵  阅读(13)  评论(0编辑  收藏  举报