11/**
22 * MageForge Toolbar – Shared highlight helpers
33 *
4- * Audits that mark elements by adding a CSS class use these two helpers
5- * instead of duplicating the same logic. The CSS class is derived from the
6- * audit key: `mageforge-audit-<key>`.
4+ * Audits use applyHighlight() / clearHighlight() to mark any DOM element.
5+ * Every element gets a fixed-position overlay span that tracks its viewport
6+ * position, so the highlight works regardless of element type, parent
7+ * overflow, or border-radius. The audit CSS class (mageforge-audit-<key>)
8+ * is kept on the element purely as a selector marker for clearHighlight().
79 */
810
9- const OVERLAY_CLASS = 'mageforge-audit-img -overlay' ;
11+ const AUDIT_OVERLAY_CLASS = 'mageforge-audit-overlay' ;
1012
1113/**
12- * Module-level registry: tracks one overlay cleanup function per <img>
13- * element and the set of audit keys currently relying on that overlay.
14- * Using a WeakMap means entries are automatically eligible for GC once
15- * the image node itself is collected.
14+ * Module-level registry: tracks one overlay per element and the set of
15+ * audit keys currently relying on it. WeakMap entries are automatically
16+ * eligible for GC when the element is collected.
1617 *
17- * @type {WeakMap<HTMLImageElement , { cleanup: function, keys: Set<string> }> }
18+ * @type {WeakMap<Element , { cleanup: function, keys: Set<string> }> }
1819 */
19- const imgOverlayRegistry = new WeakMap ( ) ;
20+ const overlayRegistry = new WeakMap ( ) ;
2021
2122/**
2223 * Shared update machinery – a single ResizeObserver and capturing scroll
@@ -43,26 +44,26 @@ function scheduleUpdate() {
4344}
4445
4546/**
46- * Creates a fixed-position overlay <span> that tracks an <img> element's
47- * position in the viewport. Shares a single RAF-throttled scroll/resize
48- * handler across all active overlays instead of creating one per image .
47+ * Creates a fixed-position overlay <span> that tracks any element's
48+ * bounding box in the viewport. Shares a single RAF-throttled scroll/resize
49+ * handler across all active overlays instead of creating one per element .
4950 * Returns a cleanup function that removes the overlay and deregisters it.
5051 *
51- * @param {HTMLImageElement } img
52+ * @param {Element } el
5253 * @returns {function } cleanup
5354 */
54- function createImgOverlay ( img ) {
55+ function createOverlay ( el ) {
5556 const overlay = document . createElement ( 'span' ) ;
56- overlay . className = OVERLAY_CLASS ;
57+ overlay . className = AUDIT_OVERLAY_CLASS ;
5758 document . body . appendChild ( overlay ) ;
5859
5960 function update ( ) {
60- if ( ! img . isConnected ) {
61+ if ( ! el . isConnected ) {
6162 cleanup ( ) ;
62- imgOverlayRegistry . delete ( img ) ;
63+
6364 return ;
6465 }
65- const rect = img . getBoundingClientRect ( ) ;
66+ const rect = el . getBoundingClientRect ( ) ;
6667 overlay . style . top = `${ rect . top } px` ;
6768 overlay . style . left = `${ rect . left } px` ;
6869 overlay . style . width = `${ rect . width } px` ;
@@ -97,33 +98,29 @@ function createImgOverlay(img) {
9798
9899/**
99100 * Removes the highlight class from all previously marked elements and
100- * destroys any associated image overlays .
101+ * destroys their overlays once no audit keys remain .
101102 *
102103 * @param {string } key - Audit key (e.g. 'images-without-alt')
103104 */
104105export function clearHighlight ( key ) {
105106 const cls = `mageforge-audit-${ key } ` ;
106107 document . querySelectorAll ( `.${ cls } ` ) . forEach ( el => {
107108 el . classList . remove ( cls ) ;
108- if ( el . tagName === 'IMG' ) {
109- const entry = imgOverlayRegistry . get ( el ) ;
110- if ( entry ) {
111- entry . keys . delete ( key ) ;
112- if ( entry . keys . size === 0 ) {
113- entry . cleanup ( ) ;
114- imgOverlayRegistry . delete ( el ) ;
115- }
109+ const entry = overlayRegistry . get ( el ) ;
110+ if ( entry ) {
111+ entry . keys . delete ( key ) ;
112+ if ( entry . keys . size === 0 ) {
113+ entry . cleanup ( ) ;
114+ overlayRegistry . delete ( el ) ;
116115 }
117116 }
118117 } ) ;
119118}
120119
121120/**
122- * Highlights a set of elements by adding the audit CSS class, scrolls to the
123- * first result, and updates the counter badge on the toolbar menu item.
124- *
125- * For <img> elements a fixed-position overlay is injected so the red
126- * background is visible regardless of parent overflow or border-radius.
121+ * Highlights a set of elements by injecting a positioned overlay, scrolls to
122+ * the first result, and updates the counter badge on the toolbar menu item.
123+ * Works for any element type – no special casing required in audit code.
127124 *
128125 * @param {Element[] } elements - Elements to mark
129126 * @param {string } key - Audit key (e.g. 'images-without-alt')
@@ -137,16 +134,14 @@ export function applyHighlight(elements, key, context) {
137134 const cls = `mageforge-audit-${ key } ` ;
138135 elements . forEach ( el => {
139136 el . classList . add ( cls ) ;
140- if ( el . tagName === 'IMG' ) {
141- const existing = imgOverlayRegistry . get ( el ) ;
142- if ( existing ) {
143- existing . keys . add ( key ) ;
144- } else {
145- imgOverlayRegistry . set ( el , {
146- cleanup : createImgOverlay ( el ) ,
147- keys : new Set ( [ key ] ) ,
148- } ) ;
149- }
137+ const existing = overlayRegistry . get ( el ) ;
138+ if ( existing ) {
139+ existing . keys . add ( key ) ;
140+ } else {
141+ overlayRegistry . set ( el , {
142+ cleanup : createOverlay ( el ) ,
143+ keys : new Set ( [ key ] ) ,
144+ } ) ;
150145 }
151146 } ) ;
152147 elements [ 0 ] . scrollIntoView ( { behavior : 'smooth' , block : 'center' } ) ;
0 commit comments