Fix exclusion grouping duplicate images together

Exclusions were keyed only by content identifier (src / role-img
label), so multiple identical images (or repeated icons) shared one
key and excluding one silently excluded all of them. Each occurrence
now gets a per-run instance number appended to the identifier, so
duplicates can be excluded individually while staying stable across
reloads.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Sascha 2026-07-13 11:29:32 +02:00
parent 1cef0eddca
commit c6a92ea26c
4 changed files with 29 additions and 9 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -80,13 +80,24 @@
return null; return null;
} }
function isExcluded(img, exclusions) { // Mehrere Bilder können dieselbe imageIdentifier() haben (z.B. dasselbe Icon
const id = imageIdentifier(img); // mehrfach auf der Seite). Ohne Zusatz würde ein Ausschluss dann alle gleich
// aussehenden Bilder treffen. Der Zähler hängt daher pro Lauf eine laufende
// Nummer an, sodass jedes Vorkommen einzeln aus-/eingeschlossen werden kann
// stabil über Neuladen, solange Reihenfolge und Anzahl der Duplikate gleich bleiben.
function instanceIdentifier(img, occurrenceCounts) {
const base = imageIdentifier(img);
if (base === null) return null;
const n = occurrenceCounts.get(base) || 0;
occurrenceCounts.set(base, n + 1);
return `${base}#${n}`;
}
function isExcluded(id, exclusions) {
return id !== null && exclusions.includes(id); return id !== null && exclusions.includes(id);
} }
function toggleExclusion(img) { function toggleExclusion(id) {
const id = imageIdentifier(img);
if (id === null) return; if (id === null) return;
const exclusions = loadExclusions(); const exclusions = loadExclusions();
const idx = exclusions.indexOf(id); const idx = exclusions.indexOf(id);
@ -288,6 +299,7 @@
function run() { function run() {
removePreviousRun(); removePreviousRun();
const exclusions = loadExclusions(); const exclusions = loadExclusions();
const occurrenceCounts = new Map();
const stats = { ok: 0, error: 0, warning: 0, excluded: 0, total: 0 }; const stats = { ok: 0, error: 0, warning: 0, excluded: 0, total: 0 };
// Fehlerhafte Verwendung von alt auf Nicht-<img>-Elementen // Fehlerhafte Verwendung von alt auf Nicht-<img>-Elementen
@ -302,14 +314,15 @@
deepQueryAll('img, [role="img"]', document, (img) => { deepQueryAll('img, [role="img"]', document, (img) => {
stats.total += 1; stats.total += 1;
const id = instanceIdentifier(img, occurrenceCounts);
if (isExcluded(img, exclusions)) { if (isExcluded(id, exclusions)) {
stats.excluded += 1; stats.excluded += 1;
createLabel(img, 'excluded', ['Ausgeschlossen (manuell als dekorativ markiert)'], { createLabel(img, 'excluded', ['Ausgeschlossen (manuell als dekorativ markiert)'], {
icon: '⦸', icon: '⦸',
position: 'absolute', position: 'absolute',
onClick: () => { onClick: () => {
toggleExclusion(img); toggleExclusion(id);
run(); run();
} }
}); });
@ -358,7 +371,7 @@
icon: kind === 'success' ? '✔' : kind === 'error' ? '✖' : '⚠️', icon: kind === 'success' ? '✔' : kind === 'error' ? '✖' : '⚠️',
position: 'absolute', position: 'absolute',
onClick: () => { onClick: () => {
toggleExclusion(img); toggleExclusion(id);
run(); run();
} }
}); });

View File

@ -36,5 +36,12 @@
<p>8. Bild mit potenziell gefährlichem alt-Text (XSS-Test):</p> <p>8. Bild mit potenziell gefährlichem alt-Text (XSS-Test):</p>
<img src="https://via.placeholder.com/100x80/000000/ffffff?text=8" alt="&lt;img src=x onerror=alert('XSS')&gt;"> <img src="https://via.placeholder.com/100x80/000000/ffffff?text=8" alt="&lt;img src=x onerror=alert('XSS')&gt;">
<p>9a. Dasselbe Bild dreimal ohne alt (Duplikat-Test nur dieses eine ausschließen):</p>
<img src="https://via.placeholder.com/100x80/999999/ffffff?text=9">
<p>9b. Zweites Vorkommen desselben Bildes:</p>
<img src="https://via.placeholder.com/100x80/999999/ffffff?text=9">
<p>9c. Drittes Vorkommen desselben Bildes:</p>
<img src="https://via.placeholder.com/100x80/999999/ffffff?text=9">
</body> </body>
</html> </html>