From 36b59ae8eefd155c7fd31afefaf9285b62ccebc1 Mon Sep 17 00:00:00 2001 From: Christian Fehmer Date: Tue, 16 Dec 2025 12:30:25 +0100 Subject: [PATCH] impr: add multiple elements found warning in dom utils (@fehmer) (#7242) Co-authored-by: Miodec --- frontend/src/html/pages/test.html | 2 +- frontend/src/ts/pages/account-settings.ts | 4 +-- frontend/src/ts/utils/dom.ts | 35 +++++++++++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/frontend/src/html/pages/test.html b/frontend/src/html/pages/test.html index 95ad7beed..f2d8f33c8 100644 --- a/frontend/src/html/pages/test.html +++ b/frontend/src/html/pages/test.html @@ -417,7 +417,7 @@

0s

-
+
diff --git a/frontend/src/ts/pages/account-settings.ts b/frontend/src/ts/pages/account-settings.ts index ab7ea2378..66de98e1a 100644 --- a/frontend/src/ts/pages/account-settings.ts +++ b/frontend/src/ts/pages/account-settings.ts @@ -12,7 +12,7 @@ import * as BlockedUserTable from "../elements/account-settings/blocked-user-tab import * as Notifications from "../elements/notifications"; import { z } from "zod"; import * as AuthEvent from "../observables/auth-event"; -import { qs, qsr, onWindowLoad } from "../utils/dom"; +import { qs, qsa, qsr, onWindowLoad } from "../utils/dom"; const pageElement = qsr(".page.pageAccountSettings"); @@ -165,7 +165,7 @@ qs(".page.pageAccountSettings")?.onChild("click", ".tabs button", (event) => { page.setUrlParams(state); }); -qs( +qsa( ".page.pageAccountSettings .section.discordIntegration .getLinkAndGoToOauth", )?.on("click", () => { Loader.show(); diff --git a/frontend/src/ts/utils/dom.ts b/frontend/src/ts/utils/dom.ts index 7c5d808ff..025b7b049 100644 --- a/frontend/src/ts/utils/dom.ts +++ b/frontend/src/ts/utils/dom.ts @@ -4,6 +4,7 @@ import { JSAnimation, } from "animejs"; +// Implementation /** * Query Selector * @@ -13,6 +14,7 @@ import { export function qs( selector: string, ): ElementWithUtils | null { + checkUniqueSelector(selector); const el = document.querySelector(selector); return el ? new ElementWithUtils(el) : null; } @@ -44,6 +46,7 @@ export function qsa( export function qsr( selector: string, ): ElementWithUtils { + checkUniqueSelector(selector); const el = document.querySelector(selector); if (el === null) { throw new Error(`Required element not found: ${selector}`); @@ -349,6 +352,7 @@ export class ElementWithUtils { * Query the element for a child element matching the selector */ qs(selector: string): ElementWithUtils | null { + checkUniqueSelector(selector, this); const found = this.native.querySelector(selector); return found ? new ElementWithUtils(found) : null; } @@ -372,6 +376,7 @@ export class ElementWithUtils { * @throws Error if the element is not found. */ qsr(selector: string): ElementWithUtils { + checkUniqueSelector(selector, this); const found = this.native.querySelector(selector); if (found === null) { throw new Error(`Required element not found: ${selector}`); @@ -691,3 +696,33 @@ export class ElementsWithUtils< return this; } } + +function checkUniqueSelector( + selector: string, + parent?: ElementWithUtils, +): void { + if (!import.meta.env.DEV) return; + const elements = parent ? parent.qsa(selector) : qsa(selector); + if (elements.length > 1) { + console.warn( + `Multiple elements found for selector "${selector}". Did you mean to use QSA? If not, try making the query more specific.`, + elements.native, + ); + console.trace("Stack trace for qs/qsr call:"); + if (document.querySelector("#domUtilsQsWarning") !== null) return; + + const bannerCenter = document.querySelector("#bannerCenter"); + const warning = document.createElement("div"); + warning.classList.add("psa", "bad", "content-grid"); + warning.id = "domUtilsQsWarning"; + warning.innerHTML = ` +
+
+
+ "Warning: qs/qsr detected selector(s) matching multiple elements, check console for details." +
+
+ `; + bannerCenter?.appendChild(warning); + } +}