您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Display a hover popup with channel info on YouTube after dynamic content load, with immediate loading indicator
当前为
- // ==UserScript==
- // @name YouTube Channel Hover Popup
- // @namespace http://tampermonkey.net/
- // @version 0.6
- // @description Display a hover popup with channel info on YouTube after dynamic content load, with immediate loading indicator
- // @author @dmtri
- // @match https://www.youtube.com/*
- // @grant none
- // @license MIT
- // ==/UserScript==
- (function() {
- 'use strict';
- const MAGIC_NUMBER = 2500;
- const initTag = '.youtube-popup-desc-init';
- const createPopup = () => {
- const popup = document.createElement('div');
- popup.style.position = 'fixed';
- popup.style.zIndex = '1000';
- popup.style.width = '300px';
- popup.style.background = 'white';
- popup.style.border = '1px solid black';
- popup.style.borderRadius = '8px';
- popup.style.padding = '16px';
- popup.style.boxShadow = '0 4px 6px rgba(0,0,0,0.1)';
- popup.style.display = 'none';
- popup.style.fontSize = '16px';
- document.body.appendChild(popup);
- return popup;
- };
- const popup = createPopup(); // Initialize the popup
- const showLoadingPopup = (popup, x, y) => {
- popup.innerHTML = '<strong>Loading...</strong>';
- popup.style.left = `${x}px`;
- popup.style.top = `${y}px`;
- popup.style.display = 'block';
- };
- const updatePopupContent = (popup, content) => {
- popup.innerHTML = content;
- // Close button
- const closeButton = document.createElement('button');
- closeButton.textContent = 'X';
- closeButton.style.position = 'absolute';
- closeButton.style.top = '5px';
- closeButton.style.right = '10px';
- closeButton.style.border = 'none';
- closeButton.style.background = 'none';
- closeButton.style.cursor = 'pointer';
- closeButton.style.color = '#333';
- closeButton.style.fontSize = '16px';
- closeButton.style.fontWeight = 'bold';
- closeButton.onclick = () => {
- popup.style.display = 'none';
- };
- popup.appendChild(closeButton);
- };
- const hidePopup = (popup) => {
- popup.style.display = 'none';
- };
- const fetchChannelInfo = async (url) => {
- try {
- const response = await fetch(url);
- const html = await response.text();
- const parser = new DOMParser();
- const doc = parser.parseFromString(html, 'text/html');
- const meta = doc.querySelector('meta[property="og:description"]');
- const description = meta ? meta.getAttribute('content') : 'No description available.';
- return `<strong>Description:</strong> ${description}<br><a href="${url}">View Channel</a>`;
- } catch (error) {
- return 'Failed to load description.';
- }
- };
- const init = () => {
- document.querySelectorAll('.ytd-channel-name#text-container').forEach(channelElement => {
- let popupTimeout;
- channelElement.addEventListener('mouseenter', async (e) => {
- clearTimeout(popupTimeout);
- popupTimeout = setTimeout(async () => {
- const url = channelElement.querySelector('a').href;
- showLoadingPopup(popup, e.clientX, e.clientY + 20);
- const content = await fetchChannelInfo(url);
- updatePopupContent(popup, content);
- }, 500); // Reduced the delay to show the loading message sooner
- });
- channelElement.addEventListener('mouseleave', setTimeout(() => {
- clearTimeout(popupTimeout);
- hidePopup(popup);
- }, 1000));
- channelElement.addEventListener('mousemove', (e) => {
- if (popup.style.display !== 'none') {
- popup.style.left = `${e.clientX}px`;
- popup.style.top = `${e.clientY + 20}px`;
- }
- });
- });
- };
- const tryInit = () => {
- if (!document.querySelector(initTag)) {
- setTimeout(() => {
- init();
- tryInit();
- }, MAGIC_NUMBER);
- }
- };
- tryInit();
- })();