Chrome拓展程序hello world
Posted hans774882968
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Chrome拓展程序hello world相关的知识,希望对你有一定的参考价值。
Chrome拓展程序的好处还用我多说吗
实现的是“阅读清单”。参考:https://blog.csdn.net/qq_24734285/article/details/118409229。参考链接有些小错误,我将在下文指出。
效果图
icon是一个小笑脸,截图没截到。点击icon就能展示了,展示的内容叫做“视图”。
目录结构
icon.png
Chrome拓展程序对项目的目录结构只有一个要求:有manifest.json
manifest.json
{
"manifest_version": 3,
"name": "阅读清单",
"version": "1.0.0",
"description": "chrome拓展程序hello world",
"action":
{
"default_icon": "images/icon.png",
"default_title": "chrome拓展程序hello world",
"default_popup": "popup.html"
},
"permissions":
[
"tabs",
"storage",
"unlimitedStorage"
],
"options_page": "options.html",
"homepage_url": "https://github.com/Hans774882968/"
}
manifest_version2和3有一些区别。3的action对应2的browser_action,因此在此指出,参考链接的json的键名是错的!这玩意写错了,页面右上角就不能显示这个icon,也不能展示popup.html指定的视图了。
提示:如果想知道这里写错与否,可以点击“更多工具”=>“拓展程序”,在展示的拓展程序列表里找到我们关注的那个,并点击“错误”按钮(如果没有错误,这个按钮不会出现)。
options.html类似游戏里面的settings,这个hello world项目没用到。在manifest.json里删掉这条键值对也行。
popup是”视图“,直接按照老一套经验开发就行。
popup.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href = "./popup.css" />
</head>
<body>
<!--参考:https://blog.csdn.net/qq_24734285/article/details/118409229-->
<p id="read-list-num"></p>
<div id="read-list"></div>
<div class="btns">
<button id="add-btn">当前tab加入阅读清单</button>
<button id="add-all-btn">加入所有当前打开的tab</button>
<button id="remove-all-btn">清空阅读清单</button>
</div>
<script src="popup.js"></script>
</body>
</html>
popup.css
#read-list{
margin: 16px 0;
/*如果想实现高度过渡效果,需要用max-height+js实时获取高度*/
}
.read-item{
margin: 8px 0;
}
button{
background-color: #6c8cd4;
color: white;
border-width: 0;
cursor: pointer;
}
button:hover{
background-color: #4769c2;
}
.del-btn{
border-radius: 4px;
}
.btns {
min-width: 400px;
display: grid;
grid-template-columns: 1fr 1fr;
column-gap: 8px;
row-gap: 8px;
}
.btns button{
padding: 8px 16px;
border-radius: 8px;
}
popup.js
'use strict';
let utils = (() => {
let isNullObj = (o) => {
return !Object.keys(o).length
}
return {
isNullObj
}
})();
// chrome.storage.sync.remove('tabs')//移除
// chrome.storage.sync.get("tabs", ({tabs}) => console.log(tabs))//获取
;(() => {
let readListNum = document.getElementById('read-list-num')
let readList = document.getElementById('read-list')
let addBtn = document.getElementById('add-btn')
let addAllBtn = document.getElementById('add-all-btn')
let removeAllBtn = document.getElementById('remove-all-btn')
let tabList = []
let getTabItemId = (offset) => {
offset = (typeof offset === 'number') ? Math.max(offset, 1) : 1
if (!tabList.length) return offset
return tabList[tabList.length - 1].id + offset
}
let tabItemHTML = (tabItem) => {
return `
<div class="read-item" data-id="${tabItem.id}">
<a href="${tabItem.url}" target="_blank">${tabItem.title}</a>
<button class="del-btn">删除</button>
</div>`
}
let readListNumHTML = () => {
readListNum.innerText = `阅读清单标签个数:${tabList.length}`
}
let initHTML = () => {
let itemsHTML = ''
tabList.forEach((tabItem) =>
itemsHTML += tabItemHTML(tabItem)
)
readList.innerHTML = itemsHTML
readListNumHTML()
}
let addHTML = (items) => {
let itemsHTML = ''
if (Array.isArray(items)) {
items.forEach((tabItem) =>
itemsHTML += tabItemHTML(tabItem)
)
} else if (items instanceof Object) {
itemsHTML = tabItemHTML(items)
}
readList.innerHTML += itemsHTML
readListNumHTML()
}
chrome.storage.sync.get("tabs", ({tabs}) => {
if ((!!tabs) && !utils.isNullObj(tabs)) {
tabList = tabs
}
// 补丁:如果存储内容没有id属性,就自己初始化一个
if (Array.isArray(tabList) && tabList.length && !(tabList[0].hasOwnProperty('id'))) {
tabList.forEach((tabItem, idx) => tabItem.id = idx + 1)
}
console.log('1', tabList)//
initHTML()
})
addBtn.addEventListener('click', () => {
new Promise((resolve) => {
chrome.tabs.query({
active: true,
windowId: chrome.windows.WINDOW_ID_CURRENT
}, (tabs) => {
if (!(tabs && tabs[0])) return
let tab = tabs[0]
resolve({url: tab.url, title: tab.title, id: getTabItemId()})
})
// 以下api对于popup.html不能正确获取tab
// chrome.tabs.getCurrent((tab) => {
// resolve({url: tab.url, title: tab.title})
// })
}).then((newTab) => {
tabList.push(newTab)
chrome.storage.sync.set({"tabs": tabList})
console.log('2', tabList)//
addHTML(newTab)
})
})
addAllBtn.addEventListener('click', () => {
new Promise((resolve) => {
chrome.windows.getAll({populate: true}, (windows) => {
let newTabs = []
windows.forEach((window) => {
window.tabs.forEach((tab) => {
newTabs.push({url: tab.url, title: tab.title, id: getTabItemId(newTabs.length + 1)})
})
})
resolve(newTabs)
})
}).then((newTabs) => {
tabList = tabList.concat(newTabs)
chrome.storage.sync.set({"tabs": tabList})
console.log('3', tabList)//
addHTML(newTabs)
})
})
// 删除单个标签页
readList.addEventListener('click', (e) => {
if (e.target.className !== 'del-btn') return
const delBtn = e.target
const id = parseInt(delBtn.parentElement.dataset.id)
for (let i = 0; i < tabList.length; ++i) {
if (id !== tabList[i].id) continue
tabList.splice(i, 1)
break
}
chrome.storage.sync.set({"tabs": tabList})
console.log('5', tabList)//
delBtn.parentElement.remove()
readListNumHTML()
})
removeAllBtn.addEventListener('click', () => {
tabList = []
chrome.storage.sync.remove('tabs')
console.log('4', tabList)//
initHTML()
})
})()
上面这些增删改查的代码,在逻辑上如果还有问题,就在评论区指出吧
为了达到存储阅读清单的效果,我们使用了chrome提供的一些api。
- chrome.tabs.query:popup.html用chrome.tabs.getCurrent是不能正确获取当前的tab的,应该用query来获取。
- chrome.windows.getAll:获取所有打开的标签页。以上两个api是异步的,第二个参数是获取完毕后的回调函数,所以要用Promise包裹。
- chrome.storage.sync.get、chrome.storage.sync.set、chrome.storage.sync.remove:用来存储数据,和localStorage类似,但有一些使用上的区别。我觉得它的返回值有点反人类……
以上是关于Chrome拓展程序hello world的主要内容,如果未能解决你的问题,请参考以下文章
WPF游览器程序为啥启动时候打开一个world文件而不运行程序?
6个变态的C语言Hello World程序 之 雷人的程序语言