您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Converts URLs into clickable links on YouTube Shorts.
当前为
- // ==UserScript==
- // @name YouTube Shorts Linkify
- // @namespace http://tampermonkey.net/
- // @version 1.1
- // @license GPL-3.0-or-later
- // @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';
- function ensureProtocol(url) {
- url = url.trim();
- if (url.startsWith("javascript:void")) return url;
- if (url.startsWith("http://") || url.startsWith("https://")) return url;
- return "https://" + url;
- }
- function initLinkify() {
- const policy = window.trustedTypes ? trustedTypes.createPolicy('ytShortsLinkify', { createHTML: input => input }) : null;
- const style = document.createElement('style');
- style.textContent = "a.yt-short-linkify, a.yt-short-linkify:link, a.yt-short-linkify:visited, a.yt-short-linkify:hover, a.yt-short-linkify:active, a.yt-short-linkify[href^='javascript:void'], a.yt-core-attributed-string__link, a.yt-core-attributed-string__link--call-to-action-color { color: inherit !important; text-decoration: underline !important; cursor: pointer !important; background-color: inherit !important; } .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);
- const urlRegex = /(?<![@,;:'"?!])\b((?:javascript:void(?:\([^)]+\))?(?:[a-zA-Z0-9\-._~:/?#[\]@!$&'()*+,;=%]*)?)|(?:(?:https?:\/\/)?(?:www\.)?[a-zA-Z0-9][a-zA-Z0-9-]*(?:\.[a-zA-Z0-9-]+)*\.[a-zA-Z]{2,}(?:\/[a-zA-Z0-9\-._~:/?#[\]@!$&'()*+,;=%]*)?))\b/g;
- function linkifyTextNode(textNode) {
- if (!textNode.nodeValue || !urlRegex.test(textNode.nodeValue)) return;
- const span = document.createElement('span');
- let text = textNode.nodeValue, lastIndex = 0;
- urlRegex.lastIndex = 0;
- let match;
- while ((match = urlRegex.exec(text)) !== null) {
- const url = match[0], index = match.index;
- span.appendChild(document.createTextNode(text.substring(lastIndex, index)));
- const a = document.createElement('a');
- a.className = 'yt-short-linkify';
- a.href = ensureProtocol(url);
- if (!url.toLowerCase().startsWith("javascript:void")) {
- a.target = '_blank';
- a.rel = 'noopener noreferrer';
- a.textContent = url.replace(/^https?:\/\//i, '');
- } else {
- a.textContent = url;
- }
- function removeTooltip() {
- if (a._tooltip) {
- a._tooltip.remove();
- a._tooltip = null;
- }
- }
- a.addEventListener('mouseenter', function() {
- removeTooltip();
- const tooltip = document.createElement('div');
- tooltip.className = 'custom-tooltip';
- if (!a.href.toLowerCase().startsWith("javascript:void")) {
- tooltip.textContent = a.href;
- } else {
- tooltip.textContent = a.textContent;
- }
- document.body.appendChild(tooltip);
- const rect = a.getBoundingClientRect();
- const tooltipRect = tooltip.getBoundingClientRect();
- tooltip.style.left = (rect.left + window.pageXOffset) + 'px';
- tooltip.style.top = (rect.top + window.pageYOffset - tooltipRect.height - 5) + 'px';
- a._tooltip = tooltip;
- setTimeout(removeTooltip, 2000);
- });
- a.addEventListener('mouseleave', removeTooltip);
- a.addEventListener('mouseout', removeTooltip);
- 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') {
- const href = node.parentNode.getAttribute('href');
- if (href && href.startsWith('javascript:void')) return NodeFilter.FILTER_ACCEPT;
- return NodeFilter.FILTER_REJECT;
- }
- 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);
- }
- const io = new IntersectionObserver(entries => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- linkifyElement(entry.target);
- io.unobserve(entry.target);
- }
- });
- }, { threshold: 0.1 });
- document.querySelectorAll('body *').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) io.observe(node); });
- } else if (mutation.type === 'characterData') {
- if (mutation.target.parentNode) io.observe(mutation.target.parentNode);
- } else if (mutation.type === 'attributes' && mutation.target) {
- io.observe(mutation.target);
- }
- });
- });
- mutationObserver.observe(document.body, { childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: ['class', 'style'] });
- document.addEventListener('click', function() {
- document.querySelectorAll('.custom-tooltip').forEach(function(tooltip) { tooltip.remove(); });
- });
- function attachOuterLinkRedirect() {
- document.querySelectorAll('a.yt-core-attributed-string__link--call-to-action-color').forEach(outerLink => {
- outerLink.removeEventListener('click', outerLink._redirectHandler, true);
- outerLink._redirectHandler = function(event) {
- event.preventDefault();
- event.stopPropagation();
- event.stopImmediatePropagation();
- const innerLink = outerLink.querySelector('a.yt-short-linkify');
- if (innerLink && innerLink.href && innerLink.href !== "javascript:void(0);") {
- setTimeout(() => { window.open(innerLink.href, '_blank'); }, 0);
- }
- };
- outerLink.addEventListener('click', outerLink._redirectHandler, true);
- });
- }
- attachOuterLinkRedirect();
- const outerLinkObserver = new MutationObserver(attachOuterLinkRedirect);
- outerLinkObserver.observe(document.body, { childList: true, subtree: true, attributes: false });
- }
- window.addEventListener('load', initLinkify);
- })();