Intermediate 7 min

Waiting for an Element to Exist

A small helper that solves 80% of timing bugs in client-side experiments.

Most variant bugs are timing bugs. The script runs before the element exists, the selector returns null, and the variant silently fails. The fix is a waitForElement helper that returns a promise resolving once the node appears.

The helper has three rules. First: check synchronously before observing — if the element is already there, do not waste a MutationObserver. Second: always disconnect the observer once you have the element. Third: include a timeout so a missing element does not leak observers indefinitely.

This single helper, written once and reused everywhere, eliminates an entire class of bug from your CRO codebase.

Example

function waitForElement(selector, { root = document.body, timeout = 8000 } = {}) {
  return new Promise((resolve, reject) => {
    const found = root.querySelector(selector);
    if (found) return resolve(found);
    const obs = new MutationObserver(() => {
      const el = root.querySelector(selector);
      if (el) { obs.disconnect(); resolve(el); }
    });
    obs.observe(root, { childList: true, subtree: true });
    setTimeout(() => { obs.disconnect(); reject(new Error('Timeout: ' + selector)); }, timeout);
  });
}

Try this on the Shop

The best way to learn is to ship. Open this surface and apply the lesson.

See the Selector Wand