我们在从掘金复制内容时,发现复制完的内容后面每次都拼接上一些东西,例如:
作者:***
链接:https://juejin.cn/post/***
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
看到这个,每次都在想这是如何实现的呢?
刚开始以为很复杂,以为要给document
或者div
上绑定keydown
,keyup
的事件,后来搞了一波,发现并不需要。这里还有个坑,就是div
上绑定的keydown
,keyup
不生效,如何让这个生效可以自行搜索一下。
复制
直奔主题,需要实现这个很简单,只需要监听浏览器自带的copy
就可以
<body>
<div class="wrap">
<p>我和我亲爱的祖国</p>
<p>一刻也不能分割</p>
<div class="copy">
<p onclick="handleCopy(false)" class="btn">复制内容</p>
<div id="copy-area">
<p>我们的祖国是花园</p>
<p>花园里的花朵真鲜艳</p>
</div>
</div>
</div>
<script>
const handleCopy = ()=>{
const copyText = document.getElementById('copy-area')
navigator.clipboard.writeText(copyText.innerText);
}
document.addEventListener('copy',(event)=>{
// 获取用户选择的文本
var selection = window.getSelection().toString();
if(selection){
const appendText = '\n\n ~~~我是后缀小尾巴~~~'
// 获取剪贴板对象
var clipboardData = event.clipboardData || window.clipboardData;
// 设置剪贴板的内容为选择的文本加上拼接的文字
clipboardData.setData("text", selection + appendText);
// 阻止默认的复制行为
event.preventDefault();
}
})
</script>
handleCopy
中也有一个坑,就是在百度时一般提供的是以下的写法
function myFunction() {
/* 获取文本内容 */
var copyText = document.getElementById("myInput");
/* 选择复制内容 */
copyText.select();
copyText.setSelectionRange(0, 99999); /* 为移动设备设置 */
/* 复制内容到文本域 */
navigator.clipboard.writeText(copyText.value);
/* 弹出已复制的内容 */
alert("复制的文本为: " + copyText.value);
}
这里的坑就时copyText.select()
不生效,可以查下,select
是**HTMLInputElement.select()
方法选中一个 <textarea>
元素或者一个带有 text 字段的 <input>
元素里的所有内容
我们这里要复制的是div
的内容,所以直接将innerText
的值给到剪切板就行
改写复制剪切板
在复制掘金的内容时,大多数情况下都会带一些作者信息之类的内容,现在既然已经了解了复制相关的知识,想着能不能自己写一个插件,当开启这个插件时,就自动把掘金上复制的内容中把作者这些的信息手动删除呢
创建目录
创建工作目录
mkdir demo-plugin
创建清单文件 manifest.json
{
"name":"demo-plugin", // 插件名称
"version":"1.0", // 版本
"description":"demo plugin", // 描述
"manifest_version":3 // 清单版本,有2和3,2在2023年已经被移除
}
本地导入
在chrome浏览器中输入chrome://extensions/
选择加载已解压的扩展程序
,选择demo-plugin
可以看到插件已经被加载进来了,为了便于开发,我们可以将其固定在操作栏位
设置图标
图标是默认的,我们可以设置一下图标
我从iconfont
上下载了一些icon,新建images
目录,将图片放置到此目录下,修改manifest.json
{
"name":"demo-plugin",
"version":"1.0",
"description":"demo plugin",
"manifest_version":3,
"icons":{
"16": "images/clipboard_16.png",
"32": "images/clipboard_32.png",
"48": "images/clipboard_48.png",
"64": "images/clipboard_48.png",
"128": "images/clipboard_128.png"
}
}
重新加载插件
可以看到我们设置的图标已经生效了
添加popup
我的设想是在点击图标时弹出一个popup,popup中有选项,选项中是让我选择是否要修改掘金内容的剪切板。我们在选完之后要将内容存储起来,存储的话,用到了storage
,popup用到了action
,所以需要先修改manifest.json
{
"name":"demo-plugin",
"version":"1.0",
"description":"demo plugin",
"manifest_version":3,
"icons":{
"16": "images/clipboard_16.png",
"32": "images/clipboard_32.png",
"48": "images/clipboard_48.png",
"64": "images/clipboard_48.png",
"128": "images/clipboard_128.png"
},
"permissions": ["storage"],
"action":{
"default_popup":"popup.html"
}
}
创建popup.html
<html>
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" href="popup.css">
</head>
<body>
<div class="clip-wrap" id="senga-clipboard"></div>
<button id="btn" class="btn">确定</button>
<script src="popup.js"></script>
</body>
</html>
popup.css
.clip-item {
margin:10px;
display: flex;
align-items: center;
}
.clip-item input {
margin-right: 10px;
}
.clip-item span {
white-space: nowrap;
}
.btn {
margin:10px 0px 10px 40px;
border:1px solid blue;
border-radius:8px;
color:blue;
padding:5px 10px;
background-color: transparent;
}
popup.js
// 定义列表内容
const lists = [
{
checked: true,
title: '修改掘金剪切板',
id: 'juejin'
}
]
const init = () => {
chrome.storage.local.get(['clipfilter']).then((result) => {
console.log(11, result.clipfilter)
lists[0].checked = result.clipfilter
insertList()
});
}
init()
const handleInput = () => {
const iptLists = document.getElementsByClassName("ipt");
Array.from(iptLists).map(item => {
item.addEventListener("change", (e) => {
const idx = e.target.dataset.id
lists[idx].checked = e.target.checked
});
})
}
// 获取剪切板容器
const clip = document.getElementById("senga-clipboard")
// 添加选项
const insertList = () => {
let str = ''
lists.forEach((item, idx) => {
const input = item.checked ? `<input type="checkbox" checked class="ipt" data-id=${idx} />` : `<input type="checkbox" class="ipt" data-id=${idx} />`
str += `<section class="clip-item">` + input +
`<span>${item.title}</span>
</section>`
})
clip.innerHTML = str
handleInput()
}
// 通过ID找到按钮
const button = document.getElementById("btn");
// 注册按钮点击回调函数
button.addEventListener("click", async () => {
// 将list中checked为true的存储到chrome中
console.log('set', lists[0].checked)
chrome.storage.local.set({ clipfilter: lists[0].checked }).then(() => {
// 关闭popup
window.close()
});
});
重新加载插件后可以看到如下的截图
如何对popup.j
s进行调试呢,可以插件右击选择审查弹出内容
可以在弹出的控制台查看storage
是否生效
坑:这里有个坑,就是在在html文件中不可以直接定义函数
例如:我们是在popup.js
中获取按钮并给按钮绑定事件,按照常规写法,我们可以直接在按钮上写上onclick="test"
,不支持这样的写法,具体是啥报错,可以自己试一下
操作剪切板
操作剪切板,需要剪切板的权限,具体的操作用content_scripts
,修改manifest.json
{
"name":"demo-plugin",
"version":"1.0",
"description":"demo plugin",
"manifest_version":3,
"icons":{
"16": "images/clipboard_16.png",
"32": "images/clipboard_32.png",
"48": "images/clipboard_48.png",
"64": "images/clipboard_48.png",
"128": "images/clipboard_128.png"
},
"permissions": ["storage", "clipboardRead","clipboardWrite"],
"action":{
"default_popup":"popup.html"
},
"content_scripts": [
{
"matches": ["<all_urls>"], // 匹配所有url
"js": ["content.js"]
}
]
}
content.js
document.addEventListener('copy', async function () {
const res = await navigator.clipboard.readText()
chrome.storage.local.get(["clipfilter"]).then((result) => {
const cpLists = result.clipfilter
if (result.clipfilter) {
const filterRes = handlejuejin(res);
navigator.clipboard.writeText(filterRes)
} else {
navigator.clipboard.writeText(res)
}
});
})
function handlejuejin(str) {
if (str.includes('稀土掘金')) {
return str.replace(/作者:.*/, "").replace(/链接:.*/, '').replace(/来源:稀土掘金/, '').replace(/著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。/, '')
}
return str
}
重新加载插件
可以看到当勾选了修改掘金剪切板时,我们从掘金复制的内容就不带【作者】一些相关文字了
如何调试content.js,就是直接打开开发者工具的控制台就可以了
坑:当开发content.js时,已经重新加载了插件,但是还是不生效,提供以下思路:1.修改manifest.json的version ; 2. 刷新浏览器页面
其他知识
上述的开发,主要应用了action
,content_scripts
,常用的还有background
- action是一个浏览器操作API,可以让你控制浏览器工具栏上的插件图标的行为和外观,例如显示或隐藏、改变颜色、添加徽章等。action API相比于browserAction和pageAction API,有更简洁和灵活的设计,可以让你根据不同的上下文来调整插件图标。在Manifest V3中,你需要使用action字段来指定你的图标信息,而不是browser_action或page_action字段。
- content_script是一个在已加载到浏览器的页面上下文中执行的JavaScript脚本,可以读取和修改页面的DOM内容,也可以使用一些WebExtension APIs。content_script只能访问WebExtension APIs的一个小的子集,但是它们可以使用通信系统与后台脚本进行通信,从而间接的访问WebExtension APIs。你可以在manifest.json中使用content_scripts字段来声明式地加载content_script,或者使用tabs.executeScript() API来程序式地加载content_script。
- background是一个在后台运行的JavaScript脚本,可以访问所有WebExtension APIs,但是不能直接访问页面的内容。background脚本可以用来监听浏览器事件、管理全局状态、与其他部分进行通信等。在Manifest V3中,你需要使用service_worker字段来指定你的background脚本,而不是background字段。
发布
至于如何发布到google商店,由于google开发者需要付费,仅支持G Pay,我没有符合的卡,所以如果有相关需求,请自行搜索