Greasy Fork镜像 支持简体中文。

web_highlight

选中高亮!

  1. // ==UserScript==
  2. // @name web_highlight
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1.4
  5. // @description 选中高亮!
  6. // @author You
  7. // @license MIT
  8. // @match *://*/*
  9. // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
  10. // @grant GM_addStyle
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. // Your code here...
  17.  
  18. // 在网页中注入自定义的 CSS 样式
  19. GM_addStyle(`
  20. .menu_dialog {
  21. width: 30px;
  22. height: 30px;
  23. border-radius: 5px;
  24. position: absolute;
  25. background-color: red;
  26. cursor: pointer;
  27. color: white;
  28. }
  29. .pale_card {
  30. position: absolute;
  31. background-color: white;
  32. border: 1px solid gray;
  33. padding: 5px;
  34. }
  35. .pale_highlight_color {
  36. width: 10px;
  37. height: 10px;
  38. border-radius: 2px;
  39. cursor: pointer;
  40. margin-left: 3px;
  41. transition: width 0.3s, height 0.3s;
  42. }
  43. .pale_highlight_color:first-child {
  44. margin-left: 0;
  45. }
  46. .pale_highlight_color:hover {
  47. border: 2px solid;
  48. }
  49. `);
  50.  
  51. const sites = {}
  52. const href = window.location.href;
  53. sites[href] = []
  54. let target = null // 选中的关键词的目标元素
  55. let currentKeyWord = '' // 当前选中的关键词
  56. let menuDom = null // 菜单图标元素
  57.  
  58. const disabledTagNameList = ['INPUT', 'TEXTAREA', 'IMG'] // 禁用的标签
  59. const isBreakRow = true // 高亮单词是否与所在行断开,如果为否,选中单词会高亮整个元素(行)
  60.  
  61. function findClosestChildWithKeyword(startingElement, keyword) {
  62. // 找到包含keyword的元素
  63. const queue = [startingElement];
  64. while (queue.length > 0) {
  65. const currentElement = queue.shift();
  66. if (currentElement.textContent.includes(keyword)) {
  67. return currentElement;
  68. }
  69. if (currentElement.children) {
  70. queue.push(...currentElement.children);
  71. }
  72. }
  73. return null; // 如果找不到含有关键词的子元素,则返回 null
  74. }
  75.  
  76. function keyHighlight(key, color, keyTarget) {
  77. if (keyTarget) {
  78. // 根据元素查关键词
  79. if (!sites[href].includes(key)) {
  80. sites[href].push(key)
  81. }
  82. } else {
  83. // 根据关键词找到元素
  84. keyTarget = findClosestChildWithKeyword(key)
  85. }
  86. // 设置背景
  87. if (isBreakRow) {
  88. // 将自定义高亮元素替换调关键词
  89. const keyBackgroundSpan = document.createElement('span')
  90. keyBackgroundSpan.style.background = color
  91. keyBackgroundSpan.innerText = key
  92. const htmlString = keyBackgroundSpan.outerHTML
  93. console.log(htmlString, '====htmlString=======>')
  94. keyTarget.innerHTML = keyTarget.innerHTML.replace(key, htmlString)
  95. } else {
  96. key = keyTarget.innerText
  97. const keyBackgroundSpan = document.createElement('span')
  98. keyBackgroundSpan.style.background = color
  99. keyBackgroundSpan.innerText = key
  100. keyTarget.innerHTML = ''
  101. keyTarget.appendChild(keyBackgroundSpan)
  102. }
  103. }
  104.  
  105. class ColorPale {
  106. // 参数
  107. colorList = ['#faa8a8', 'blue', 'green', 'yellow', 'orange']
  108. cardWidth = 20
  109. iconWidth = 30
  110. palePadding = 5
  111. colorMargin = 3
  112.  
  113. // 以下参数仅供程序使用
  114. paleWidth = 0
  115. paleHeight = 0
  116. paleLeft = 0
  117. paleTop = 0
  118. pale = null
  119. isOnCard = false // 鼠标是否进入色卡
  120.  
  121. initColorPale(menuDom) {
  122. this.paleWidth = this.colorList.length * this.cardWidth + this.palePadding * 2 + (this.colorList.length - 1) * this.colorMargin
  123. this.paleHeight = this.cardWidth + this.palePadding * 2
  124. this.paleLeft = parseInt(menuDom.style.left) - ((this.paleWidth - this.iconWidth) / 2)
  125. this.paleTop = parseInt(menuDom.style.top) + this.iconWidth
  126.  
  127. this.pale = document.createElement('div')
  128. this.pale.className = 'pale_card'
  129. this.pale.style.width = this.paleWidth + 'px'
  130. this.pale.style.height = this.paleHeight + 'px'
  131. this.pale.style.left = this.paleLeft + 'px'
  132. this.pale.style.top = this.paleTop + 'px'
  133. this.pale.style.zIndex = '9999';
  134. this.pale.style.display = 'none' // 隐藏
  135.  
  136. this.colorList.forEach(color => {
  137. const colorCard = document.createElement('div')
  138. colorCard.className = 'pale_highlight_color'
  139. colorCard.style.width = this.cardWidth + 'px'
  140. colorCard.style.height = this.cardWidth + 'px'
  141. colorCard.style.backgroundColor = color
  142. colorCard.style.zIndex = '9998';
  143. colorCard.addEventListener('click', (e) => {
  144. keyHighlight(currentKeyWord, color, target)
  145. e.stopPropagation();
  146. })
  147. colorCard.addEventListener('mouseup', (e) => {
  148. // 阻止在设置色卡时触发清除函数
  149. e.stopPropagation();
  150. })
  151. this.pale.appendChild(colorCard)
  152. })
  153.  
  154. this.pale.addEventListener('mouseenter', (e) => {
  155. this.isOnCard = true
  156. })
  157. this.pale.addEventListener('mouseleave', (e) => {
  158. this.isOnCard = false
  159. this.closePale()
  160. })
  161.  
  162. document.body.appendChild(this.pale)
  163. return Promise.resolve(this)
  164. }
  165.  
  166. showPale(menuDom) {
  167. if (this.pale) {
  168. if (menuDom) {
  169. this.paleLeft = parseInt(menuDom.style.left) - ((this.paleWidth - this.iconWidth) / 2)
  170. this.paleTop = parseInt(menuDom.style.top) + this.iconWidth
  171. }
  172. this.pale.style.left = this.paleLeft + 'px'
  173. this.pale.style.top = this.paleTop + 'px'
  174. this.pale.style.display = 'flex'
  175. return Promise.resolve(this)
  176. } else if (menuDom) {
  177. return this.initColorPale(menuDom).then(Pale => {
  178. Pale.pale.style.display = 'flex'
  179. return Pale
  180. })
  181. }
  182. }
  183.  
  184. closePale() {
  185. // 鼠标在色卡上不能关闭
  186. if (!this.isOnCard) {
  187. this.pale.style.display = 'none'
  188. }
  189. }
  190. }
  191.  
  192. // 初始化
  193. const colorPale = new ColorPale
  194.  
  195. // 滚动后重新加载高亮关键词
  196. window.addEventListener('scroll', highlight);
  197.  
  198. let selectionTimeout;
  199.  
  200. document.addEventListener('selectionchange', function() {
  201. // 移除之前添加的mouseup监听器
  202. document.removeEventListener('mouseup', handleMouseUp);
  203.  
  204. // 添加新的mouseup监听器
  205. document.addEventListener('mouseup', handleMouseUp);
  206. });
  207.  
  208. /**document.addEventListener('click', () => {
  209. // 监听选中消失的事件
  210. const selection = window.getSelection();
  211. if (selection.toString().length > 0) {
  212. // 没有选中
  213. clearData()
  214. }
  215. })*/
  216.  
  217. function handleMouseUp(event) {
  218. // 删除之前的数据
  219. clearData()
  220. const selection = window.getSelection();
  221. currentKeyWord = selection.toString()
  222. if (currentKeyWord.length > 0 && !disabledTagNameList.includes(event.target.tagName)) {
  223. // 用户选中了文本,可以在这里处理相应的逻辑
  224. target = event.target;
  225. console.dir(target, 'target');
  226. // 不处理特殊标签 input/textarea
  227. menuDom = document.createElement('div');
  228. menuDom.className = 'menu_dialog';
  229. menuDom.style.top = (event.clientY + 10) + 'px'
  230. menuDom.style.left = (event.clientX + 10) + 'px'
  231. menuDom.style.zIndex = '9999';
  232. menuDom.innerHTML = '色卡';
  233.  
  234. menuDom.addEventListener('mouseup', (e) => {
  235. // 阻止在设置色卡时触发清除函数
  236. e.stopPropagation();
  237. })
  238.  
  239. // colorPale.showPale(menuDom).then(pale => {
  240. menuDom.addEventListener('mouseenter', (e) => {
  241. console.log(e, '数据')
  242. colorPale.showPale(menuDom).then(pale => {
  243. // 鼠标移出时删除菜单
  244. const handleMouseLeave = () => {
  245. setTimeout(() => {
  246. pale.closePale();
  247. menuDom.removeEventListener('mouseleave', handleMouseLeave);
  248. }, 300)
  249. };
  250. menuDom.addEventListener('mouseleave', handleMouseLeave)
  251. })
  252. })
  253. document.body.appendChild(menuDom);
  254. // })
  255. }
  256. }
  257.  
  258. function clearData() {
  259. target = null
  260. if (menuDom) {
  261. document.body.removeChild(menuDom)
  262. menuDom = null
  263. }
  264. }
  265.  
  266. function highlight() {
  267. sites[href].forEach(keyword => {
  268. const { word, color } = keyword
  269.  
  270. })
  271. }
  272.  
  273. function getSelectionElement() {
  274. const selection = window.getSelection();
  275. if (selection.rangeCount > 0) {
  276. const range = selection.getRangeAt(0);
  277. const startContainer = range.startContainer;
  278. const endContainer = range.endContainer;
  279.  
  280. // 找到最接近的包含选中文本的父元素
  281. const closestParentElement = (startContainer.nodeType === 3) ? startContainer.parentNode : startContainer;
  282. // closestParentElement 就是包含选中文本的最接近的父元素
  283. return closestParentElement;
  284. }
  285. }
  286. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址