Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use a <video> element inside a preview container, start muted inline playback on pointer entry, and pause it on exit. Handle the play() Promise because autoplay may be refused, and keep a poster plus an explicit Play button for touch and keyboard users.

Basic hover preview

This pattern previews a clip while a pointer is over a tile and resets it when the pointer leaves. The poster remains visible while the first frame loads or if playback is blocked.

<div class="video-preview" tabindex="0" aria-label="Preview video">
  <video muted playsinline preload="metadata" poster="preview.jpg">
    <source src="preview.mp4" type="video/mp4">
  </video>
  <button type="button" class="play-button">Play preview</button>
</div>
const preview = document.querySelector(".video-preview");
const video = preview.querySelector("video");

function startPreview() {
  video.muted = true;
  const playback = video.play();
  if (playback) {
    playback.catch(() => {
      // Keep the poster and explicit play control available.
    });
  }
}

function stopPreview() {
  video.pause();
  video.currentTime = 0; // Remove this line to resume at the same point.
}

preview.addEventListener("pointerenter", startPreview);
preview.addEventListener("pointerleave", stopPreview);
preview.addEventListener("focusin", startPreview);
preview.addEventListener("focusout", stopPreview);

The pointerenter and pointerleave events cover a mouse or pen without firing repeatedly as the pointer moves between children. Focus events add a keyboard-oriented preview. This is an illustrative implementation pattern, not a guarantee that every browser will autoplay.

Make the control usable on every input

Keep an explicit Play button

A phone does not provide a hover state. The button must start playback on tap and remain usable with Enter or Space. Give the video native controls when users need seeking, volume, pause, or resume.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const playButton = preview.querySelector(".play-button");

playButton.addEventListener("click", async () => {
  video.muted = false; // A user gesture may permit sound.
  try {
    await video.play();
  } catch (error) {
    // Show a message or leave the poster in place.
    console.warn("Playback could not start", error);
  }
});

If the preview should always be silent, leave video.muted = true in the click handler and provide a separate, clearly labelled route to full playback with sound. Do not hide the only way to operate the media behind hover.

Prevent focus from stopping too early

When focus moves from the container to its button, a naïve focusout handler can pause the video even though focus remains inside the tile. Test the new focus target before stopping.

preview.addEventListener("focusout", (event) => {
  if (!preview.contains(event.relatedTarget)) {
    stopPreview();
  }
});

Use a similar containment check if your component has links, menus, or other interactive descendants.

Autoplay rules and muted playback

Calling video.play() returns a Promise in modern browsers. The Promise can reject when an autoplay policy, permission, unsupported source, or loading problem prevents playback. Always catch the rejection and preserve a usable poster and control.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Muted or silent media is less likely to be blocked. MDN notes that autoplay blocking does not apply to video without an audio track or with muted audio (autoplay guide). Set the property before calling play(); the markup Boolean attribute is also important:

<video muted playsinline ...>

Boolean attributes do not work like strings: muted="false" still means that muted is present, so the element remains muted. playsinline asks the browser to keep playback in the element instead of switching to a full-screen presentation. Safari’s autoplay examples use it, but it is not a promise that every mobile browser will play automatically.

Choose loading and reset behavior

Preload choices

preload is a request, not a command, and autoplay can take precedence. MDN defines the values as follows (video reference):

Setting Request Use it when
metadata Fetch duration, dimensions, and other metadata. You have a moderate number of tiles and want a quick start without asking for the whole file.
none Do not preload the media. There are many off-screen previews or bandwidth is more important than instant hover response.
auto Allow the browser to download the whole file. A small number of short clips justify the extra data.

Start with posters and preload="metadata". For long lists, defer distant sources until a tile nears the viewport. Lazy loading can delay media work near the viewport, but exact timing depends on browser, network, and the asset.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Restart or resume

Use video.pause() on exit in either design. Add video.currentTime = 0 when every hover should begin at the beginning. Omit the reset when returning visitors should resume where they stopped. Resetting can require the media to be seekable; guard unusual sources if necessary:

function stopPreview() {
  video.pause();
  if (video.readyState >= 1) video.currentTime = 0;
}

Multiple preview tiles

Give each tile its own video and bind events with querySelectorAll. Stop a previous tile before starting a new one so several videos do not play simultaneously.

let activeVideo = null;

document.querySelectorAll(".video-preview").forEach((preview) => {
  const video = preview.querySelector("video");
  const start = () => {
    if (activeVideo && activeVideo !== video) {
      activeVideo.pause();
      activeVideo.currentTime = 0;
    }
    activeVideo = video;
    video.muted = true;
    const request = video.play();
    request?.catch(() => {});
  };
  const stop = () => {
    video.pause();
    video.currentTime = 0;
    if (activeVideo === video) activeVideo = null;
  };
  preview.addEventListener("pointerenter", start);
  preview.addEventListener("pointerleave", stop);
  preview.addEventListener("focusin", start);
  preview.addEventListener("focusout", (event) => {
    if (!preview.contains(event.relatedTarget)) stop();
  });
});

