@vexia/vexia-widgets-lib
v1.0.5
Published
A lightweight DOM library for embedding Vexia competition widgets
Readme
Vexia Widgets Library
A lightweight Web/DOM library for embedding Vexia competition widgets in an iframe.
Installation
pnpm add @vexia/vexia-widgets-libUsage
NPM/TypeScript
import { createCompetitionIframe } from "@vexia/vexia-widgets-lib";
const container = document.getElementById("vexia-widget");
if (container) {
createCompetitionIframe(container, "YOUR_COMPETITION_ID");
}The iframe URL is generated as:
https://vexia-widgets--vexia-widgets.europe-west4.hosted.app/competitions/{competitionId}Browser Script
<div id="vexia-widget" style="height: 600px"></div>
<script src="https://cdn.jsdelivr.net/npm/@vexia/vexia-widgets-lib@1/dist/browser.min.js"></script>
<script>
VexiaWidgets.createCompetitionIframe(
document.getElementById("vexia-widget"),
"YOUR_COMPETITION_ID"
);
</script>API
createCompetitionIframe(target, competitionId, options?)
Creates an iframe and appends it to target.
Parameters:
target(HTMLElement) - DOM element that will receive the iframe.competitionId(string) - Competition ID used in the iframe URL.options(CreateCompetitionIframeOptions, optional) - iframe customization.
Options:
title- Accessible iframe title. Defaults to"Vexia competition widget".className- CSS class to add to the iframe.height- iframe height. Defaults to"100%".dynamicHeight- update iframe height when a matchingpostMessagepayload is received. Defaults totrue.dynamicHeightEventName- message type used for resizing. Defaults to"vexiaWidgetsIframeResizer".width- iframe width. Defaults to"100%".backgroundColor- background color applied to the iframe element. Defaults to"white".fullWidth- make the iframe span the viewport width even inside a narrower parent. Defaults tofalse.allow- iframe allow policy.allowFullscreen- whether fullscreen is allowed. Defaults totrue.sectionScroll- when the host page is opened at a section hash (e.g.#classification-table), forward it into the competition page and scroll the host window to the section. Defaults totrue.sectionScrollEventName- message type used for section-scroll messages. Defaults to"vexiaWidgetsScrollTo".
addCompetitionWidget is also exported as an alias of createCompetitionIframe.
Full Width
Use fullWidth: true when the iframe should visually span the viewport even if the target element is inside a centered or narrow parent.
createCompetitionIframe(container, "YOUR_COMPETITION_ID", {
fullWidth: true
});This applies viewport-width styles to the iframe. If an ancestor has overflow: hidden, the iframe can still be clipped by that ancestor.
Dynamic Height Message
By default, the library listens for message events from the iframe and only handles messages with type: "vexiaWidgetsIframeResizer".
Add this to the page rendered inside the iframe:
const VEXIA_RESIZE_MESSAGE_TYPE = "vexiaWidgetsIframeResizer";
function sendVexiaHeight() {
const height = Math.max(
document.documentElement?.scrollHeight ?? 0,
document.body?.scrollHeight ?? 0
);
window.parent.postMessage(
{
type: VEXIA_RESIZE_MESSAGE_TYPE,
detail: {
height
}
},
"*"
);
}
function startVexiaHeightReporter() {
const root = document.documentElement;
if (!(root instanceof Node)) {
return;
}
sendVexiaHeight();
window.addEventListener("load", sendVexiaHeight);
window.addEventListener("resize", sendVexiaHeight);
if ("ResizeObserver" in window) {
new ResizeObserver(sendVexiaHeight).observe(root);
}
if (document.body instanceof Node && "MutationObserver" in window) {
new MutationObserver(sendVexiaHeight).observe(document.body, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
}
}
if (document.readyState === "loading") {
window.addEventListener("DOMContentLoaded", startVexiaHeightReporter, {
once: true
});
} else {
startVexiaHeightReporter();
}The payload must match this shape:
window.parent.postMessage(
{
type: "vexiaWidgetsIframeResizer",
detail: {
height: 720
}
},
"*"
);Section Scroll
createCompetitionIframe supports deep-linking to a section of the competition
page from another page. Because the competition iframe reports its full height
(so the host window scrolls, not the iframe), native hash scrolling can't reach
a section inside the iframe. Instead:
- Navigate the host to the competition page URL with a section hash, e.g.
https://esporte.ig.com.br/jogoajogo/brasileirao-serie-a-2026/#classification-table. - The library forwards that hash into the competition iframe
src. - The competition page measures the section offset and posts it back.
- The library scrolls the host window to
iframeTop + offset.
The page rendered inside the iframe must post this payload once its layout has
settled (an element with id matching the hash must exist):
window.parent.postMessage(
{
type: "vexiaWidgetsScrollTo",
detail: {
top: 1840 // offsetTop of the target section inside the iframe document
}
},
"*"
);Pass sectionScroll: false to opt out.
Build
pnpm install
pnpm run buildExample
An Express example is available in examples/express-example.
