Web plugins for @adelv/adelv. Banner, native, video, and audio rendering; MRC viewability; click detection; and JavaScript tracker injection.
See @adelv/gpt for Google Publisher Tag integration.
npm install @adelv/adelv @adelv/webRenders display ads. Two shapes are supported:
- Markup —
ad.display.admrendered in a sandboxed iframe. - Structured image —
ad.display.banner.imgrendered as an<img>, wrapped in an<a href>whenad.display.banner.link.urlis set.
import { createDelivery } from "@adelv/adelv"
import { banner } from "@adelv/web"
// Default usage
const delivery = createDelivery(document.getElementById("ad-slot")!)
delivery.use(banner())
delivery.deliver({ ad, purl, burl })
// With allow-same-origin for postMessage-based creatives
delivery.use(banner({
sandbox: ["allow-scripts", "allow-same-origin", "allow-popups"],
}))
// Custom iframe attributes and style
delivery.use(banner({
attrs: { loading: "lazy" },
style: { width: "100%", height: "auto" },
}))
// Remove sandbox entirely (not recommended)
delivery.use(banner({ sandbox: null }))Options:
| Option | Type | Default | Description |
|---|---|---|---|
sandbox |
string[] | null |
["allow-scripts", "allow-popups", "allow-popups-to-escape-sandbox"] |
iframe sandbox tokens. null removes sandbox. |
attrs |
Record<string, string> |
— | Additional iframe attributes. |
style |
Partial<CSSStyleDeclaration> |
{ border: "none" } |
CSS styles. Merged with defaults. |
pending→adm→ iframesrcdoc; otherwisebanner.img→<img>(+<a>if linked)onload→ transitions torendered;onerror→ transitions toerror- Cleanup removes the element from DOM
MRC viewability measurement using IntersectionObserver.
import { viewability } from "@adelv/web"
delivery.use(viewability())
// Measure multiple MRC standards
delivery.use(viewability({
standards: ["mrc50", "mrc100", "video50"],
}))| Standard | Criteria | AdCOM EventType |
|---|---|---|
mrc50 (default) |
50% visible for 1s continuous | VIEWABLE_MRC_50 |
mrc100 |
100% visible for 1s continuous | VIEWABLE_MRC_100 |
video50 |
50% visible for 2s continuous | VIEWABLE_VIDEO_50 |
- Starts observing after
rendered - Emits a
viewableevent carrying the metstandard; each standard fires once - Core maps the standard to its
EventTypeand deduplicates per standard
Native ad renderer. Delegates DOM construction to a user-provided render function.
import { native } from "@adelv/web"
import { NativeDataAssetType } from "iab-adcom/enum"
delivery.use(native({
render(target, ad) {
const assets = ad.display!.native!.asset ?? []
const link = ad.display!.native!.link
const title = assets.find(a => a.title)?.title?.text ?? ""
const img = assets.find(a => a.image)?.image?.url ?? ""
const desc = assets.find(a => a.data?.type === NativeDataAssetType.DESCRIPTION)?.data?.value ?? ""
const sponsor = assets.find(a => a.data?.type === NativeDataAssetType.SPONSORED)?.data?.value ?? ""
target.innerHTML = `
<a href="${link?.url ?? "#"}" class="native-ad">
<img src="${img}" alt="${title}" />
<h3>${title}</h3>
<p>${desc}</p>
<span class="sponsor">${sponsor}</span>
</a>
`
return () => { target.innerHTML = "" }
},
}))- Skips if
ad.display?.nativeis not present renderfunction receives(target: HTMLElement, ad: Ad)- Optionally return a cleanup function. Default cleanup:
target.innerHTML = "" - Errors in
rendertransition toerrorstate
Video (ad.video, VAST) and audio (ad.audio, DAAST) renderers. Like native(),
they delegate to a user render function — playback and in-stream tracking are the
external player's responsibility (the VAST/DAAST document carries its own trackers,
so these media have no AdCOM event[]).
import { video } from "@adelv/web"
delivery.use(video({
render(target, ad) {
const player = mountVastPlayer(target, ad.video!.adm!)
return () => player.destroy()
},
}))- Skips if
ad.video(orad.audio) is not present - Same lifecycle and cleanup semantics as
native() burlstill fires onrendered; impression/viewability event[] do not apply
Injects EventTrackingMethod.JAVASCRIPT trackers from ad.display.event as
<script> tags. Core fires IMAGE_PIXEL trackers as beacons; this plugin
handles the JavaScript method.
import { jsTracker } from "@adelv/web"
delivery.use(jsTracker()) // inject into the target element (default)
delivery.use(jsTracker({ mount: "head" })) // or into document.head- On
impression(rendered): injectsLOADED+IMPRESSIONJS trackers - On
viewable: injects the JS tracker for the met standard'sEventType - Cleanup removes all injected scripts
Click detection for target elements and iframe focus.
import { click } from "@adelv/web"
delivery.use(click())- Direct clicks: Listens for
clickevents on the target element (native ads) - Iframe clicks: Detects via
window.blur+document.activeElementheuristic (best effort) - Landing URL is extracted from
ad.display.banner.link.urlorad.display.native.link.url - Starts after
rendered
Register rendering plugin first, then measurement plugins:
delivery.use(banner()) // rendering plugin
delivery.use(viewability()) // measurement plugin
delivery.use(click()) // measurement plugin
delivery.use(jsTracker()) // measurement pluginOnly one rendering plugin per delivery (banner(), native(), video(), audio(), or gpt()). Multiple measurement plugins (viewability(), click(), jsTracker()) are fine.
MIT