← Back to home
Tutorial · Detection

How to detect every iframe
on a webpage.

You have three options: a one-line snippet in DevTools, a slightly fuller script that returns size and origin per frame, or a one-click Chrome extension that does both visually. All three find every iframe on the page — visible, hidden, same-origin, cross-origin. Pick the one that fits the moment.

Updated:11 May 2026Read:5 minLevel:Beginner

Why detect iframes at all?

Most modern pages are not one document — they are a host document with a handful of iframes from elsewhere. Video players, payment widgets, ads, analytics beacons, social embeds, consent banners, A/B-testing scripts — every one of them sits inside an iframe. A page might display six and load fifteen. Knowing which is which is the difference between an ad audit, a security sweep, a privacy report, or a QA pass.

The DOM API gives you a one-line query that returns all of them. The rest of this guide is what to do with the result.

Method 1 — The DevTools one-liner

Open DevTools (F12 on Windows / Linux, Cmd+Option+I on macOS), click the Console tab, and run:

document.querySelectorAll('iframe')

The returned NodeList contains every <iframe> element in the document. You can iterate it directly:

for (const f of document.querySelectorAll('iframe')) {
  console.log(f.src || '(blank)', f.offsetWidth, '×', f.offsetHeight);
}

That covers 80% of casual checks. What it does not tell you: which frames are hidden, which are cross-origin, and which were added after page load.

Method 2 — Enriched audit in JavaScript

For a fuller picture in one shot, map each frame to a structured object with size, visibility and same-origin status:

Paste in Console — returns a JSON-friendly array.
Array.from(document.querySelectorAll('iframe')).map(f => {
  const visible =
    f.offsetParent !== null && f.offsetWidth > 0 && f.offsetHeight > 0;
  let sameOrigin = null;
  try { sameOrigin = new URL(f.src).origin === location.origin; }
  catch { /* blank or javascript: src */ }
  return {
    src: f.src || '(blank)',
    size: `${f.offsetWidth}x${f.offsetHeight}`,
    visible,
    sameOrigin,
    sandbox: f.getAttribute('sandbox'),
  };
});

The Chrome DevTools console renders the resulting array as a table — right-click → Store as global variable and you can keep working with it. For an audit report, paste the printed JSON into a ticket or a CSV.

Catching hidden frames

Hidden iframes — trackers, 1×1 pixels, frames moved off-screen — already appear in the list above, but they are not flagged. To single them out:

Array.from(document.querySelectorAll('iframe')).filter(f =>
  f.offsetParent === null ||
  f.offsetWidth <= 1 ||
  f.offsetHeight <= 1
);

That filter catches display: none, visibility: hidden (matched via offsetParent === null), and 1×1 tracking pixels. For a full treatment of the patterns, see the dedicated guide on finding hidden iframes.

Method 3 — One click with Iframe Detector

If you do this more than once a week, the console workflow gets old. The Iframe Detector Chrome extension shows the same list as the script above — plus origin colour-coding, hidden-frame badges, an on-page highlight overlay, and copy / JSON / CSV / Markdown export — from one click on the toolbar icon. No console, no pasting, no MutationObserver setup; the popup also catches frames that are added after first paint.

Same data, no typing. Free, MIT, no account, no URLs sent anywhere.

Limits of querySelectorAll

document.querySelectorAll('iframe') only sees frames in the current document. Three caveats follow from that:

  • Nested frames are not enumerated recursively. You have to run the query inside each frame separately. Cross-origin frames refuse access.
  • Late-loaded frames (added after page load) are not in the static NodeList — you need a MutationObserver to keep watching.
  • Shadow DOM iframes inside open shadow roots are reachable; closed shadow roots are not.

To watch for late-loaded iframes:

new MutationObserver(records => {
  for (const r of records) {
    for (const n of r.addedNodes) {
      if (!(n instanceof HTMLElement)) continue;
      if (n.tagName === 'IFRAME') console.log('+ iframe', n.src);
      n.querySelectorAll?.('iframe').forEach(f =>
        console.log('+ iframe', f.src)
      );
    }
  }
}).observe(document.body, { childList: true, subtree: true });

Frequently asked questions

It returns a static NodeList of every <iframe> element in the current document, in document order. The list is captured at the moment of the call — frames inserted afterwards are not added. Hidden frames (display:none, visibility:hidden, zero-size, off-screen) are all included; the call does not differentiate them.

Only on the top-level document you're inspecting. Each cross-origin iframe is its own document with its own Same-Origin Policy boundary — you cannot reach into it from the parent. Run the same query inside each frame to enumerate its own iframes, but most third-party frames will block you with the browser's security model.

Tracking pixels, analytics beacons, and ad attribution use 1×1 hidden iframes by design. Older sites also load chat widgets, A/B-testing tools, and consent banners as hidden frames that only become visible on demand. All of them are real iframe elements in the DOM.

Yes — use MutationObserver on the document body, filter for added nodes that are iframes or contain iframes, and re-run your audit each time one appears. This is what extension content scripts do behind the scenes.

No, by design. It runs only on the tab whose toolbar icon you click. There is no background sweep of your other tabs. This keeps the activeTab permission minimal and avoids any possibility of cross-site tracking.