您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Hover tooltip of image displaying alt attribute, original title, some accessibility-related properties, and URL info.
当前为
// ==UserScript== // @name Image Alt to Title // @namespace myfonj // @include * // @grant none // @version 1.5.2 // @run-at document-start // @description Hover tooltip of image displaying alt attribute, original title, some accessibility-related properties, and URL info. // ==/UserScript== /* * https://gf.qytechs.cn/en/scripts/418348/versions/new * * § Trivia: * ¶ Hover tooltip displays content of nearest element's title attribute (@title). * ¶ Alt attribute (@alt) is possible only at IMG element. * ¶ IMG@alt is not displayed in tooltip. * ¶ IMG cannot have children. * ¶ @title is possible on any element, including IMG. * ¶ IMG@src is also valuable. * * Goal: * Display image alt attribute value in images hover tooltip, add valuable @SRC chunks. * * Details * Pull @alt from image and set it so it is readable as @title tooltip * so that produced title value will not obscure existing parent title * that would be displayed otherwise. Also include image filename from @src, * and additionally path or domain. * * Means * Load (and error?) event listener constructing and setting title. * * Dangers * Artificially altered alt or title after image load event will not be taken into account. * Mitigate with mutationObserver? * * Process * Draw the rest of the owl * * * § Tastcases * * FROM: * <a> * <img> * </a> * TO: * <a> * <img title="Alt missing."> * </a> * * FROM: * <a> * <img alt=""> * </a> * TO: * <a> * <img alt="" title="Alt: ''"> * </a> * * FROM: * <a> * <img alt="░"> * </a> * TO: * <a> * <img alt="░" title="Alt: ░"> * </a> * * FROM: * <a> * <img alt="░" title="▒"> * </a> * TO: * <a> * <img title="Alt: ░, title: ▒"> * </a> * FROM: * <a title="▒"> * <img alt="░"> * </a> * TO: * <a> * <img title="Alt: ░, title: ▒"> * </a> * * */ /** * @type {WeakMap.<HTMLImageElement,{originalTitle?:string,augmentedTitle?:string}>} */ const processedImages = new WeakMap(); const dppx = window.devicePixelRatio; // do not run at image-only pages if (document.querySelector('body>img[alt="' + document.location.href + '"]:only-child')) { // @ts-ignore (GreaseMonkey script is in fact function body) return } document.documentElement.addEventListener('load', delayAltPic, true); // document.documentElement.addEventListener('error', altPic, true); /** * @param {{target: EventTarget}} event */ function delayAltPic (event) { const img = /**@type {HTMLImageElement}*/(event.target); if (!img.tagName) { return }; if (img.tagName != 'IMG') { return }; setTimeout(function(){altPic(img)}, 64); } /** * @param {HTMLImageElement} img */ function altPic (img) { const separator = '---'; var uri; try { const info = []; const alt = img.getAttribute('alt'); // using .getAttribute because .alt == ''; // console.log(alt,img); var known = processedImages.get(img); if (known) { // existing img loaded new src if (img.title == known.augmentedTitle) { // reset to not recursively add our own title to new title // console.log('known, resetting to original', known.originalTitle); img.title = known.originalTitle; } else { // in this case "original" means "augmented by page author" // console.log('known, title does not match', img.title); known.originalTitle = img.title; } } else { processedImages.set(img, { originalTitle: img.title }); known = processedImages.get(img); // console.log('unknown', known); } const title = getClosestTitle(img); switch (alt) { case null: info.push('⚠ Alt missing'); break; case '': info.push(`Alt: ""`); break; default: if( alt != alt.trim() ) { // "quote" characters are generally useful only to reveal leading/trailing whitespace alt = `»${alt}«`; } if (alt == title) { info.push(`Alt (=title): ${alt}`); } else { info.push(`Alt: ${alt}`); } } if (title && alt != title) { info.push(separator); info.push('Title: ' + title); } const descby = img.getAttribute('aria-describedby'); if (descby) { info.push(separator); info.push('Described by `' + descby + '`: ' + (document.getElementById(descby) || { textContent: '(element not found)' }).textContent); } // depreated, but let's see // https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/longDesc // https://www.stylemanual.gov.au/format-writing-and-structure/content-formats/images/alt-text-captions-and-titles-images const longdesc = img.getAttribute('longdesc'); if (longdesc) { info.push(separator); info.push('Long Description: ' + longdesc); } const arialabel = img.getAttribute('aria-label'); if (arialabel) { info.push(separator); info.push('Label (ARIA): ' + arialabel); } // https://html5accessibility.com/stuff/2021/02/09/aria-description-by-public-demand-and-to-thunderous-applause/ const histeve = img.getAttribute('aria-description'); if (histeve) { info.push(separator); info.push('Description: ' + histeve); } var fig = getClosestEl(img, 'FIGURE'); if( fig ){ let capt = fig.querySelector('figcaption'); if( capt) { info.push(separator); info.push('Figcaption: ' + capt.textContent.trim()); } } info.push(separator); const srcURI = new URL(img.currentSrc || img.src, img.baseURI); uri = srcURI; const slugRx = /[^/]+$/; switch (srcURI.protocol) { case 'http:': case 'https:': { if (srcURI.search) { info.push('Params: ' + srcURI.search); } info.push('File: ' + srcURI.pathname.match(slugRx)); let path = srcURI.pathname.replace(slugRx, ''); if(path && path != '/') { info.push('Path: ' + srcURI.pathname.replace(slugRx, '')); } if (document.location.hostname != srcURI.hostname || window != window.top) { info.push('Host: ' + srcURI.hostname); } break; } case 'data:': { let durichunks = srcURI.href.split(','); info.push(durichunks[0] + ', + ' + durichunks[1].length + ' b.'); break; } default: info.push('Src: ' + srcURI.href); } var CSSsizes = `${img.width}×${img.height}px`; // var physicalSizes = `${img.width*dppx}×${img.height*dppx}px`; // ↑ hm, this is not working. how to get dimensions in sampling point counts when dppx != 1 / zoomed page? if (img.naturalWidth && img.naturalHeight) { // SVG have zero naturals if (img.naturalWidth == img.width && img.naturalHeight == img.height) { CSSsizes += ` (Natural)`; } else { CSSsizes += ` (Natural: ${img.naturalWidth}×${img.naturalHeight}px)`; // physicalSizes += ` (Natural: ${img.naturalWidth*dppx}×${img.naturalHeight*dppx}px)`; } } info.push('CSS Size: ' + CSSsizes); // if (dppx != 1) { // info.push('Physical: ' + physicalSizes); // } img.title = known.augmentedTitle = info.join('\n'); } catch (e) { // console.error(e, uri, img); } } /** * @param {HTMLElement} el */ function getClosestTitle (el) { do { if (el.title) { return el.title; } } while ((el = el.parentElement) && el !== document.documentElement); return '' } /** * @param {HTMLElement} el */ function getClosestEl (el, tagName) { do { if (el.tagName == tagName) { return el; } } while ((el = el.parentElement) && el !== document.documentElement); return false }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址