diff --git a/academyFIVE__HighlightStudyRow.user.js b/academyFIVE__HighlightStudyRow.user.js index bf4aa79..6c78733 100644 --- a/academyFIVE__HighlightStudyRow.user.js +++ b/academyFIVE__HighlightStudyRow.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name academyFIVE::HighlightStudyRow // @namespace dakp/academyfive -// @version 2026.10.003 +// @version 2026.10.004 // @description Hebt beim Hovern einer Zelle die gesamte -Zeile der Studiensemester-Tabelle (profil_studenten_edit.php4) per CSS hervor // @author Dims Akpan // @match https://a5.fhdw-hannover.de/admin/* @@ -15,27 +15,48 @@ (function() { 'use strict'; - const STYLE_CLASS = 'a5-row-hl'; const HOVER_COLOR = 'rgba(0, 120, 200, 0.12)'; + const STUDY_INPUTS = 'input[name*="hochschulsemester"], input[name*="fachsemester"], input[name*="as_gesamt"]'; - // Reines CSS: Hover wird nativ vom Browser verfolgt - const style = document.createElement('style'); - style.textContent = ` + function injectStyle(css) { + const style = document.createElement('style'); + style.textContent = css; + (document.head || document.documentElement).appendChild(style); + } + + // Bevorzugter Pfad: reines CSS über :has(). Kein JavaScript, kein Observer, + // keine DOM-Änderungen -> das Script kann Eigenheiten der Seite (z.B. + // Kontextmenüs, die auf DOM-Änderungen/Mausbewegung reagieren) nicht stören. + const HAS_TABLE = `table:has(${STUDY_INPUTS})`; + const cssSupportsHas = typeof CSS !== 'undefined' + && typeof CSS.supports === 'function' + && CSS.supports(`selector(${HAS_TABLE})`); + + if (cssSupportsHas) { + injectStyle(` + ${HAS_TABLE} tr:hover th, + ${HAS_TABLE} tr:hover td { + background-color: ${HOVER_COLOR} !important; + } + `); + return; + } + + // Fallback für ältere Browser ohne :has()-Unterstützung: + const STYLE_CLASS = 'a5-row-hl'; + injectStyle(` table.${STYLE_CLASS} tr:hover th, table.${STYLE_CLASS} tr:hover td { background-color: ${HOVER_COLOR} !important; } - `; - (document.head || document.documentElement).appendChild(style); + `); - // Studiensemester-Tabelle anhand typischer Eingabefelder erkennen function isStudyTable(table) { - return !!table.querySelector('input[name*="hochschulsemester"], input[name*="fachsemester"], input[name*="as_gesamt"]'); + return !!table.querySelector(STUDY_INPUTS); } function markTable(table) { - if (table.dataset.a5RowHl || table.classList.contains(STYLE_CLASS)) return; - table.dataset.a5RowHl = 'true'; + if (table.classList.contains(STYLE_CLASS)) return; table.classList.add(STYLE_CLASS); } @@ -45,12 +66,30 @@ }); } + // Entprellter Observer, der nur auf neu eingefügte Tabellen/Inputs reagiert + // und nicht auf jede beliebige DOM-Änderung (Kontextmenü, Tooltips, Hover-Klassen). + let scanScheduled = false; + function scheduleScan() { + if (scanScheduled) return; + scanScheduled = true; + setTimeout(() => { + scanScheduled = false; + scanTables(); + }, 200); + } + function onDocReady() { scanTables(); - // Für dynamisch nachgeladene Tabellen: Klassenzuweisung ist idempotent, - // daher dünner Observer mit kindList/childList. - new MutationObserver(scanTables) - .observe(document.documentElement, { childList: true, subtree: true }); + new MutationObserver(mutations => { + const relevant = mutations.some(m => + Array.from(m.addedNodes).some(n => + n.nodeType === Node.ELEMENT_NODE && + (n.tagName === 'TABLE' || n.tagName === 'INPUT' || + n.querySelector('table') || n.querySelector('input')) + ) + ); + if (relevant) scheduleScan(); + }).observe(document.documentElement, { childList: true, subtree: true }); } if (document.readyState === 'loading') {