Start here
Initializing
Five entry points, two ways to configure them, and one set of precedence rules that decides who wins when you use both. Every player on this page is running the code shown beneath it.
Auto-init — no JavaScript at all
Loaded as a classic <script>, Fideo scans for [data-fideo] on
DOMContentLoaded and mounts what it finds.
<script src="https://cdn.jsdelivr.net/gh/devjtv/fideo-js@v0.7.0/dist/fideo.global.js"></script> <video data-fideo src="/media/clip.mp4" data-fideo-poster="/media/poster.jpg" data-fideo-muted data-fideo-loop ></video>
<!-- Must run before the library script --> <script> window.__fideoAutoInit = { selector: '.hero-video', muted: true, }; </script>
document.currentScript is null) and stays out of the way, so a bundled app never
gets a surprise mount pass.
initFideo() — mount everything that matches
The same scan, run on demand. Returns the mounted players plus a destroy() that tears down the
whole batch. Pass a selector to scope it; every other option becomes the default for each player
it mounts.
const result = initFideo({ selector: '[data-fideo-demo="batch"]', muted: true, loop: true, volume: 0.6, }); result.players.length; // 2 result.destroy(); // unmounts both // Same thing as a static method Fideo.init({ selector: '[data-fideo-demo="batch"]' }); // Default selector when you pass nothing initFideo(); // → [data-fideo]
WeakMap, so a
second pass skips them and returns the existing instances. Re-run it after injecting content and nothing is
double-mounted. An element that cannot mount is warned about and skipped — it does not abort the batch.
new Fideo() — one player, by selector
The class form. Takes a CSS selector or an element and returns an instance carrying the full player API.
The element does not need data-fideo — that attribute only matters to the selector-driven entry
points.
const player = new Fideo('#player-new', { muted: true, loop: true, volume: 0.5, playbackRates: [0.5, 1, 1.5, 2], }); await player.play(); await player.seek(8); player.getState(); // { currentTime, duration, paused, … }
createFideo() — the same thing, without new
A factory wrapper around the class, for codebases that avoid new or where a function reads
better. Identical arguments, identical instance. This one is handed a live element rather than a selector,
which is what you usually have inside a framework ref.
const el = document.querySelector('#player-create'); const player = createFideo(el, { muted: true, loop: true, controlVisibility: { settings: false }, });
See Framework recipes for the React, Vue and SPA versions of this.
mountFideo() — element only, never duplicates
The primitive the other entry points are built on. It takes an element — never a selector — and is idempotent: call it twice on the same element and you get the same instance back rather than a second player stacked on the first.
const el = document.querySelector('#player-mount'); const a = mountFideo(el, { muted: true }); const b = mountFideo(el, { muted: false }); a === b; // true — the second call's options are ignored
destroy() the player and mount it again.
Who wins when both are set
Anything you can set in JavaScript has a data-fideo-* equivalent, and the two mix freely on the
same element. Most options let the data attribute override JavaScript, so a page author can adjust one
player without touching the code that mounts it. Collection options merge instead, and there
JavaScript wins per key.
mountFideo(el, { autoplay: true, // attribute says "false" → attribute wins volume: 1, // attribute says "0.25" → attribute wins muted: true, // no attribute → JS applies posters: { // merged map → JS wins per key desktop: './media/poster-alt.jpg', }, });
| Option | Attribute | JavaScript | Resolved |
|---|---|---|---|
| Reading instance… | |||
| Options | Winner |
|---|---|
Scalars — autoplay, muted, loop, controls, volume, lazy, preload, viewport, background, className, and every controlVisibility flag |
Data attribute |
Maps — sources, posters, cssVars | JavaScript, per key |
provider | JavaScript |
selector, icons, disabledProviders, injectStyles | JavaScript only — no attribute exists |
background: true forces autoplay,
muted, loop and playsInline on from either source, because a
background video cannot work without them. See Background video.
Mount, destroy, mount again
destroy() unwraps the element, removes everything Fideo generated and releases the instance,
leaving markup you can mount again with different options. That is the supported way to reconfigure a player,
and what a single-page app should call when a route unmounts.
let player = mountFideo(el, { muted: true }); // Options are locked in at mount, so swap the instance: player.destroy(); player = mountFideo(el, { muted: true, cssVars: { '--fideo-accent': '#7ab8ff' }, }); // Or tear down a whole batch at once const result = initFideo(); result.destroy();
AbortControllers, observers disconnect, the
wrapper and generated nodes are removed, and the element returns to where it started.
Getting the library into your page
<script src="/dist/fideo.global.js"></script> <script> // window.Fideo, initFideo, createFideo, mountFideo Fideo.init(); </script>
import { Fideo, initFideo, createFideo, mountFideo } from 'fideo-js'; initFideo(); // nothing auto-mounts for module imports
const { initFideo } = require('fideo-js');
Picking an entry point
| Call | Target | Returns | Reach for it when |
|---|---|---|---|
| auto-init | every [data-fideo] | nothing | A static page or CMS, no build step |
| initFideo(options) | a selector | { players, destroy() } | Many players share one configuration |
| new Fideo(target, options) | selector or element | player instance | One player you hold a reference to |
| createFideo(target, options) | selector or element | player instance | Same, without new |
| mountFideo(element, options) | element only | player instance | You already have the node and want no duplicates |