If your design allows several simultaneous previews, remove the active-video arbitration but consider CPU, decoding, and network usage.

Visual and semantic details

  • Keep a meaningful poster that communicates what the clip contains.
  • Provide a text label or accessible name for the preview region.
  • Do not rely on motion alone to convey status; show a visible Play/Pause state.
  • Respect reduced-motion preferences if the preview is decorative or especially distracting.
  • Keep full playback controls available somewhere; native controls include play, seek, volume, pause, and resume.
  • Use a supported source and an appropriate MIME type. You can provide more than one <source> format when browser coverage requires it.

Diagnose a preview that does not play

The Promise rejects immediately

Confirm that the video is muted before play(), that playsinline is present, and that the browser has not blocked autoplay. Leave the poster and button visible rather than retrying in a loop. A direct user click is more likely to permit playback.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Only a blank rectangle appears

Check the poster URL, source URL, server response, and MIME type. Listen for media events while debugging:

["loadedmetadata", "loadeddata", "canplay", "playing", "error"].forEach((name) => {
  video.addEventListener(name, () => console.log(name, video.error));
});

loadedmetadata means basic metadata arrived; canplay indicates the browser estimates it can begin. An error event points to a source or decoding failure.

It works with a mouse but not on a phone

That is expected: touch has no hover state. Test the visible button with a tap, and test keyboard focus with Tab, Enter, and Space. Do not make touch users discover a hidden gesture.

Rank #4
HTML5 For Dummies Quick Reference
  • Used Book in Good Condition

It pauses when moving to a button

Use the relatedTarget containment check shown above. The pause should occur only when focus leaves the entire preview, not when it moves between descendants.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Many tiles consume too much data

Use posters, preload="metadata" or none, and defer sources for tiles outside the viewport. Consider assigning the source only after interaction:

const video = preview.querySelector("video");
video.dataset.src = "preview.mp4";

function loadOnDemand() {
  if (!video.src) {
    video.src = video.dataset.src;
    video.load();
  }
  video.muted = true;
  video.play().catch(() => {});
}
preview.addEventListener("pointerenter", loadOnDemand, { once: true });

On-demand loading reduces initial bandwidth but adds delay before the first moving frame. Choose based on tile count, clip size, and connection quality rather than assuming one setting is best.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Option: capture a page instead of running browser automation

Or skip the browser setup

If your goal is to capture a rendered page or document that contains the preview, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP, or PDF. Its cleaner removes cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, with the result identified by X-Page-Verdict and X-Billed headers. The MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

See the full parameter list in the ScreenshotNeo documentation. This cURL request captures the rendered page:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

There is a free allowance of 1,000 screenshots each month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan. Create a free ScreenshotNeo account.

Best Value
Sale
Beginning HTML5 and CSS3 For Dummies
  • Used Book in Good Condition

Cost, reliability, and security considerations

  • Short, compressed preview files reduce transfer and decode cost; posters should be optimized separately.
  • Do not assume preload is enforced. Measure requests in browser developer tools on the browsers and networks you support.
  • Resetting every tile on exit can trigger extra seeks. If that is costly for your media, leave the playhead where it is or use a short preview asset.
  • Serve media over HTTPS when the page is HTTPS, and configure range requests and caching on your media host for responsive seeking.
  • Do not autoplay sound on pointer entry. It is surprising, inaccessible for many users, and more likely to be blocked.

Frequently Asked Questions

Can CSS alone play a video on hover?

No. CSS can style a hover state, but starting and stopping media requires JavaScript or a user-operated native control.

Should I use mouseenter instead of pointerenter?

Pointer events cover mouse and pen with one API. They still do not create a hover interaction on touch, so keep an explicit tap action.

Why does muted=”false” still mute the video?

muted is a Boolean HTML attribute. If it is present, including as muted=”false”, the video is muted; change the DOM property with JavaScript or remove the attribute.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Is autoplay guaranteed for muted previews?

No. Muted or silent media is less likely to be blocked, but browser, device, embedded-webview, and user settings differ. Catch play() failures and retain the fallback.

Quick Recap

SaleBestseller No. 2
Bestseller No. 4
HTML5 For Dummies Quick Reference
HTML5 For Dummies Quick Reference
Used Book in Good Condition
$5.67
SaleBestseller No. 5
Beginning HTML5 and CSS3 For Dummies
Beginning HTML5 and CSS3 For Dummies
Used Book in Good Condition
$23.56

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.