web_highlight

选中高亮!

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==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;
        }
    }
})();