An overview of the Fullscreen API

Digital software technology development concept. Coder programmer, software engineer coding computer language, javascript on laptop computer
The Fullscreen API lets you display a specific element (and its descendants) in true fullscreen and then return to windowed mode. Learn the key methods, state checks, events, permissions considerations, and UX best practices for building focused video, game, and presentation experiences.

Fullscreen experiences are most compelling when the content benefits from maximum focus: a web game, a video player, a slide deck, a data dashboard, or any interface where browser chrome and surrounding apps become distractions. The Fullscreen API is the browser feature that enables this by letting a page present a specific DOM element (and its descendants) in fullscreen and then return to normal windowed mode when appropriate.

What the API actually does

Rather than being a standalone object, the Fullscreen API adds capabilities onto existing web platform types, primarily Document and Element. In practice, that means:

This element-based model is useful because it aligns fullscreen with the thing the user cares about (a <video>, a <canvas>, a container for a presentation) without forcing everything else on the page to be treated as the fullscreen target.

Key concepts

Entering and exiting fullscreen is request-based

Going fullscreen is initiated by requesting it on an element, while leaving fullscreen is requested via the document. Both are designed as asynchronous operations (they return promises), reflecting the fact that the user agent may need to perform checks, prompt the user, or refuse the request.

State is observable via fullscreenElement

To know whether fullscreen is active—and which element is currently presented—use the document’s fullscreenElement. If it’s null, nothing is in fullscreen.

Capability can be gated with fullscreenEnabled

Not every environment will allow fullscreen. The API provides a way to check whether fullscreen engagement is even possible in the current context. This is particularly relevant for embedded contexts (e.g., iframes) or restrictive environments.

There are events for lifecycle and failure

Fullscreen transitions and failures generate dedicated events:

These events are what you use to keep UI in sync—update buttons, show hints, pause animations, or restore layout after exit.

Permissions and embedding: why fullscreen sometimes “doesn’t work”

Fullscreen isn’t just a technical capability; it’s also controlled by browser policy. The feature is governed by Permissions Policy under the name "fullscreen", with a default allowlist of "self". In plain terms: fullscreen is generally allowed for top-level pages, and for same-origin nested contexts by default—but embedding scenarios can restrict it depending on how the page is framed and which policies are applied.

If you’ve ever seen fullscreen behave inconsistently across an embedded widget versus the same page opened directly, policy controls are often the reason.

UX realities: users can (and will) exit on their own

A good fullscreen implementation assumes the user remains in control:

				
					<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Fullscreen API Demo</title>
    <link data-wphbdelayedstyle="style.css" rel="stylesheet" />
    <script type="wphb-delay-type" src="fullscreen_API.js" defer></script>
</head>
    
<body>
    <div class="wrap">
        <header>
            <h1>Fullscreen API Demo</h1>
            <p>
                Click “Enter fullscreen” (or press <kbd>Enter</kbd>) to toggle fullscreen for the stage element.
                The log will show <code>fullscreenchange</code> events and any errors.
            </p>
        </header>
    
        <div class="row">
            <section class="card">
                <!-- This is the element we request fullscreen on -->
                <div id="stage" tabindex="0" aria-label="Fullscreen stage">
                    <div class="hint">
                        <div style="font-size: 18px; font-weight: 750; margin-bottom: 8px;">
                            The Stage Element
                        </div>
                        <div style="color: var(--muted);">
                            Try toggling fullscreen. Some browsers require a user gesture (click/keypress),     and some platforms (notably iOS) have limitations.
                        </div>
                        <div class="controls">
                            <button id="toggleBtn" class="primary" type="button">Enter fullscreen</button>
                            <button id="exitBtn" type="button">Exit fullscreen</button>
                            <span class="pill" id="supportPill">Checking support…</span>
                        </div>
                    </div>
                </div>
    
                <div style="margin-top: 12px; color: var(--muted); font-size: 13px;">
                    We request fullscreen on a specific element, and the document tracks the current fullscreen element.”
                </div>
            </section>
    
            <aside class="card status">
                <div class="line">
                    <div class="label">document.fullscreenEnabled</div>
                    <div class="value" id="enabledVal">—</div>
                </div>
                <div class="line">
                    <div class="label">document.fullscreenElement</div>
                    <div class="value" id="elementVal">—</div>
                </div>
                <div class="line">
                    <div class="label">Last event</div>
                    <div class="value" id="eventVal">—</div>
                </div>
    
                <div>
                    <div style="margin: 6px 0 8px; color: var(--muted); font-weight: 650;">Event log</div>
                    <div id="log" aria-live="polite"></div>
                    <div style="margin-top: 10px; display:flex; gap: 10px; flex-wrap: wrap;">
                        <button id="clearLogBtn" type="button">Clear log</button>
                        <span class="pill">Events: fullscreenchange, fullscreenerror</span>
                    </div>
                </div>
            </aside>
        </div>
    </div>
