这是本人的第二个简单的chrome小插件,第一个插件的开发过程写的比较详细,需要的读者可以看本人的上一篇文章,这里就不重复介绍某些概念性的东西了。
GotoLink
大家通常会把一些常用网站整理在书签里,我也不例外,虽然很便捷,但是总觉得还差那么一点意思(对于喜欢不停按右键的我来说)。因此萌生了一个不如做一个右键菜单版的书签,这是项目地址,最终效果如下图](url)。
开始
先创建一个GotoLink的项目文件夹,项目结构如下图所示:
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需要把对链接列表数据的改动同步到background,background把数据进行保存。
在做给删除按钮绑定事件的时候,碰到了一个坑。起初我是直接把绑定事件通过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的内容是空的。
结束
到此,这款简单的小工具就开发完成了。这款插件还有很多不完善的地方,比如不支持多级菜单,不支持编辑修改等等,大家可以根据需要自行发挥啦。