您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Cool stuffs!
当前为
- // ==UserScript==
- // @name Youtube channel description popup on hover
- // @namespace http://tampermonkey.net/
- // @version 0.1
- // @description Cool stuffs!
- // @author Duc Trinh @dmtri
- // @match https://www.youtube.com/*
- // @icon
- // @grant none
- // @license MIT
- // ==/UserScript==
- (function () {
- 'use strict';
- const initTag = '.youtube-popup-desc-init';
- // a tampermonkey script to get the profile details
- // upon hover a channel profile on youtube (after 1.5 sec)
- // and then display the channel desc in a popup
- const profileIdentifierUrlContainer = '#container.ytd-channel-name';
- // get the url from element with profileIdentifier
- const getProfileUrl = (profileMetaDataElement) => {
- const anchor = profileMetaDataElement.getElementsByTagName('a')[0];
- return anchor.href;
- };
- const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
- // display a popup when hover over the profileMetaData element
- const init = (force = false) => {
- if (!force && document.querySelector(initTag)) {
- return;
- }
- const profileMetaDataElement = document.querySelectorAll(profileIdentifierUrlContainer);
- if (!profileMetaDataElement || !profileMetaDataElement.length) {
- return;
- }
- profileMetaDataElement.forEach((element) => {
- element.addEventListener('mouseenter', async () => {
- let isValidGesture = true;
- const mouseLeaveHandler = () => {
- isValidGesture = false;
- };
- element.removeEventListener('mouseleave', mouseLeaveHandler);
- element.addEventListener('mouseleave', mouseLeaveHandler);
- await wait(1500);
- // valid gesture meaning a mouse enter event
- // is not followed by a mouse leave event
- // within 1.5 seconds
- handler(element, isValidGesture);
- });
- });
- // append init tag to document
- const initTagElement = document.createElement('div');
- initTagElement.classList.add(initTag.replace('.', ''));
- document.body.appendChild(initTagElement);
- // try init when popstate event happen
- window.addEventListener('popstate', () => {
- tryInit(true);
- });
- };
- const handler = async (profileMetaDataElement, isValidGesture) => {
- if (!isValidGesture) return;
- // display a native dialog element
- const dialog = document.createElement('dialog');
- const url = getProfileUrl(profileMetaDataElement);
- let desc = '';
- // append a spinner next to a channel name, then close it after 3 sec
- const spinner = document.createElement('div');
- spinner.innerHTML = 'loading...';
- profileMetaDataElement.appendChild(spinner);
- setTimeout(() => {
- spinner.remove();
- }, 3000);
- await fetch(url)
- .then((response) => response.text())
- .then((html) => {
- // <meta property="og:description" content="This is a place for all the things that are awesome on stream. ">
- const parser = new DOMParser();
- const doc = parser.parseFromString(html, 'text/html');
- const meta = doc.querySelector('meta[property="og:description"]');
- desc = !meta ? (desc = 'No desc available') : meta.getAttribute('content');
- });
- dialog.innerHTML = `
- <div>
- <h2>${desc}</h2>
- <h1>
- <a href="${url}">${url}</h1>
- </h1>
- </div>
- `;
- document.body.appendChild(dialog);
- dialog.showModal();
- setTimeout(() => {
- dialog.close();
- }, 3000);
- };
- // while there is no init tag in the document, keep trying to init
- const tryInit = (force = false) => {
- if (!document.querySelector(initTag)) {
- setTimeout(() => {
- init(force);
- tryInit(force);
- }, 1500);
- }
- };
- tryInit();
- })();