<script type="text/javascript" id="wphb-delayed-styles-js">
			(function () {
				const events = ["keydown", "mousemove", "wheel", "touchmove", "touchstart", "touchend"];
				function wphb_load_delayed_stylesheets() {
					document.querySelectorAll("link[data-wphbdelayedstyle]").forEach(function (element) {
						element.setAttribute("href", element.getAttribute("data-wphbdelayedstyle"));
					}),
						 events.forEach(function (event) {
						  window.removeEventListener(event, wphb_load_delayed_stylesheets, { passive: true });
						});
				}
			   events.forEach(function (event) {
				window.addEventListener(event, wphb_load_delayed_stylesheets, { passive: true });
			  });
			})();
		</script></body>

</html>
				
			
				
					:root {
    --bg: #0b1220;
    --panel: #111a2e;
    --text: #e8eefc;
    --muted: #a9b5d6;
    --accent: #3e8c60;
    --danger: #ff5a67;
    --border: rgba(255, 255, 255, 0.12);
}

body {
    margin: 0;
    font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
    color: var(--text);
    background: radial-gradient(1200px 700px at 20% 10%, #16254a, var(--bg));
    }

    .wrap {
        max-width: 980px;
        margin: 0 auto;
        padding: 28px 16px 40px;
        display: grid;
        gap: 16px;
    }

    header h1 {
        margin: 0 0 6px; font-size: 22px;
    }

    header p {
        margin: 0; color: var(--muted); line-height: 1.4;
    }

    .row {
        display: grid;
        grid-template-columns: 1.2fr 0.8fr;
        gap: 16px;
    }

    @media (max-width: 860px) {
        .row {
            grid-template-columns: 1fr;
        }
    }

    .card {
        background: color-mix(in srgb, var(--panel) 88%, transparent);
        border: 1px solid var(--border);
        border-radius: 14px;
        padding: 14px;
        box-shadow: 0 12px 30px rgba(0, 0, 0, 0.25);
    }

    /* This is the element we’ll toggle into fullscreen */
    #stage {
        border-radius: 12px;
        border: 1px solid var(--border);
        overflow: hidden;
        min-height: 320px;
        display: grid;
        place-items: center;
        position: relative;

        /* Visually interesting, but simple (no external assets needed) */
        background: radial-gradient(600px 240px at 30% 25%, rgba(62, 140, 96, 0.35), transparent),
        radial-gradient(500px 260px at 80% 70%, rgba(120, 180, 255, 0.25), transparent),
        linear-gradient(135deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0.02));
    }

    #stage .hint {
        text-align: center;
        padding: 18px;
        max-width: 52ch;
    }

    #stage kbd {
        font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
        padding: 2px 8px;
        border-radius: 8px;
        border: 1px solid var(--border);
        background: rgba(0, 0, 0, 0.25);
    }

    .controls {
        display: flex;
        flex-wrap: wrap;
        gap: 10px;
        align-items: center;
        margin-top: 12px;
    }

    button {
        appearance: none;
        border: 1px solid var(--border);
        border-radius: 12px;
        padding: 10px 12px;
        font-weight: 650;
        color: var(--text);
        background: rgba(255, 255, 255, 0.06);
        cursor: pointer;
    }

    button.primary {
        background: color-mix(in srgb, var(--accent) 40%, rgba(255, 255, 255, 0.06));
        border-color: color-mix(in srgb, var(--accent) 60%, var(--border));
    }

    button:disabled {
        opacity: 0.55;
        cursor: not-allowed;
    }

    .pill {
        font-size: 12px;
        color: var(--muted);
        border: 1px solid var(--border);
        border-radius: 999px;
        padding: 6px 10px;
        background: rgba(0, 0, 0, 0.18);
    }

    .status {
        display: grid;
        gap: 10px;
    }

    .status .line {
        display: flex;
        justify-content: space-between;
        gap: 10px;
        border-bottom: 1px dashed rgba(255, 255, 255, 0.14);
        padding: 8px 0;
    }

    .status .label {
        color: var(--muted);
    }

    .status .value {
        font-weight: 650;
    }

    #log {
        height: 190px;
        overflow: auto;
        font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
        font-size: 12px;
        line-height: 1.35;
        padding: 10px;
        border-radius: 12px;
        border: 1px solid var(--border);
        background: rgba(0, 0, 0, 0.28);
        white-space: pre-wrap;
    }

    .error {
        color: color-mix(in srgb, var(--danger) 82%, white);
        font-weight: 650;
    }

    /* Optional: show a visible outline while in fullscreen */
    :fullscreen #stage {
        outline: 6px solid rgba(62, 140, 96, 0.55);
        outline-offset: -6px;
        border-radius: 0;
        /* many demos remove rounding in fullscreen */
    }
				
			
				
					/**
 * Fullscreen API quick demo
 *
 * - Element.requestFullscreen() to enter fullscreen (user gesture required in most browsers)
 * - Document.exitFullscreen() to leave fullscreen
 * - document.fullscreenElement tells you *which* element is fullscreen right now
 * - fullscreenchange fires on document when entering/exiting succeeds
 * - fullscreenerror fires when it fails (permissions, platform limitations, etc.)
 */

