起因
很久之前在看文章的时候习惯于PDF格式的笔者总是想要用高亮勾画自己认为重要的内容,奈何网页不是pdf,没有那么方便的东西。
知道接触到油猴脚本,突然觉得自己可以搞一下,但笔者js技术力太菜,又不想花费很多时间在这上面,于是一拖再拖。直到最近AI大火,觉得自己可以试着搞一下了,就通过Bing Chat搞出来了,希望可以为有类似烦恼的朋友提供一个开箱即用的参考。
描述
-
浏览器: Edge
-
油猴: TamperMonkey
-
使用方式:
- 添加高亮:按住Ctrl,同时鼠标选中网页文本即可高亮显示。
- 删除高亮:按住Alt,鼠标左键点击高亮文本即可删除高亮。
-
亮点:
首先,这个脚本可以让你在任何网页上高亮文本。无论你是在阅读新闻、研究报告,还是学习教程,你都可以使用这个工具来标记重要的信息。其次,这个脚本还有一个便笺功能。当你高亮文本时,这个脚本会自动将高亮的文本添加到便笺中。这样,你就可以在一个地方查看所有的高亮文本,而不需要在页面上下滚动寻找。
最后,该脚本存在复制和清空按钮,可以一键复制所有Markdown形式的高亮文本内容或清楚当前所有的高亮。
-
添加方式:
-
找到添加了油猴脚本的右上角,选择“添加新脚本”。如下图
-
进入界面后,将原本的初始内容删除,将下述代码粘贴( 2024.01.04 更新):
-
// ==UserScript==
// @name HighLighter
// @namespace http://tampermonkey.net/
// @version 0.3.0
// @description 当按住键盘的Ctrl键并使用鼠标选中部分网页文字时,将对应文字的背景高亮显示(例如明黄色),当按住键盘的Alt键并使用鼠标左键点击部分网页文字时,删除对应高亮
// @author Baxkiller with Bing AI & Copilot
// @match *://*/*
// @grant none
// ==/UserScript==
// 定义一个高亮颜色
var highlightColor = "yellow";
var highlight_cnt=0;
var highlight_limit=100;
var created_note = false;
// 创建用于暂存高亮内容的数组
var highlightContents = [];
var note;
// 定义和创建网页右侧便笺
class Note {
constructor() {
this.wrapper = document.createElement('div');
this.wrapper.style.position = 'fixed';
this.wrapper.style.right = '0';
this.wrapper.style.top = '20%'; // 垂直居中
this.wrapper.style.width = '200px';
this.wrapper.style.height = '70vh'; // 自动高度
this.wrapper.style.backgroundColor = 'rgba(240, 240, 240, 0.8)'; // 设置背景颜色为略微透明的灰色
this.wrapper.style.padding = '10px';
this.wrapper.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.1)'; // 添加阴影
this.wrapper.style.borderRadius = '18px'; // 添加圆角
this.wrapper.style.zIndex = '9999'; // 置于顶层
this.note = document.createElement('div');
this.note.style.overflow = 'auto';
this.note.style.height = '92%'; // 最大高度60%的视口高度
this.wrapper.appendChild(this.note);
document.body.appendChild(this.wrapper);
this.addCopyButton();
this.addClearButton();
}
addText(text) {
// console.log(text);
this.note.innerHTML += text + '<br>';
}
addHightLightNode(node) {
var text = node.textContent;
var parent = node.parentNode;
var markdownText = text;
if (parent.tagName === 'H1') {
markdownText = '# ' + text;
} else if (parent.tagName === 'H2') {
markdownText = '## ' + text;
} else if (parent.tagName === 'H3') {
markdownText = '### ' + text;
} else if (parent.tagName === 'P') {
markdownText = text;
} else if (parent.tagName === 'SPAN') {
markdownText = text;
} else if (parent.tagName === 'A') {
markdownText = '[' + text + '](' + parent.href + ')';
} else if (parent.tagName === 'IMG') {
markdownText = '![' + parent.alt + '](' + parent.src + ')';
} else if (parent.tagName === 'LI') {
markdownText = '- ' + text;
} else if (parent.tagName === 'UL') {
markdownText = '- ' + text;
} else if (parent.tagName === 'OL') {
markdownText = '1. ' + text;
}
this.addText(markdownText);
}
addCopyButton() {
var copyButton = document.createElement('button');
copyButton.innerText = 'Copy';
copyButton.style.width = '100%'; // 与便笺同宽
copyButton.style.height = '6%';
copyButton.style.backgroundColor = '#007BFF'; // 蓝色背景
copyButton.style.color = '#FFFFFF'; // 白色文字
copyButton.style.border = 'none'; // 无边框
copyButton.style.padding = '10px';
copyButton.style.cursor = 'pointer'; // 鼠标悬停时变为手形
copyButton.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.1)'; // 添加阴影
copyButton.style.borderRadius = '18px'; // 添加圆角
copyButton.style.zIndex = '9999';
copyButton.onclick = () => {
console.log("copy button clicking");
navigator.clipboard.writeText(this.note.innerText).then(function() {
console.log('复制成功');
}, function(err) {
console.error('复制失败', err);
});
console.log("copy button clicked");
};
this.wrapper.appendChild(copyButton);
}
addClearButton() {
var clearButton = document.createElement('button');
clearButton.innerText = 'Clear';
clearButton.style.width = '100%'; // 占据一半宽度
clearButton.style.height = '6%';
clearButton.style.backgroundColor = '#dc3545'; // 红色背景
clearButton.style.color = '#FFFFFF'; // 白色文字
clearButton.style.border = 'none'; // 无边框
clearButton.style.padding = '10px';
clearButton.style.cursor = 'pointer'; // 鼠标悬停时变为手形
clearButton.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.1)'; // 添加阴影
clearButton.style.borderRadius = '18px'; // 添加圆角
clearButton.style.zIndex = '9999';
clearButton.onclick = () => {
console.log("clear button clicking");
this.note.innerHTML = ''; // 清除所有文本
console.log("clear button clicked");
};
this.wrapper.appendChild(clearButton);
}
}
function highlightNode(node, startOffset, endOffset) {
if (highlight_cnt > highlight_limit) {
return false;
}
var highlight_block = document.createElement("highlight");
highlight_block.style.backgroundColor = highlightColor;
var nodeRange = document.createRange();
nodeRange.setStart(node, startOffset !== undefined ? startOffset : 0);
nodeRange.setEnd(node, endOffset !== undefined ? endOffset : node.nodeValue.length);
nodeRange.surroundContents(highlight_block);
if (!created_note) {
note = new Note();
created_note = true;
}
note.addHightLightNode(highlight_block);
highlight_cnt++;
return true;
}
function highlightRange(range) {
var commonAncestor = range.commonAncestorContainer;
var startNode = range.startContainer;
var endNode = range.endContainer;
var treeWalker = document.createTreeWalker(commonAncestor, NodeFilter.SHOW_TEXT);
var currentNode = treeWalker.currentNode;
var inRange = false;
while (currentNode) {
if (currentNode === startNode) {
inRange = true;
}
if (inRange && currentNode.nodeValue.trim() !== '') {
var startOffset = (currentNode === startNode) ? range.startOffset : 0;
var endOffset = (currentNode === endNode) ? range.endOffset : currentNode.nodeValue.length;
highlightContents.push({
node: currentNode,
startOffset: startOffset,
endOffset: endOffset
});
}
if (currentNode === endNode) {
break;
}
currentNode = treeWalker.nextNode();
}
}
function highlightSelection() {
var selection = window.getSelection();
if (selection.rangeCount > 0) {
var range = selection.getRangeAt(0);
var isHighlighted = range.startContainer.parentNode.style.backgroundColor === highlightColor;
if (isHighlighted) return;
highlightRange(range);
highlightContents.forEach(function (highlightContent) {
highlightNode(highlightContent.node, highlightContent.startOffset, highlightContent.endOffset);
});
// 一次性选中内容的后面添加换行符
if (highlightContents.length > 0) {
note.addText('<br>');
}
// 直接清空,防止溢出
highlightContents.length = 0;
highlightContents = [];
selection.removeAllRanges();
}
}
// 定义一个函数,用于取消高亮选中的文字
function unhighlightSelection(event) {
// 获取鼠标点击的元素
var element = event.target;
var isHighlighted = element.tagName === "HIGHLIGHT" && element.style.backgroundColor === highlightColor;
if (!isHighlighted){
// console.log(element.tagName);
// console.log(element.style.backgroundColor);
return;
}
// 将higlilight元素替换为它textContent的内容
var highlight_block = element;
var text = highlight_block.textContent;
var textNode = document.createTextNode(text);
highlight_block.parentNode.replaceChild(textNode, highlight_block);
}
// 监听键盘按下事件
document.addEventListener("keydown", function (event) {
// 判断是否按下了Ctrl键或Alt键
if (event.ctrlKey || event.altKey) {
// 根据按下的键,选择对应的函数作为鼠标事件的监听器
var handler = event.ctrlKey ? highlightSelection : unhighlightSelection;
// 监听鼠标事件,调用对应函数
if (event.ctrlKey) {
// 如果按下了Ctrl键,则监听鼠标松开事件,用于高亮选中的文字
document.addEventListener("mouseup", handler);
} else {
document.addEventListener("mousedown", handler);
}
}
});
// 监听键盘松开事件
document.addEventListener("keyup", function (event) {
// 判断是否松开了Ctrl键或Shift键
if (event.key === "Control" || event.key === "Alt") {
// 根据松开的键,选择对应的函数作为鼠标事件的监听器
var handler = event.key === "Control" ? highlightSelection : unhighlightSelection;
// 移除鼠标事件的监听器
if (event.key === "Control") {
document.removeEventListener("mouseup", handler);
} else {
document.removeEventListener("mousedown", handler);
}
}
});
- Ctrl+S 保存,退出编辑页面后随便打开一篇博文或网页,将右上角的
HighLighter
脚本打开,即可使用。
其他
如果您在使用过程中有任何问题或者建议,可以在本文下留言或私信。
或者,如果您也有其他关于本脚本或者相关的脚本创建/使用/改进想法,欢迎与我一起参与开发/维护新的脚本。