YouTube Channel Hover Popup

Display a hover popup with channel info on YouTube after dynamic content load, with immediate loading indicator

目前为 2024-05-07 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name YouTube Channel Hover Popup
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.6
  5. // @description Display a hover popup with channel info on YouTube after dynamic content load, with immediate loading indicator
  6. // @author @dmtri
  7. // @match https://www.youtube.com/*
  8. // @grant none
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. const MAGIC_NUMBER = 2500;
  16. const initTag = '.youtube-popup-desc-init';
  17.  
  18. const createPopup = () => {
  19. const popup = document.createElement('div');
  20. popup.style.position = 'fixed';
  21. popup.style.zIndex = '1000';
  22. popup.style.width = '300px';
  23. popup.style.background = 'white';
  24. popup.style.border = '1px solid black';
  25. popup.style.borderRadius = '8px';
  26. popup.style.padding = '16px';
  27. popup.style.boxShadow = '0 4px 6px rgba(0,0,0,0.1)';
  28. popup.style.display = 'none';
  29. popup.style.fontSize = '16px';
  30. document.body.appendChild(popup);
  31. return popup;
  32. };
  33.  
  34. const popup = createPopup(); // Initialize the popup
  35.  
  36. const showLoadingPopup = (popup, x, y) => {
  37. popup.innerHTML = '<strong>Loading...</strong>';
  38. popup.style.left = `${x}px`;
  39. popup.style.top = `${y}px`;
  40. popup.style.display = 'block';
  41. };
  42.  
  43. const updatePopupContent = (popup, content) => {
  44. popup.innerHTML = content;
  45. // Close button
  46. const closeButton = document.createElement('button');
  47. closeButton.textContent = 'X';
  48. closeButton.style.position = 'absolute';
  49. closeButton.style.top = '5px';
  50. closeButton.style.right = '10px';
  51. closeButton.style.border = 'none';
  52. closeButton.style.background = 'none';
  53. closeButton.style.cursor = 'pointer';
  54. closeButton.style.color = '#333';
  55. closeButton.style.fontSize = '16px';
  56. closeButton.style.fontWeight = 'bold';
  57.  
  58. closeButton.onclick = () => {
  59. popup.style.display = 'none';
  60. };
  61.  
  62. popup.appendChild(closeButton);
  63. };
  64.  
  65. const hidePopup = (popup) => {
  66. popup.style.display = 'none';
  67. };
  68.  
  69. const fetchChannelInfo = async (url) => {
  70. try {
  71. const response = await fetch(url);
  72. const html = await response.text();
  73. const parser = new DOMParser();
  74. const doc = parser.parseFromString(html, 'text/html');
  75. const meta = doc.querySelector('meta[property="og:description"]');
  76. const description = meta ? meta.getAttribute('content') : 'No description available.';
  77. return `<strong>Description:</strong> ${description}<br><a href="${url}">View Channel</a>`;
  78. } catch (error) {
  79. return 'Failed to load description.';
  80. }
  81. };
  82.  
  83. const init = () => {
  84. document.querySelectorAll('.ytd-channel-name#text-container').forEach(channelElement => {
  85. let popupTimeout;
  86.  
  87. channelElement.addEventListener('mouseenter', async (e) => {
  88. clearTimeout(popupTimeout);
  89. popupTimeout = setTimeout(async () => {
  90. const url = channelElement.querySelector('a').href;
  91. showLoadingPopup(popup, e.clientX, e.clientY + 20);
  92. const content = await fetchChannelInfo(url);
  93. updatePopupContent(popup, content);
  94. }, 500); // Reduced the delay to show the loading message sooner
  95. });
  96.  
  97. channelElement.addEventListener('mouseleave', setTimeout(() => {
  98. clearTimeout(popupTimeout);
  99. hidePopup(popup);
  100. }, 1000));
  101.  
  102. channelElement.addEventListener('mousemove', (e) => {
  103. if (popup.style.display !== 'none') {
  104. popup.style.left = `${e.clientX}px`;
  105. popup.style.top = `${e.clientY + 20}px`;
  106. }
  107. });
  108. });
  109. };
  110. const tryInit = () => {
  111. if (!document.querySelector(initTag)) {
  112. setTimeout(() => {
  113. init();
  114. tryInit();
  115. }, MAGIC_NUMBER);
  116. }
  117. };
  118. tryInit();
  119. })();