const stage = document.querySelector('#stage');
const toggleBtn = document.querySelector('#toggleBtn');
const exitBtn = document.querySelector('#exitBtn');
const clearLogBtn = document.querySelector('#clearLogBtn');
const enabledVal = document.querySelector('#enabledVal');
const elementVal = document.querySelector('#elementVal');
const eventVal = document.querySelector('#eventVal');
const logEl = document.querySelector('#log');
const supportPill = document.querySelector('#supportPill');

const timeStamp = () => {
	return new Date().toLocaleTimeString();
};

const log = (message, { type = 'info' } = {}) => {
	const prefix = `[${timeStamp()}] `;
	const line = prefix + message;

	const div = document.createElement('div');
	div.textContent = line;

	if (type === 'error') div.className = 'error';

	logEl.appendChild(div);
	logEl.scrollTop = logEl.scrollHeight;
};

const updateUI = (lastEvent = '—') => {
	enabledVal.textContent = String(document.fullscreenEnabled);
	elementVal.textContent = document.fullscreenElement
		? `#${document.fullscreenElement.id || document.fullscreenElement.tagName.toLowerCase()}`
		: 'null';
	eventVal.textContent = lastEvent;

	const isFullscreen = Boolean(document.fullscreenElement);

	toggleBtn.textContent = isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen';

	// If the browser indicates fullscreen is not enabled, disable the controls.
	// (Note: Some browsers may still allow fullscreen in certain contexts even if this is false,
	// but generally it’s a good signal.)
	const supported =
		typeof stage.requestFullscreen === 'function' &&
		typeof document.exitFullscreen === 'function';

	toggleBtn.disabled = !supported;
	exitBtn.disabled = !supported || !isFullscreen;
};

