文本提取助手

一个轻量级的用户脚本,可帮助您快速提取网页上的文本内容。通过简单的点击操作,即可查看、复制或深入提取嵌套文本内容。

// ==UserScript==
// @name         文本提取助手
// @namespace    http://tampermonkey.net/
// @version      2.1
// @description  一个轻量级的用户脚本,可帮助您快速提取网页上的文本内容。通过简单的点击操作,即可查看、复制或深入提取嵌套文本内容。
// @license      MIT
// @author       niweizhuan
// @match        *://*/*
// @grant        GM_setClipboard
// @grant        GM_notification
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// ==/UserScript==

(function() {
    'use strict';

    // 检查是否被禁用
    if (GM_getValue('scriptDisabled', false)) {
        return;
    }

    // 添加自定义样式
    GM_addStyle(`
        #text-copier-container {
            position: fixed;
            bottom: 20px;
            right: 20px;
            z-index: 9999;
            display: flex;
            flex-direction: column;
            gap: 10px;
            align-items: flex-end;
        }
        
        #text-copier-main-btn {
            padding: 8px 16px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
        }
        
        #text-copier-delete-btn {
            padding: 4px 8px;
            background-color: #f44336;
            color: white;
            border: none;
            border-radius: 50%;
            cursor: pointer;
            width: 24px;
            height: 24px;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 16px;
        }
        
        #text-copier-modal {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            z-index: 10000;
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
            max-width: 80%;
            max-height: 80vh;
            display: flex;
            flex-direction: column;
            min-width: 300px;
            /* 新增响应式控制 */
            max-width: 90vw;
            max-height: 80vh;
            width: auto;
            min-width: 300px;
            overflow: hidden;
            display: flex;
            flex-direction: column;
        }
        
        #text-copier-modal-content {
            flex: 1;
            overflow-y: auto;
            margin-bottom: 15px;
            padding: 10px;
            border: 1px solid #eee;
            border-radius: 4px;
            white-space: pre-wrap;
            word-break: break-word;
            flex: 1;
            max-height: 60vh;
        /* 添加漂亮的滚动条 */
            scrollbar-width: thin;
            scrollbar-color: #4CAF50 #f1f1f1;
        }
        
        #text-copier-modal-footer {
            display: flex;
            justify-content: space-between;
            gap: 10px;
        }
        
        .text-copier-modal-btn {
            padding: 8px 16px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            flex: 1;
        }
        
        #text-copier-copy-btn {
            background-color: #4CAF50;
            color: white;
        }
        
        #text-copier-next-btn {
            background-color: #2196F3;
            color: white;
        }
        
        #text-copier-close-btn {
            background-color: #f44336;
            color: white;
        }
        
        .text-copier-highlight {
            outline: 2px dashed #FF9800;
            outline-offset: 2px;
            background-color: rgba(255, 152, 0, 0.1);
        }
        
        #text-copier-modal-content::-webkit-scrollbar {
        width: 8px;
    }
    
        #text-copier-modal-content::-webkit-scrollbar-track {
        background: #f1f1f1;
        border-radius: 4px;
    }
    
        #text-copier-modal-content::-webkit-scrollbar-thumb {
        background-color: #4CAF50;
        border-radius: 4px;
    }
    `);

    // 注册(不可用)菜单命令
    GM_registerMenuCommand("切换文本获取工具状态", function() {
        const currentState = GM_getValue('scriptDisabled', false);
        GM_setValue('scriptDisabled', !currentState);
        
        if (!currentState) {
            showNotification('文本获取工具已禁用 - 将在下次页面加载时生效');
            // 立即隐藏界面
            const container = document.getElementById('text-copier-container');
            if (container) container.style.display = 'none';
        } else {
            showNotification('文本获取工具已启用 - 将在下次页面加载时生效');
            // 立即显示界面
            const container = document.getElementById('text-copier-container');
            if (container) container.style.display = 'flex';
        }
    });

    // 创建主按钮容器
    const buttonContainer = document.createElement('div');
    buttonContainer.id = 'text-copier-container';

    // 创建主按钮
    const mainButton = document.createElement('button');
    mainButton.id = 'text-copier-main-btn';
    mainButton.textContent = '获取文本';

    // 创建删除按钮
    const deleteButton = document.createElement('button');
    deleteButton.id = 'text-copier-delete-btn';
    deleteButton.textContent = '×';
    deleteButton.title = '隐藏按钮(直到下次打开浏览器)';

    // 将元素添加到容器和页面
    buttonContainer.appendChild(deleteButton);
    buttonContainer.appendChild(mainButton);
    document.body.appendChild(buttonContainer);

    // 创建模态框
    const modal = document.createElement('div');
    modal.id = 'text-copier-modal';
    modal.style.display = 'none';
    
    const modalContent = document.createElement('div');
    modalContent.id = 'text-copier-modal-content';
    
    const modalFooter = document.createElement('div');
    modalFooter.id = 'text-copier-modal-footer';
    
    const copyButton = document.createElement('button');
    copyButton.id = 'text-copier-copy-btn';
    copyButton.textContent = '复制';
    
    const nextButton = document.createElement('button');
    nextButton.id = 'text-copier-next-btn';
    nextButton.textContent = '下一层';
    
    const closeButton = document.createElement('button');
    closeButton.id = 'text-copier-close-btn';
    closeButton.textContent = '关闭';
    
    modalFooter.appendChild(copyButton);
    modalFooter.appendChild(nextButton);
    modalFooter.appendChild(closeButton);
    modal.appendChild(modalContent);
    modal.appendChild(modalFooter);
    document.body.appendChild(modal);

    let isSelecting = false;
    let currentElement = null;
    let clickHistory = [];

    // 主按钮点击事件
    mainButton.addEventListener('click', function(e) {
        e.stopPropagation();
        
        if (!isSelecting) {
            // 进入选择模式
            isSelecting = true;
            mainButton.textContent = '取消选择';
            mainButton.style.backgroundColor = '#f44336';
            
            // 添加高亮样式
            document.body.style.cursor = 'pointer';
            document.querySelectorAll('*').forEach(el => {
                el.style.cursor = 'pointer';
            });
        } else {
            // 取消选择模式
            exitSelectionMode();
        }
    });

    // 删除按钮点击事件
    deleteButton.addEventListener('click', function(e) {
        e.stopPropagation();
        buttonContainer.style.display = 'none';
        GM_setValue('buttonHidden', true);
        showNotification('获取文本按钮已隐藏');
        if (currentElement) {
            currentElement.classList.remove('text-copier-highlight');
        }
    });

    // 复制按钮点击事件
    copyButton.addEventListener('click', function() {
        const text = modalContent.textContent;
        if (text && text !== '未找到文本内容') {
            try {
                if (typeof GM_setClipboard !== 'undefined') {
                    GM_setClipboard(text);
                    showNotification('复制成功', 'success');
                } else {
                    navigator.clipboard.writeText(text).then(() => {
                        showNotification('复制成功', 'success');
                    }).catch(err => {
                        console.error('复制失败:', err);
                        showNotification('复制失败: ' + err.message, 'error');
                    });
                }
                
                // 复制成功后1秒移除高亮
                if (currentElement) {
                    setTimeout(() => {
                        currentElement.classList.remove('text-copier-highlight');
                    }, 1000);
                }
            } catch (err) {
                console.error('复制失败:', err);
                showNotification('复制失败: ' + err.message, 'error');
            }
        } else {
            showNotification('没有可复制的文本内容', 'warning');
        }
    });

    // 下一层按钮点击事件
    nextButton.addEventListener('click', function() {
        if (!currentElement) return;
        
        // 先移除旧元素的高亮
        currentElement.classList.remove('text-copier-highlight');
        
        // 查找下一个元素
        const children = Array.from(currentElement.children);
        let nextElement = null;
        
        for (const child of children) {
            if (hasVisibleText(child)) {
                nextElement = child;
                break;
            }
        }
        
        if (nextElement) {
            currentElement = nextElement;
            showElementText(currentElement);
        } else {
            showNotification('已到达最底层');
            // 如果没有下层了,1秒后移除当前高亮
            setTimeout(() => {
                if (currentElement) {
                    currentElement.classList.remove('text-copier-highlight');
                }
            }, 1000);
        }
    });

    // 关闭按钮点击事件
    closeButton.addEventListener('click', function() {
        modal.style.display = 'none';
        if (currentElement) {
            currentElement.classList.remove('text-copier-highlight');
            currentElement = null;
        }
        clickHistory = [];
    });

    // 显示通知
    function showNotification(message, type = 'info') {
        // 移除之前的通知
        const oldNotification = document.getElementById('text-copier-custom-notification');
        if (oldNotification) oldNotification.remove();

        // 创建新通知
        const notification = document.createElement('div');
        notification.id = 'text-copier-custom-notification';
        notification.textContent = message;
        notification.style.position = 'fixed';
        notification.style.bottom = '20px';
        notification.style.left = '50%';
        notification.style.transform = 'translateX(-50%)';
        notification.style.padding = '10px 20px';
        notification.style.borderRadius = '4px';
        notification.style.zIndex = '10000';
        notification.style.transition = 'opacity 0.3s';
        notification.style.maxWidth = '80%';
        notification.style.textAlign = 'center';
        
        // 根据类型设置样式
        switch(type) {
            case 'success':
                notification.style.backgroundColor = '#4CAF50';
                notification.style.color = 'white';
                break;
            case 'error':
                notification.style.backgroundColor = '#f44336';
                notification.style.color = 'white';
                break;
            case 'warning':
                notification.style.backgroundColor = '#FF9800';
                notification.style.color = 'white';
                break;
            default:
                notification.style.backgroundColor = '#2196F3';
                notification.style.color = 'white';
        }
        
        document.body.appendChild(notification);
        
        // 自动消失
        setTimeout(() => {
            notification.style.opacity = '0';
            setTimeout(() => notification.remove(), 300);
        }, 300);
    }

    // 检查元素是否有可见文本
    function hasVisibleText(element) {
        if (!element) return false;
        
        // 检查元素是否可见
        const style = window.getComputedStyle(element);
        if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') {
            return false;
        }
        
        // 检查是否有直接文本内容
        if (element.textContent.trim()) {
            return true;
        }
        
        // 检查是否有子元素包含文本
        for (const child of element.children) {
            if (hasVisibleText(child)) {
                return true;
            }
        }
        
        return false;
    }

    // 显示元素的文本内容
    function showElementText(element) {
        if (!element) return;
        
        // 移除之前的高亮
        if (currentElement) {
            currentElement.classList.remove('text-copier-highlight');
        }
        
        currentElement = element;
        currentElement.classList.add('text-copier-highlight');
        
        // 获取文本内容(限制最大长度)
        let text = element.textContent.trim();
        
        // 如果文本为空,尝试获取其他属性
        if (!text) {
            text = element.value || element.getAttribute('placeholder') || 
                   element.getAttribute('aria-label') || element.getAttribute('title') || '';
            text = text.trim();
        }
        
        // 显示在模态框中
        modalContent.textContent = text || '未找到文本内容';
        modal.style.display = 'block';
        
        // 记录点击历史
        clickHistory.push({
            element: element,
            position: getElementPosition(element),
            text: text
        });
    }
    
    // 获取元素在页面中的位置
    function getElementPosition(element) {
        const rect = element.getBoundingClientRect();
        return {
            x: rect.left + window.scrollX,
            y: rect.top + window.scrollY,
            width: rect.width,
            height: rect.height
        };
    }

    // 退出选择模式
    function exitSelectionMode() {
        isSelecting = false;
        mainButton.textContent = '获取文本';
        mainButton.style.backgroundColor = '#4CAF50';
        
        // 恢复光标样式
        document.body.style.cursor = '';
        document.querySelectorAll('*').forEach(el => {
            el.style.cursor = '';
        });
    }

    // 页面点击事件处理
    document.addEventListener('click', function(e) {
        if (!isSelecting) return;
        
        // 如果点击的是我们的按钮或模态框,则不处理
        if (e.target.closest('#text-copier-container') || e.target.closest('#text-copier-modal')) {
            return;
        }
        
        e.preventDefault();
        e.stopPropagation();
        
        // 获取点击元素
        const target = e.target;
        showElementText(target);
        
        // 退出选择模式
        exitSelectionMode();
    }, true);
})();

QingJ © 2025

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