您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Converts URLs into clickable links on YouTube Shorts.
当前为
// ==UserScript== // @name YouTube Shorts Linkify // @namespace http://tampermonkey.net/ // @author UniverseDev // @license GPL-3.0-or-later // @version 1.2 // @description Converts URLs into clickable links on YouTube Shorts. // @match https://www.youtube.com/shorts/* // @icon https://www.google.com/s2/favicons?domain=www.youtube.com&sz=64 // @grant none // ==/UserScript== (function(){ 'use strict'; let tooltipElement = document.createElement('div'); tooltipElement.className = 'custom-tooltip'; tooltipElement.style.display = 'none'; const urlRegex = /\b((?:https?:\/\/)?(?:www\.)?[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*\.[a-zA-Z]{2,}(?:\/\S*?))(?=\s|$)/g; function isInsideTooltip(node) { let current = node.parentNode; while (current) { if (current.classList && current.classList.contains('custom-tooltip')) { return true; } current = current.parentNode; } return false; } function linkifyTextNode(textNode) { if (!textNode.nodeValue) return; urlRegex.lastIndex = 0; if (!urlRegex.test(textNode.nodeValue)) return; const span = document.createElement('span'); const text = textNode.nodeValue; let lastIndex = 0; urlRegex.lastIndex = 0; let match; while ((match = urlRegex.exec(text)) !== null) { const url = match[1]; const index = match.index; span.appendChild(document.createTextNode(text.substring(lastIndex, index))); const a = document.createElement('a'); a.className = 'yt-short-linkify'; a.href = /^https?:\/\//i.test(url) ? url : 'https://' + url; a.target = '_blank'; a.rel = 'noopener noreferrer'; a.textContent = url; a.addEventListener('mouseenter', function() { tooltipElement.textContent = url; tooltipElement.style.display = 'block'; const rect = a.getBoundingClientRect(); const tooltipRect = tooltipElement.getBoundingClientRect(); tooltipElement.style.left = (rect.left + window.pageXOffset) + 'px'; tooltipElement.style.top = (rect.top + window.pageYOffset - tooltipRect.height - 5) + 'px'; }); a.addEventListener('mouseleave', function() { tooltipElement.style.display = 'none'; }); span.appendChild(a); lastIndex = index + url.length; } span.appendChild(document.createTextNode(text.substring(lastIndex))); textNode.parentNode.replaceChild(span, textNode); } function linkifyElement(element) { const treeWalker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, { acceptNode: function(node) { if (node.parentNode && node.parentNode.nodeName === 'A') return NodeFilter.FILTER_REJECT; if (isInsideTooltip(node)) return NodeFilter.FILTER_REJECT; urlRegex.lastIndex = 0; return (node.nodeValue && urlRegex.test(node.nodeValue)) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; } }); const nodes = []; while (treeWalker.nextNode()) { nodes.push(treeWalker.currentNode); } nodes.forEach(linkifyTextNode); } function initLinkify() { const style = document.createElement('style'); style.textContent = "a.yt-short-linkify { color: inherit; text-decoration: underline; cursor: pointer; } " + ".custom-tooltip { position: absolute; background: #333; color: #fff; padding: 4px 8px; " + "border-radius: 4px; font-size: 12px; pointer-events: none; z-index: 10000; opacity: 0.9; white-space: nowrap; }"; document.head.appendChild(style); document.body.appendChild(tooltipElement); const io = new IntersectionObserver(entries => { entries.forEach(entry => { if (entry.isIntersecting) { linkifyElement(entry.target); io.unobserve(entry.target); } }); }, { threshold: 0.1 }); document.querySelectorAll('body *:not(.custom-tooltip)').forEach(el => io.observe(el)); const mutationObserver = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.type === 'childList') { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE && !node.classList.contains('custom-tooltip')) { io.observe(node); } }); } else if (mutation.type === 'characterData') { if (mutation.target.parentNode && !isInsideTooltip(mutation.target)) { io.observe(mutation.target.parentNode); } } else if (mutation.type === 'attributes') { if (mutation.target && !mutation.target.classList.contains('custom-tooltip')) { io.observe(mutation.target); } } }); }); mutationObserver.observe(document.body, { childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: ['class', 'style'] }); } window.addEventListener('load', initLinkify); })();