web_highlight

选中高亮!

اعتبارا من 23-02-2024. شاهد أحدث إصدار.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

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

(I already have a user script manager, let me install it!)

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.

ستحتاج إلى تثبيت إضافة مثل Stylus لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتتمكن من تثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

(لدي بالفعل مثبت أنماط للمستخدم، دعني أقم بتثبيته!)

// ==UserScript==
// @name         web_highlight
// @namespace    http://tampermonkey.net/
// @version      0.1.2
// @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: 40px;
          height: 40px;
          border-radius: 5px;
          position: absolute;
          background-color: red;
          cursor: pointer;
        }
        .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 menuDom = null // 菜单图标元素

    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) {
            // 根据元素查关键词
            key = keyTarget.innerText
            if (!sites[href].includes(key)) {
                sites[href].push(key)
            }
        } else {
            // 根据关键词找到元素
            keyTarget = findClosestChildWithKeyword(key)
        }
        // 设置背景
        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 = 40
        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 = '99';
            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 = '100';
                colorCard.addEventListener('click', (e) => {
                    keyHighlight(null, 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();
        if (selection.toString().length > 0) {
            // 用户选中了文本,可以在这里处理相应的逻辑
            target = event.target;
            console.dir(target, 'target');
            menuDom = document.createElement('div');
            menuDom.className = 'menu_dialog';
            menuDom.style.top = event.clientY + 'px'
            menuDom.style.left = event.clientX + 'px'
            menuDom.style.zIndex = '9999';
            menuDom.innerHTML = 'hello';

            menuDom.addEventListener('mouseup', (e) => {
                // 阻止在设置色卡时触发清除函数
                e.stopPropagation();
            })

            colorPale.showPale(menuDom).then(pale => {
                menuDom.addEventListener('mouseenter', (e) => {
                    console.log(e, '数据')
                    pale.showPale(menuDom)
                    // 鼠标移出时删除菜单
                    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;
        }
    }
})();