Skip to content

Commit a9f8fdf

Browse files
committed
Fix banner ads: isolate each slot in a sandboxed iframe
Only the top banner and social bar were rendering. Root cause: the Adsterra banner tag shares a single global window.atOptions object, and the first async invoke.js execution consumes and deletes it, so the other slots injected with the same code render nothing. Every slot now renders inside its own sandboxed iframe (opaque origin, same mechanism the native banner already used successfully), giving each ad an isolated window/atOptions scope. Also made the auto-height probe poll for ~6s so asynchronously loading banner creatives get an accurate frame height.
1 parent fb4e3f9 commit a9f8fdf

1 file changed

Lines changed: 11 additions & 29 deletions

File tree

‎src/components/ads/ad-loader.ts‎

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,16 @@
11
import { sizeForWidth, WIDE_BREAKPOINT } from '@/config/ads';
22

3-
function recreateScripts(wrap: HTMLElement): void {
4-
for (const old of Array.from(wrap.querySelectorAll('script'))) {
5-
const s = document.createElement('script');
6-
if (old.type) s.type = old.type;
7-
if (old.src) {
8-
s.async = true;
9-
s.src = old.src;
10-
} else {
11-
s.text = old.text;
12-
}
13-
old.replaceWith(s);
14-
}
15-
}
16-
17-
function injectCode(container: HTMLElement, html: string): void {
18-
const target = container.querySelector<HTMLElement>('.ad-code');
19-
if (!target) return;
20-
target.replaceChildren();
21-
const wrap = document.createElement('div');
22-
wrap.className = 'ad-inner';
23-
wrap.innerHTML = html;
24-
recreateScripts(wrap);
25-
target.appendChild(wrap);
26-
}
27-
283
let adUid = 0;
294

30-
/** Renders ad HTML inside a sandboxed iframe (no top-navigation, opaque origin) with auto-height via postMessage. */
5+
/**
6+
* Renders ad HTML inside a sandboxed iframe (opaque origin, no top-navigation)
7+
* with auto-height via postMessage.
8+
*
9+
* Every slot is sandboxed on purpose: the Adsterra banner tag relies on a single
10+
* global `window.atOptions` object, and the first `invoke.js` to execute consumes
11+
* and deletes it. Running each ad in its own iframe gives it an isolated window,
12+
* so multiple banner units on the same page can render independently.
13+
*/
3114
function injectSandboxed(container: HTMLElement, html: string): void {
3215
const target = container.querySelector<HTMLElement>('.ad-code');
3316
if (!target) return;
@@ -43,7 +26,7 @@ function injectSandboxed(container: HTMLElement, html: string): void {
4326
const resizeProbe =
4427
'(function(){var s=function(){try{parent.postMessage({nodehuntAdHeight:document.body.scrollHeight,uid:"' +
4528
uid +
46-
'"},"*")}catch(e){}};window.addEventListener("load",s);setTimeout(s,300);window.addEventListener("resize",s);})();';
29+
'"},"*")}catch(e){}};window.addEventListener("load",s);window.addEventListener("resize",s);s();var i=0,t=setInterval(function(){s();if(++i>12)clearInterval(t)},500);})();';
4730
frame.srcdoc =
4831
'<!doctype html><html><head><base target="_blank"></head><body style="margin:0;background:transparent">' +
4932
html +
@@ -94,8 +77,7 @@ function activate(slot: HTMLElement): void {
9477
if (slot.dataset.activeSize === size) return;
9578
slot.dataset.activeSize = size;
9679
slot.classList.remove('ad-empty');
97-
if (slot.dataset.adSandbox && size === 'native') injectSandboxed(slot, tpl.innerHTML);
98-
else injectCode(slot, tpl.innerHTML);
80+
injectSandboxed(slot, tpl.innerHTML);
9981
}
10082

10183
function runDeferredSocialbar(): void {

0 commit comments

Comments
 (0)