Skip to content

Commit 98de375

Browse files
committed
fix(iframe): stop suggesting an option that is already on
A cross-origin iframe is captured as a placeholder that keeps its box, because placeholders defaults to true. The warning now says that and points at the opt-out. Docs said such frames appear empty; they do not.
1 parent 533e7e7 commit 98de375

2 files changed

Lines changed: 11 additions & 5 deletions

File tree

docs/how-to/capture-iframe/index.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
<link rel="icon" type="image/png" href="../../assets/favicon/favicon-96x96.png" sizes="96x96">
1919
<link rel="stylesheet" href="../../shared.css"><link rel="stylesheet" href="../../page.css">
2020
<script type="application/ld+json">{"@context":"https://schema.org","@type":"TechArticle","headline":"Capture an iframe as an image","description":"Capture a same-origin iframe as part of a DOM screenshot and understand the browser limits for cross-origin frames.","url":"https://snapdom.dev/how-to/capture-iframe/","datePublished":"2026-07-16","dateModified":"2026-07-16","author":{"@type":"Organization","name":"Zumerlab"},"dependencies":"SnapDOM (@zumer/snapdom)"}</script>
21-
<script type="application/ld+json">{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":"Can I capture a YouTube or third-party iframe?","acceptedAnswer":{"@type":"Answer","text":"Not directly when it is cross-origin. The browser blocks access to its document."}},{"@type":"Question","name":"What happens to inaccessible frames?","acceptedAnswer":{"@type":"Answer","text":"Enable placeholders to preserve their layout while making the unavailable content explicit."}}]}</script>
21+
<script type="application/ld+json">{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":"Can I capture a YouTube or third-party iframe?","acceptedAnswer":{"@type":"Answer","text":"Not directly when it is cross-origin. The browser blocks access to its document."}},{"@type":"Question","name":"What happens to inaccessible frames?","acceptedAnswer":{"@type":"Answer","text":"They are captured as a striped placeholder the size of the frame, so the layout is preserved and the unavailable content is explicit. Pass placeholders: false for an invisible spacer instead."}}]}</script>
2222
<script type="application/ld+json">{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"SnapDOM","item":"https://snapdom.dev/"},{"@type":"ListItem","position":2,"name":"How-To","item":"https://snapdom.dev/how-to/"},{"@type":"ListItem","position":3,"name":"Capture an iframe as an image","item":"https://snapdom.dev/how-to/capture-iframe/"}]}</script>
2323
</head>
2424
<body>
@@ -31,9 +31,9 @@
3131
placeholders: true,
3232
embedFonts: true,
3333
})
34-
document.body.appendChild(image)</pre></section><section class="prose"><h2>Why this pattern works</h2><p>SnapDOM reads an accessible iframe document, captures its visible content and places the result back into the parent snapshot.</p></section><section class="prose"><h2 id="live-demo">Capture a same-origin iframe</h2><p>The embedded srcdoc frame is same-origin and can be included in the parent capture.</p><div class="demo-box"><div class="demo-stage"><div id="demo-iframe" style="width:360px;padding:12px;background:#0F1E4D"><div style="color:#D9E2F7;font-size:11px;margin:0 0 8px">SAME-ORIGIN FRAME</div><iframe title="Capture demo" srcdoc="<style>body{margin:0;padding:20px;font-family:Arial;background:#fff;color:#0F1E4D}b{color:#2D5BFF}</style><b>Inside iframe</b><p>This document can be read and captured.</p>" style="display:block;width:100%;height:120px;border:0"></iframe></div></div><div class="demo-actions"><button type="button" class="btn btn-primary" data-demo-action="png" data-demo-target="demo-iframe" data-demo-output="demo-iframe-output" data-demo-filename="iframe">Capture iframe panel</button><button type="button" class="btn btn-ghost" data-demo-action="download" data-demo-target="demo-iframe" data-demo-output="demo-iframe-output" data-demo-filename="iframe">Download panel</button></div><div class="demo-output" id="demo-iframe-output"><span class="demo-hint">The captured result will appear here.</span></div></div></section>
34+
document.body.appendChild(image)</pre></section><section class="prose"><h2>Why this pattern works</h2><p>SnapDOM reads an accessible iframe document, captures its visible content and places the result back into the parent snapshot. A cross-origin frame cannot be read, so it is drawn as a striped placeholder of the same size (the default) and a console warning names it; the parent layout never collapses around it.</p></section><section class="prose"><h2 id="live-demo">Capture a same-origin iframe</h2><p>The embedded srcdoc frame is same-origin and can be included in the parent capture.</p><div class="demo-box"><div class="demo-stage"><div id="demo-iframe" style="width:360px;padding:12px;background:#0F1E4D"><div style="color:#D9E2F7;font-size:11px;margin:0 0 8px">SAME-ORIGIN FRAME</div><iframe title="Capture demo" srcdoc="<style>body{margin:0;padding:20px;font-family:Arial;background:#fff;color:#0F1E4D}b{color:#2D5BFF}</style><b>Inside iframe</b><p>This document can be read and captured.</p>" style="display:block;width:100%;height:120px;border:0"></iframe></div></div><div class="demo-actions"><button type="button" class="btn btn-primary" data-demo-action="png" data-demo-target="demo-iframe" data-demo-output="demo-iframe-output" data-demo-filename="iframe">Capture iframe panel</button><button type="button" class="btn btn-ghost" data-demo-action="download" data-demo-target="demo-iframe" data-demo-output="demo-iframe-output" data-demo-filename="iframe">Download panel</button></div><div class="demo-output" id="demo-iframe-output"><span class="demo-hint">The captured result will appear here.</span></div></div></section>
3535
<section class="prose"><h2>Limits and common mistakes</h2><p>The same-origin policy prevents JavaScript from reading arbitrary third-party iframe documents. A client-side library cannot bypass that boundary.</p></section>
36-
<section class="prose faq"><h2 id="faq">Frequently asked questions</h2><details><summary>Can I capture a YouTube or third-party iframe?</summary><p>Not directly when it is cross-origin. The browser blocks access to its document.</p></details><details><summary>What happens to inaccessible frames?</summary><p>Enable placeholders to preserve their layout while making the unavailable content explicit.</p></details></section>
36+
<section class="prose faq"><h2 id="faq">Frequently asked questions</h2><details><summary>Can I capture a YouTube or third-party iframe?</summary><p>Not directly when it is cross-origin. The browser blocks access to its document.</p></details><details><summary>What happens to inaccessible frames?</summary><p>They become a striped placeholder the size of the frame, so the surrounding layout is unchanged and the unavailable content is explicit. That is the default; pass <code>placeholders: false</code> for an invisible spacer of the same size.</p></details></section>
3737
<section class="related"><div class="section-label">Related</div><div class="related-grid"><a class="related-card" href="../capture-full-page/">Capture a full page <span class="arrow"></span></a><a class="related-card" href="../screenshot-a-div/">Screenshot a div <span class="arrow"></span></a><a class="related-card" href="../../docs/options/">Capture options <span class="arrow"></span></a><a class="related-card" href="../capture-element-without-puppeteer/">Capture without Puppeteer <span class="arrow"></span></a></div></section>
3838
<section class="cta-section"><div class="cta-card"><h2>Capture it in the browser</h2><p>Install SnapDOM and turn the DOM your users already see into a portable image.</p><a href="../../showcase/" class="btn btn-primary">Open the demo</a><a href="https://www.npmjs.com/package/@zumer/snapdom" class="btn btn-ghost" target="_blank" rel="noopener">Install from npm</a></div></section>
3939
</main> <script defer src="/copy-code.js"></script>

src/core/clone.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -563,9 +563,15 @@ async function cloneIframe(node, sessionCache, options) {
563563
}
564564
}
565565

566-
// NEW-7: warn that this iframe was skipped so callers can react
566+
// NEW-7: warn that this iframe was skipped so callers can react. `placeholders` is on by
567+
// default, so what lands in the capture is the striped placeholder below; only mention the
568+
// opt-out, never suggest enabling an option that is already on.
567569
if (!sameOrigin) {
568-
console.warn('[snapdom] cross-origin <iframe> skipped (cannot access content). Use options.placeholders to show a placeholder instead.', node)
570+
console.warn(
571+
'[snapdom] cross-origin <iframe> skipped (its document cannot be read). Captured as a ' +
572+
'placeholder that keeps the frame\'s box; pass { placeholders: false } for an invisible spacer.',
573+
node
574+
)
569575
}
570576

571577
// Fallback actual (placeholder o spacer)

0 commit comments

Comments
 (0)