waitForElement
Waits for an element matching the selector to appear in the DOM.
1/**
2 * Waits for an element matching the selector to appear in the DOM.
3 *
4 * @param selector - CSS selector to match.
5 * @param timeout - Max time to wait in ms (default: 5000).
6 * @returns A Promise that resolves with the element or rejects on timeout.
7 */
8export function waitForElement(
9 selector: string,
10 timeout: number = 5000
11): Promise<Element> {
12 return new Promise((resolve, reject) => {
13 const existing = document.querySelector(selector);
14 if (existing) return resolve(existing);
15
16 const observer = new MutationObserver(() => {
17 const el = document.querySelector(selector);
18 if (el) {
19 observer.disconnect();
20 resolve(el);
21 }
22 });
23
24 observer.observe(document.body, {
25 childList: true,
26 subtree: true,
27 });
28
29 const timer = setTimeout(() => {
30 observer.disconnect();
31 reject(new Error(`Element "${selector}" not found within ${timeout}ms`));
32 }, timeout);
33 });
34}
Asynchronous DOM Readiness
Allows waiting for dynamically rendered elements without polling or blocking the main thread.
MutationObserver-Based Efficiency
Utilizes
MutationObserver
to detect DOM changes in a performant, event-driven way.Built-in Timeout Safety
Automatically rejects the promise if the element doesn't appear within the specified time, avoiding potential hangs.
Immediate Resolution When Already Present
Resolves instantly if the element already exists in the DOM, optimizing for fast-loading pages.
Reusable and Flexible
Accepts any CSS selector, making it adaptable for various UI components or conditional DOM scenarios.
Tests | Examples
1beforeEach(() => {
2 document.body.innerHTML = '';
3});
4
5test('resolves if element already exists', async () => {
6 const div = document.createElement('div');
7 div.className = 'test';
8 document.body.appendChild(div);
9
10 const el = await waitForElement('.test');
11 expect(el).toBe(div);
12});
13
14test('resolves when element is added later', async () => {
15 setTimeout(() => {
16 const div = document.createElement('div');
17 div.id = 'delayed';
18 document.body.appendChild(div);
19 }, 50);
20
21 const el = await waitForElement('#delayed');
22 expect(el).toBeInstanceOf(HTMLElement);
23 expect(el?.id).toBe('delayed');
24});
25
26test('rejects after timeout', async () => {
27 await expect(waitForElement('.missing', 100)).rejects.toThrow(
28 'Element ".missing" not found within 100ms'
29 );
30});
Common Use Cases
Waiting for Lazy-Loaded Components
Detect when React/Vue components, modals, or third-party widgets are injected into the DOM.
Automation and Testing
Use in integration tests or browser automation to ensure elements are ready before interaction.
User Interaction Hooks
Trigger behavior (e.g., animations, tracking, accessibility adjustments) once a UI element appears.
Progressive Enhancement
Apply enhancements or listeners only after specific content becomes available in the DOM.
Single-Page Application Routing
React to route-driven DOM changes where elements are not immediately rendered.