In the previous tutorial “How to “Lazy Load” Embedded YouTube Videos” we looked into how to load a Youtube video only when a user clicks it. This in turn helps our web page load a bit faster, especially if there it contains multiple Youtube videos.
If you followed the tutorial thoroughly, you’ll have seen that we had to add a couple of div
elements, along with the classes, the styles, and the scripts to get it working.
Having to add all this each time is not the most convenient approach; what if we could replace all this by instead using an out-of-the-box element, like <youtube-embed>
instead? That’s exactly what we are going to do in this tutorial; we are going to create a fully-functioning custom HTML element using “Web Components”. Check out the demo, then dive in!

A Quick Primer
Before getting your hands dirty, however, I highly encourage you look into one of Kezz Bracey’s fantastic tutorials, How to Create Your Own HTML Elements With Web Components. This will give you an insight into Web Components, and things like “Shadow DOM” (also known as “Shadow Root”), HTML Imports, and the <template>
element.
Getting Started
To begin with, we need to create a new HTML file. We’ll name it “youtube-embed.html”. This file will contain all the code to register and build our new element, <youtube-embed>
.
It will include the following JavaScript, so let’s take a look at the bare bones:
( function( window, document, undefined ) { // (1) var thatDoc = document; var thisDoc = ( thatDoc._currentScript || thatDoc.currentScript ).ownerDocument; // (2) var template = thisDoc.querySelector( 'template' ).content; // (3) var YoutubeProto = Object.create( HTMLElement.prototype ); // (4) YoutubeProto.createdCallback = function() { // (5) var shadowRoot = this.createShadowRoot(); var clone = thatDoc.importNode(template, true); shadowRoot.appendChild( clone ); // Add custom code here... }; // (6) window.youtubeEmbed = thatDoc.registerElement( 'youtube-embed', { prototype : YoutubeProto }); })( window, document );
Quite a few things, but numbered logically, so let’s take a look at what it does:
- Here we define two variables referring to two different “document” objects. The first variable,
thatDoc
, refers to the main document where we deploy the custom element. The second variable,thisDoc
, is the document where we register our new HTML element, in this case, isyoutube-embed.html
. - Next we define a variable to store the
<template>
element content (we will get into this matter shortly). - We then create a new object based on the
HTMLElement
object. This will allow our new element to inherit methods and properties of any HTML element likeid
,className
,clientHeith
,scrollTop
, andchildeNodes
. createdCallback
is the function which will be immediately instantiated when the new element is created.- Within that callback function we create the “Shadow DOM” which determines the shape of our custom element,
<youtube-embed>
, in the browser. We will also start writing a custom function in here. - Finally we register our custom element in order for the browser to recognize it.
Importing HTML
Next, within the main document, where our videos will be embedded, we import the youtube-embed.html
.
<script src="webcomponents.min.js"></script><link rel="import" href="youtube-embed.html">
Web Components Polyfill
Web Components are a series of web technologies (Template, HTML Import, Custom Element, and Shadow DOM) put together. Some browsers like Opera and Chrome already support these features, but Firefox, Edge, and Safari have their own on views on supporting them, in that they only support them partially or not at all.
So, if you want your element to be applicable in a wide range of browsers (of course you do), you also need to load the Web Components polyfill.
<script src="webcomponents.min.js"></script>
Once we have done all these things and put the files in their place, we are now ready to add the other snippets of code to make our custom element come alive.
Bring the Custom Element to Life
To begin with, in “youtube-embed.html”, we add the <template>
element. Then we nest the div
and the styles that we created in our previous tutorial within it.
<template><style> .youtube { background-color: #000; margin-bottom: 30px; position: relative; cursor: pointer; padding-top: 56.25%; } .youtube img { width: 100%; top: 0; left: 0; opacity: 0.7; } .youtube .play-button { width: 90px; height: 60px; background-color: #333; box-shadow: 0 0 30px rgba( 0,0,0,0.6 ); z-index: 1; opacity: 0.8; border-radius: 6px; } .youtube .play-button:before { content: ""; border-style: solid; border-width: 15px 0 15px 26.0px; border-color: transparent transparent transparent #fff; } .youtube img, .youtube .play-button { cursor: pointer; } .youtube img, .youtube iframe, .youtube .play-button, .youtube .play-button:before { position: absolute; } .youtube .play-button, .youtube .play-button:before { top: 50%; left: 50%; transform: translate3d( -50%, -50%, 0 ); } .youtube iframe { height: 100%; width: 100%; top: 0; left: 0; }</style><div class="youtube"><div class="play-button"></div></div></template>
At this point, if we deploy our <youtube-embed>
element and inspect it with Chrome DevTools, we will find the div
elements and the styles we have just added now appear beneath the custom element Shadow DOM.

Selecting a Shadow DOM Element
Going back to our JavaScript, we need to add the following code to select the video wrapper element from the Shadow DOM. Notice that we use querySelector()
from our shadowRoot
variable; this is the element where we will append the Youtube iframe later on.
YoutubeProto.createdCallback = function() { ... var video = shadowRoot.querySelector( ".youtube" ); // Select the video wrapper };
Custom Attribute
In our previous tutorial, we used the data-embed
attribute to pass the Youtube video ID. As a reminder, the ID is used to fetch the video image thumbnail and for pointing to the correct embedding URL of the video.
In the case of Web Components, a custom named attribute is acceptable. In this case, we can, for example, introduce an embed
attribute.
<youtube-embed embed="AqcjdkPMPJA">
Then within the createdCallback
function we need to add the following to get the embed
attribute value.
YoutubeProto.createdCallback = function() { ... var video = shadowRoot.querySelector( ".youtube" ); // Select the video wrapper. var embed = this.getAttribute( "embed" ); // Get the embed attribute value. };
We will pass these two variables to our custom function.
Do the Thing
Maybe my head’s full, but I can’t think of a proper name for the function, hence doTheThing
.
YoutubeProto.createdCallback = function() { ... var embed = this.getAttribute( "embed" ); var video = shadowRoot.querySelector( ".youtube" ); this.doTheThing( embed, video ); }; YoutubeProto.doTheThing = function( embedID, videoElem ) { var source = "https://img.youtube.com/vi/"+ embedID +"/sddefault.jpg"; var image = new Image(); image.src = source; image.addEventListener( "load", function() { videoElem.appendChild( image ); }); videoElem.addEventListener( "click", function() { var iframe = document.createElement( "iframe" ); iframe.setAttribute( "frameborder", "0" ); iframe.setAttribute( "allowfullscreen", "" ); iframe.setAttribute( "src", "https://www.youtube.com/embed/"+ embedID +"?rel=0&showinfo=0&autoplay=1" ); this.innerHTML = ""; this.appendChild( iframe ); } ); };
This function carries out the same line of codes that we added in our previous tutorial, albeit with a few adjustments. The function will show the Youtube video image thumbnail and append the Youtube video in the wrapper element, .youtube
, upon the user click.
And we are all set! Check out the source code and the demo site.
Use
In this tutorial, we have wrapped code from our previous tutorial into a Web Component. We are now able to embed a Youtube video more elegantly with our new custom element: <youtube-embed>
, for example:
<youtube-embed embed="AqcjdkPMPJA"></youtube-embed>
All the code (JavaScript, CSS, HTML) is encapsulated in separate HTML, preventing potential errors within that file breaking the entire site. And whenever we need to reuse it in other projects, we go ahead and import the HTML, youtube-embed.html
.

Wrapping Up
This is just one example of how we can use Web Components. You can find more amazing implementations of Web Components on customelements.io. Lastly, I hope you enjoyed this tutorial and found it an easy-to-follow reference.
Further Resources
- WebComponents.org a place to discuss and evolve web component best-practices
- How to Create Your Own HTML Elements With Web Components
- Custom Elements: Explore the world of Web Components
- Practical lessons from a year of building web components Google I/O 2016
- Polymer: making Web Components accessible