把网站链接放在右键菜单里?那就来试试吧~

1,045 阅读3分钟

这是本人的第二个简单的chrome小插件,第一个插件的开发过程写的比较详细,需要的读者可以看本人的上一篇文章,这里就不重复介绍某些概念性的东西了。

GotoLink

大家通常会把一些常用网站整理在书签里,我也不例外,虽然很便捷,但是总觉得还差那么一点意思(对于喜欢不停按右键的我来说)。因此萌生了一个不如做一个右键菜单版的书签,这是项目地址,最终效果如下图](url)。

image.png

image.png

开始

先创建一个GotoLink的项目文件夹,项目结构如下图所示:

image.png

manifest.json

首先来配置chrome插件的描述文件,相关配置在上一篇文章中都有介绍,此处不再赘述。

{
    "name": "GotoLink",
    "description": "右键快捷跳转网页",
    "version": "0.0.1",
    "manifest_version": 2,
    "icons": {
        "16": "images/logo.png",
        "48": "images/logo.png",
        "128": "images/logo.png"
    },
    "browser_action": {
        "default_icon": "images/logo.png",
        "default_title": "GotoLink",
        "default_popup": "index.html"
    },
    "background": {
        "scripts": [
            "js/background.js"
        ],
        "parsistent": false
    },
    "content_scripts": [
        {
            "matches": [
                "<all_urls>"
            ],
            "js": [
                "js/content-script.js"
            ],
            "run_at": "document_idle"
        }
    ],
    "permissions": [
        "contextMenus",
        "tabs",
        "notifications",
        "webRequest",
        "webRequestBlocking",
        "storage",
        "cookies",
        "activeTab",
        "http://*/*",
        "https://*/*"
    ]
}

index.html

此文件是点击插件图标的弹出页,用来增加删除右键菜单内容的区域。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./css/index.css">
    <script src="./js/index.js"></script>
</head>

<body>
    <div class="container">
        <div class="add-bar">
            <div class="add-area">
                <input class="input-name" type="text" placeholder="请输入网站名称">
                <input class="input-href" type="text" placeholder="请输入网址">
            </div>

            <div class="add-btn">添加</div>
        </div>
        <div class="link-list"></div>
    </div>
</body>

</html>

index.css

页面的样式文件。

.container {
  width: 500px;
}

.add-bar {
  display: flex;
  justify-content: space-between;
}