const detectSupport = () => {
	const supported =
		typeof stage.requestFullscreen === 'function' &&
		typeof document.exitFullscreen === 'function';

	if (!supported) {
		supportPill.textContent = 'Fullscreen API not supported here';
		supportPill.style.borderColor = 'rgba(255,90,103,0.55)';
		log('Fullscreen API not supported in this browser/context.', {
			type: 'error'
		});
	} else {
		supportPill.textContent = 'Fullscreen API supported';
		supportPill.style.borderColor = 'rgba(62,140,96,0.55)';
		log('Fullscreen API supported. Try clicking the button or pressing Enter.');
	}
};

// Toggle function that handles both enter and exit paths.
const toggleFullscreen = async () => {
	try {
		// If something is already fullscreen, exit.
		if (document.fullscreenElement) {
			await document.exitFullscreen();
			// UI will also update via fullscreenchange, but we can log intent here.
			log('Requested exitFullscreen()');
			return;
		}

		// Otherwise, request fullscreen on our stage element.
		// navigationUI is optional; supported in some browsers.
		// Remove the options object if you want maximum compatibility.
		await stage.requestFullscreen({ navigationUI: 'hide' });
		log('Requested stage.requestFullscreen()');
	} catch (err) {
		// Common reasons:
		// - Not initiated by a user gesture (click/keypress)
		// - Platform limitation (e.g., iOS Safari)
		// - Browser policy / permissions
		log(`Fullscreen request failed: ${err.name}: ${err.message}`, {
			type: 'error'
		});
		updateUI('error');
	}
};

// --- Event listeners ---

document.addEventListener('fullscreenchange', () => {
	log(
		`fullscreenchange → fullscreenElement is now ${document.fullscreenElement ? 'SET' : 'null'}`
	);
	updateUI('fullscreenchange');
});

document.addEventListener('fullscreenerror', () => {
	// Some browsers provide little/no extra info; still useful to surface.
	log('fullscreenerror → The browser reported a fullscreen error event.', {
		type: 'error'
	});
	updateUI('fullscreenerror');
});

// Click controls
toggleBtn.addEventListener('click', toggleFullscreen);
exitBtn.addEventListener('click', async () => {
	try {
		if (document.fullscreenElement) {
			await document.exitFullscreen();
			log('Requested exitFullscreen() via Exit button');
		}
	} catch (err) {
		log(`Exit failed: ${err.name}: ${err.message}`, { type: 'error' });
	}
});

clearLogBtn.addEventListener('click', () => {
	logEl.textContent = '';
	log('Log cleared.');
	updateUI('cleared');
});

// Keyboard demo: press Enter to toggle fullscreen
window.addEventListener('keydown', (e) => {
	if (e.key === 'Enter') {
		// Prevent accidental form submission if you embed this in a bigger page
		e.preventDefault();
		toggleFullscreen();
	}

	// Optional: let Escape exit fullscreen (most browsers already do this by default)
	if (e.key === 'Escape' && document.fullscreenElement) {
		// Let the browser handle it; we just log user intent.
		log('Escape pressed (browser typically exits fullscreen automatically).');
	}
});

// Initialize
detectSupport();
updateUI('init');

// Give focus to stage for natural keyboard interactions
stage.focus();

				
			

When the Fullscreen API shines

Use fullscreen when it meaningfully improves the experience:

The best fullscreen experiences feel intentional: a clear entry point, clear exit instructions, and UI that stays coherent whether fullscreen is granted, denied, or ended unexpectedly.

Share the Post:

Related Posts

small_c_popup.png

Need help?

Let's have a chat...


Login

Jump Back In!

Here at Webolution Designs, we love to learn. This includes sharing things we have learned with you. 

Register

Begin Your Learning Journey Today!

Come back inside to continue your learning journey.