您需要先安装一个扩展,例如 篡改猴、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.8.4 // @run-at document-start // @description Hover tooltip of image displaying alt attribute, original title, some accessibility-related properties, and URL info. // @license CC0 // ==/UserScript== /* * https://gf.qytechs.cn/en/scripts/418348/versions/new * * Changelog: * 1.8.4 (2022-11-04): trim long strings * 1.8.3 (2022-11-02): ~ minor, omit empty filename from info. * 1.8.2 (2022-10-23): ~ minor, bail out from image-only page also in Chrome / Edge. * 1.8.1 (2022-10-19): ~ minor text corrections. * 1.8.0 (2022-10-18): + 'generator-unable-to-provide-required-alt' https://html.spec.whatwg.org/multipage/images.html#guidance-for-markup-generators. * * § 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 * Upon "hover" set image's title attribute. Luckily tooltips delay catches augmented value. * * § 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> * */ // do not run at image-only pages // Firefox is adding alt same as location if ( document.querySelector(`body > img[src="${document.location.href}"]:only-child`) ) { // @ts-ignore (GreaseMonkey script is in fact function body) return } const originalTitles = new WeakMap(); let lastSetTitle = ''; const docEl = document.documentElement; const listenerConf = { capture: true, passive: true }; docEl.addEventListener('mouseenter', altToTitle, listenerConf); docEl.addEventListener('mouseleave', restoreTitle, listenerConf); const hoverLoadHandlerConf = { passive: true, once: false, capture: true }; function hoverLoadHandler (event) { const tgt = event.target; // console.log('load', tgt) altPic(tgt, 'prepend'); } function altToTitle (event) { const tgt = event.target if (tgt.tagName && tgt.tagName == 'IMG') { if (originalTitles.has(tgt) || (tgt.title && tgt.title === lastSetTitle)) { // few times I got situations when mouseout was not triggered // presumably because someting covered the image // or whole context were temporarily replaced or covered // or perhaps it was reconstructed from dirty snapshot // so this should prevent exoponentially growing title return } tgt.addEventListener('load', hoverLoadHandler, hoverLoadHandlerConf); originalTitles.set(tgt, tgt.getAttribute('title')); altPic(tgt); } } function restoreTitle (event) { const tgt = event.target; if (originalTitles.has(tgt)) { let ot = originalTitles.get(tgt); if (ot === null) { tgt.removeAttribute('title'); } else { tgt.title = ot; } originalTitles.delete(tgt); } tgt.removeEventListener('load', hoverLoadHandler, hoverLoadHandlerConf); } /** * @param {HTMLImageElement} img * @param {'prepend'} [mode] */ function altPic (img, mode) { // console.log('altPic', mode); try { let titleToAppend = ''; if (mode == 'prepend') { titleToAppend = img.title; if (titleToAppend == lastSetTitle) { img.removeAttribute('title'); } } const separator = '---'; const info = []; const alt = img.getAttribute('alt'); let altText = alt; const title = getClosestTitle(img); const role = img.getAttribute('role'); const isPresentation = role === 'presentation'; if (role) { info.push('Role: ' + role); } switch (alt) { case null: info.push(isPresentation ? `(Alt missing but not needed for this role.)` : `⚠ Alt missing`); break; case '': info.push(`Alt: ""`); break; default: if (alt != alt.trim()) { // "quote" characters are generally useful only to reveal leading/trailing whitespace altText = `»${alt}«`; } if (alt == title) { info.push(`Alt (=title): ${altText}`); } else { info.push(`Alt: ${altText}`); } } // https://html.spec.whatwg.org/multipage/images.html#guidance-for-markup-generators const gutpra = img.getAttribute('generator-unable-to-provide-required-alt'); if (gutpra !== null) { info.push(separator); info.push('generator-unable-to-provide-required-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 (ARIA) `' + descby + '`: ' + (document.getElementById(descby) || { textContent: '(element not found)' }).textContent); } // deprecated, 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 (deprecated): ' + 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 (ARIA): ' + histeve); } var fig = getClosestEl(img, 'FIGURE'); if (fig) { let capt = fig.querySelector('figcaption'); if (capt) { info.push(separator); info.push('Caption: ' + capt.textContent.trim()); } } info.push(separator); const srcURI = new URL(img.currentSrc || img.src, img.baseURI); const slugRx = /[^/]+$/; switch (srcURI.protocol) { case 'http:': case 'https:': { if (srcURI.hash) { info.push('Hash: ' + trimString(decodeURIComponent(srcURI.hash))); } if (srcURI.search) { info.push('Params: ' + trimString(decodeURIComponent(srcURI.search))); } let filename = srcURI.pathname.match(slugRx); if (filename) { info.push('File: ' + trimString(decodeURIComponent(filename))); } let path = srcURI.pathname.replace(slugRx, ''); if (path && path != '/') { info.push('Path: ' + trimString(decodeURIComponent(srcURI.pathname.replace(slugRx, '')))); } if (document.location.hostname != srcURI.hostname || window != window.top) { info.push('Host: ' + trimString(srcURI.hostname)); } break; } case 'data:': { info.push(trimString(srcURI.href)); break; } default: info.push('Src: ' + trimString(srcURI.href)); } // ↔ ↕ var CSSsizes = `${img.width} × ${img.height} CSSpx${findRatio(img.width, img.height)}`; var _width_ratio, _height_ratio; if (img.naturalWidth && img.naturalHeight) { // SVG have zero naturals if (img.naturalWidth == img.width && img.naturalHeight == img.height) { CSSsizes += ` (Natural)`; } else { _width_ratio = '~' + (img.width / img.naturalWidth * 100).toFixed(0) + '% of '; _height_ratio = '~' + (img.height / img.naturalHeight * 100).toFixed(0) + '% of '; if (_height_ratio == _width_ratio) { _height_ratio = ''; } CSSsizes += ` (${_width_ratio}${img.naturalWidth} × ${_height_ratio}${img.naturalHeight} natural px${findRatio(img.naturalWidth, img.naturalHeight)})`; } } info.push('Size: ' + CSSsizes); const finalTitle = info.join('\n'); img.title = finalTitle; if (titleToAppend && (finalTitle != titleToAppend)) { img.title += '\n\n-- Previously --\n\n' + titleToAppend; } lastSetTitle = img.title; } catch (e) { // console.error('altPic ERROR', e, img); } } /** * @param {HTMLElement} el */ function getClosestTitle (el) { do { if (el.title) { return el.title; } } while (el = el.parentElement); return '' } /** * @param {HTMLElement} el */ function getClosestEl (el, tagName) { do { if (el.tagName == tagName) { return el; } } while (el = el.parentElement); return false } function findRatio (x, y) { var smallest = Math.min(x, y); var n = 0; var res = n; while (++n <= smallest) { if (x % n == 0 && y % n == 0) res = n; } if (res == 1) { return '' } return ' [' + x / res + ':' + y / res + ']' } function trimString (str) { const limit = 524; if(str.length < limit) { return str; } return str.slice(0, limit) + ' (…+ '+ (str.length - limit) + ' characters)'; }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址