.add-area {
  width: 80%;
  height: 60px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

input {
  height: 27px;
  border: none;
  box-sizing: border-box;
  outline: none;
  background: #f6f4f6;
  font-size: 14px;
  padding-left: 5px;
}

.add-btn {
  height: 27px;
  width: 15%;
  background: #f4f4f5;
  text-align: center;
  line-height: 27px;
  color: #909399;
  cursor: pointer;
  transition: all linear 0.1s;
  font-size: 15px;
}

.add-btn:hover {
  background: #909399;
  color: #ffffff;
}

.link-list {
  width: 98%;
  height: 250px;
  margin: 15px auto;
  border: 1px solid #dcdcdd;
  overflow: auto;
  box-sizing: border-box;
}

.link-item {
  color: #5b5c5e;
  height: 45px;
  border-bottom: 1px solid #d9d9db;
  font-size: 15px;
  padding-left: 5px;
  display: flex;
}

.link-content {
  width: 80%;
}

.link-name,
.link-href {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.link-href {
  font-size: 14px;
  color: #909399;
}

.del-content {
  width: 20%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.del-btn {
  width: 70%;
  height: 30px;
  line-height: 30px;
  text-align: center;
  background: #fef0f0;
  color: #f56c6c;
  cursor: pointer;
  transition: all linear 0.1s;
}

.del-btn:hover {
  background: #f56c6c;
  color: #ffffff;
}

::-webkit-scrollbar {
  /*滚动条整体样式*/
  width: 10px;
  /*高宽分别对应横竖滚动条的尺寸*/
  height: 10px;
}

::-webkit-scrollbar-thumb {
  /*滚动条里面小方块*/
  border-radius: 5px;
  -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.1);
  background: rgba(0, 0, 0, 0.1);
}

::-webkit-scrollbar-track {
  /*滚动条里面轨道*/
  -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.1);
  border-radius: 0;
  background: rgba(0, 0, 0, 0.1);
}

::-webkit-scrollbar-button {
  display: none;
}

index.js

window.onload = () => {
  const webName = document.querySelector(".input-name");
  const webHref = document.querySelector(".input-href");
  const addBtn = document.querySelector(".add-btn");
  const list = document.querySelector(".link-list");
  let linkList = [];
  // 点击添加按钮
  addBtn.addEventListener("click", () => {
    const name = webName.value;
    const href = webHref.value;
    if (name.trim() == "" || href.trim() == "") {
      return;
    }
    addLink(name, href);
    webName.value = "";
    webHref.value = "";
  });

  // 给删除按钮绑定点击事件
  function bindDel() {
    linkList.forEach((item) => {
      document.getElementById(item.key).addEventListener("click", () => {
        delLink(item.key);
      });
    });
  }
  // 增加链接
  function addLink(name, href) {
    const link = {
      key: new Date().getTime(),
      name,
      href,
    };
    linkList.push(link);
    saveLinkList();
  }
  // 删除链接
  function delLink(key) {
    linkList = linkList.filter((item) => item.key != key);
    saveLinkList();
  }
  // 存储数据
  function saveLinkList() {
    // 将链接数据发送给background
    chrome.runtime.sendMessage({ linkList: linkList });
  }
  // 监听来自background的数据
  chrome.runtime.onMessage.addListener((message) => {
    if (message && message.linkList) {
      linkList = message.linkList;
    } else {
      linkList = [];
    }
    renderList();
  });

  // 渲染链接列表
  function renderList() {
    const html = linkList
      .map((item) => {
        return `
                    <div class="link-item">
                        <div class="link-content">
                            <div class="link-name">${item.name}</div>
                            <div class="link-href">${item.href}</div>
                        </div>
                        <div class="del-content">
                            <div class="del-btn" id="${item.key}">删除</div>
                        </div>
                    </div>
                `;
      })
      .join("");
    list.innerHTML = html;
    bindDel();
  }
  // 通知background返回列表数据,用来第一次初始化列表数据
  chrome.runtime.sendMessage("return");
};

总结来说,index.js需要把对链接列表数据的改动同步到backgroundbackground把数据进行保存。 在做给删除按钮绑定事件的时候,碰到了一个坑。起初我是直接把绑定事件通过onclick="delLink(${item.key})"的方式写在标签行内的,但是Chrome扩展程序是不允许使用内联JavaScript的,因此采用了addEventListener,这是一个需要注意的点。

background.js

background的作用就三个:1.监听index.js传过来的消息,将链接数据放到本地存储;2.把本地存储里的链接数据发送给index.js;3.根据链接数据创建右键菜单项。

// 创建右键菜单
function createMenu() {
  // 移除旧菜单项
  chrome.contextMenus.removeAll(() => {
    // 从本地存储中取出数据
    chrome.storage.sync.get("linkList", (res) => {
      res.linkList.forEach((item) => {
        // 创建菜单项
        chrome.contextMenus.create({
          id: item.key + "",
          title: item.name,
          type: "normal",
          contexts: ["page"],
          onclick: function () {
            window.open(item.href, item.name, "");
          },
        });
      });
    });
  });
}

// 监听来自idnex.js的数据
chrome.runtime.onMessage.addListener((message) => {
  if (message != "return") {
    // 将数据存在本地存储中
    chrome.storage.sync.set({ linkList: message.linkList });
  }
  // 从本地存储中取出数据
  chrome.storage.sync.get("linkList", (res) => {
    // 将数据发送给index.js
    chrome.runtime.sendMessage({ linkList: res.linkList });
  });
  createMenu();
});

createMenu();

content-script.js

本插件中并不需要对网页元素进行操作,因此content-script的内容是空的。

结束

到此,这款简单的小工具就开发完成了。这款插件还有很多不完善的地方,比如不支持多级菜单,不支持编辑修改等等,大家可以根据需要自行发挥啦。