Day 17: Load YouTube Videos on Click
Build a reusable YouTube embed component that displays a thumbnail by default and loads the video iframe only when the user clicks, using a data attribute to define the video ID.
JavaScript focus
- reading data-* attributes
- building dynamic URLs
- creating elements (createElement)
- setting attributes
- replacing DOM nodes
- handling click events
- preventing duplicate actions
Nice extras
- add a play button overlay
- include accessible text (“Play video”)
- support keyboard activation (Enter / Space)
- fade transition between thumbnail and iframe
- support aspect ratio container (so layout doesn't jump)
- optionally pause other videos when one is opened (advanced, optional)
MDN prep
Load YouTube Videos on Click
The HTML
<figure class="figure yt_video" data-video="qy_ctSLSuf4">
<div class="yt_video-container">
<button class="play_button" aria-label="Play Video">
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"><path d="M133 440a35.37 35.37 0 01-17.5-4.67c-12-6.8-19.46-20-19.46-34.33V111c0-14.37 7.46-27.53 19.46-34.33a35.13 35.13 0 0135.77.45l247.85 148.36a36 36 0 010 61l-247.89 148.4A35.5 35.5 0 01133 440z"/></svg>
</button>
<img class="yt_thumb" src="../images/heidi-the-sheltie-looking-out-window.jpg" alt="Heidi the Sheltie looking out the window.">
</div>
<figcaption class="figcaption">Responsive Navbar Build (HTML CSS JavaScript) No Talking Time Lapse</figcaption>
</figure>
The JavaScript
function initLoadVideo() {
const root = document.querySelector(".yt_video");
if (!root) return;
// if multiple videos per page
// use querySelectorAll + forEach through the video containers
const videoContainer = root.querySelector(".yt_video-container");
// get the video id from the data-video attribute
const videoId = root.dataset.video;
// if no figcaption, just skip it
const figCap = root.querySelector("figcaption");
let figCapText;
if (figCap) {
figCapText = figCap.textContent;
}
// on page load, load the YouTube thumbnail
// and replace the fallback image + alt text
const thumb = root.querySelector(".yt_thumb");
thumb.src = `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`;
if (figCap) {
thumb.alt = figCapText;
} else {
thumb.alt = "YouTube thumbnail image.";
}
// get the play button
const playButton = root.querySelector(".play_button");
// when clicking the play button...
playButton.addEventListener("click", () => {
// create the iframe + set attributes
const iframe = document.createElement("iframe");
iframe.classList.add("yt_iframe");
iframe.src = `https://www.youtube.com/embed/${videoId}?autoplay=1`;
if (figCap) {
iframe.title = figCapText;
} else {
iframe.title = "YouTube video player";
}
iframe.allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share";
iframe.referrerPolicy = "strict-origin-when-cross-origin";
iframe.allowFullscreen = true;
// append the iframe to the container
videoContainer.append(iframe);
// add class to thumbnail for transition
thumb.classList.add("yt_fade");
// remove play button immediately to prevent extra clicks
playButton.remove();
setTimeout(() => {
// remove the thumbnail after 750ms
thumb.remove();
}, 750);
});
}
initLoadVideo();