选中高亮!
// ==UserScript==
// @name web_highlight
// @namespace http://tampermonkey.net/
// @version 0.1.4
// @description 选中高亮!
// @author You
// @license MIT
// @match *://*/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant GM_addStyle
// ==/UserScript==
(function() {
'use strict';
// Your code here...
// 在网页中注入自定义的 CSS 样式
GM_addStyle(`
.menu_dialog {
width: 30px;
height: 30px;
border-radius: 5px;
position: absolute;
background-color: red;
cursor: pointer;
color: white;
}
.pale_card {
position: absolute;
background-color: white;
border: 1px solid gray;
padding: 5px;
}
.pale_highlight_color {
width: 10px;
height: 10px;
border-radius: 2px;
cursor: pointer;
margin-left: 3px;
transition: width 0.3s, height 0.3s;
}
.pale_highlight_color:first-child {
margin-left: 0;
}
.pale_highlight_color:hover {
border: 2px solid;
}
`);
const sites = {}
const href = window.location.href;
sites[href] = []
let target = null // 选中的关键词的目标元素
let currentKeyWord = '' // 当前选中的关键词
let menuDom = null // 菜单图标元素
const disabledTagNameList = ['INPUT', 'TEXTAREA', 'IMG'] // 禁用的标签
const isBreakRow = true // 高亮单词是否与所在行断开,如果为否,选中单词会高亮整个元素(行)
function findClosestChildWithKeyword(startingElement, keyword) {
// 找到包含keyword的元素
const queue = [startingElement];
while (queue.length > 0) {
const currentElement = queue.shift();
if (currentElement.textContent.includes(keyword)) {
return currentElement;
}
if (currentElement.children) {
queue.push(...currentElement.children);
}
}
return null; // 如果找不到含有关键词的子元素,则返回 null
}
function keyHighlight(key, color, keyTarget) {
if (keyTarget) {
// 根据元素查关键词
if (!sites[href].includes(key)) {
sites[href].push(key)
}
} else {
// 根据关键词找到元素
keyTarget = findClosestChildWithKeyword(key)
}
// 设置背景
if (isBreakRow) {
// 将自定义高亮元素替换调关键词
const keyBackgroundSpan = document.createElement('span')
keyBackgroundSpan.style.background = color
keyBackgroundSpan.innerText = key
const htmlString = keyBackgroundSpan.outerHTML
console.log(htmlString, '====htmlString=======>')
keyTarget.innerHTML = keyTarget.innerHTML.replace(key, htmlString)
} else {
key = keyTarget.innerText
const keyBackgroundSpan = document.createElement('span')
keyBackgroundSpan.style.background = color
keyBackgroundSpan.innerText = key
keyTarget.innerHTML = ''
keyTarget.appendChild(keyBackgroundSpan)
}
}
class ColorPale {
// 参数
colorList = ['#faa8a8', 'blue', 'green', 'yellow', 'orange']
cardWidth = 20
iconWidth = 30
palePadding = 5
colorMargin = 3
// 以下参数仅供程序使用
paleWidth = 0
paleHeight = 0
paleLeft = 0
paleTop = 0
pale = null
isOnCard = false // 鼠标是否进入色卡
initColorPale(menuDom) {
this.paleWidth = this.colorList.length * this.cardWidth + this.palePadding * 2 + (this.colorList.length - 1) * this.colorMargin
this.paleHeight = this.cardWidth + this.palePadding * 2
this.paleLeft = parseInt(menuDom.style.left) - ((this.paleWidth - this.iconWidth) / 2)
this.paleTop = parseInt(menuDom.style.top) + this.iconWidth
this.pale = document.createElement('div')
this.pale.className = 'pale_card'
this.pale.style.width = this.paleWidth + 'px'
this.pale.style.height = this.paleHeight + 'px'
this.pale.style.left = this.paleLeft + 'px'
this.pale.style.top = this.paleTop + 'px'
this.pale.style.zIndex = '9999';
this.pale.style.display = 'none' // 隐藏
this.colorList.forEach(color => {
const colorCard = document.createElement('div')
colorCard.className = 'pale_highlight_color'
colorCard.style.width = this.cardWidth + 'px'
colorCard.style.height = this.cardWidth + 'px'
colorCard.style.backgroundColor = color
colorCard.style.zIndex = '9998';
colorCard.addEventListener('click', (e) => {
keyHighlight(currentKeyWord, color, target)
e.stopPropagation();
})
colorCard.addEventListener('mouseup', (e) => {
// 阻止在设置色卡时触发清除函数
e.stopPropagation();
})
this.pale.appendChild(colorCard)
})
this.pale.addEventListener('mouseenter', (e) => {
this.isOnCard = true
})
this.pale.addEventListener('mouseleave', (e) => {
this.isOnCard = false
this.closePale()
})
document.body.appendChild(this.pale)
return Promise.resolve(this)
}
showPale(menuDom) {
if (this.pale) {
if (menuDom) {
this.paleLeft = parseInt(menuDom.style.left) - ((this.paleWidth - this.iconWidth) / 2)
this.paleTop = parseInt(menuDom.style.top) + this.iconWidth
}
this.pale.style.left = this.paleLeft + 'px'
this.pale.style.top = this.paleTop + 'px'
this.pale.style.display = 'flex'
return Promise.resolve(this)
} else if (menuDom) {
return this.initColorPale(menuDom).then(Pale => {
Pale.pale.style.display = 'flex'
return Pale
})
}
}
closePale() {
// 鼠标在色卡上不能关闭
if (!this.isOnCard) {
this.pale.style.display = 'none'
}
}
}
// 初始化
const colorPale = new ColorPale
// 滚动后重新加载高亮关键词
window.addEventListener('scroll', highlight);
let selectionTimeout;
document.addEventListener('selectionchange', function() {
// 移除之前添加的mouseup监听器
document.removeEventListener('mouseup', handleMouseUp);
// 添加新的mouseup监听器
document.addEventListener('mouseup', handleMouseUp);
});
/**document.addEventListener('click', () => {
// 监听选中消失的事件
const selection = window.getSelection();
if (selection.toString().length > 0) {
// 没有选中
clearData()
}
})*/
function handleMouseUp(event) {
// 删除之前的数据
clearData()
const selection = window.getSelection();
currentKeyWord = selection.toString()
if (currentKeyWord.length > 0 && !disabledTagNameList.includes(event.target.tagName)) {
// 用户选中了文本,可以在这里处理相应的逻辑
target = event.target;
console.dir(target, 'target');
// 不处理特殊标签 input/textarea
menuDom = document.createElement('div');
menuDom.className = 'menu_dialog';
menuDom.style.top = (event.clientY + 10) + 'px'
menuDom.style.left = (event.clientX + 10) + 'px'
menuDom.style.zIndex = '9999';
menuDom.innerHTML = '色卡';
menuDom.addEventListener('mouseup', (e) => {
// 阻止在设置色卡时触发清除函数
e.stopPropagation();
})
// colorPale.showPale(menuDom).then(pale => {
menuDom.addEventListener('mouseenter', (e) => {
console.log(e, '数据')
colorPale.showPale(menuDom).then(pale => {
// 鼠标移出时删除菜单
const handleMouseLeave = () => {
setTimeout(() => {
pale.closePale();
menuDom.removeEventListener('mouseleave', handleMouseLeave);
}, 300)
};
menuDom.addEventListener('mouseleave', handleMouseLeave)
})
})
document.body.appendChild(menuDom);
// })
}
}
function clearData() {
target = null
if (menuDom) {
document.body.removeChild(menuDom)
menuDom = null
}
}
function highlight() {
sites[href].forEach(keyword => {
const { word, color } = keyword
})
}
function getSelectionElement() {
const selection = window.getSelection();
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
const startContainer = range.startContainer;
const endContainer = range.endContainer;
// 找到最接近的包含选中文本的父元素
const closestParentElement = (startContainer.nodeType === 3) ? startContainer.parentNode : startContainer;
// closestParentElement 就是包含选中文本的最接近的父元素
return closestParentElement;
}
}
})();