Towards Lighter Weight Youtube Embeds

youtubetechnical detailswebsite designprogramminggetzola

Howdy.

A friend let me know that the post I wrote on my first impressions of Eurovision was loading nearly a hundred scripts into the browser.

The reason for this is simple. Besides the relatively minimalist amount of client-side Javascript I use on this site, I happened to embed 39 YouTube videos, each of which loaded an additional two scripts into the browser.

The result is a rather bloated user experience; one that performs poorly on my six-year-old phone.

There's a simple solution to this problem: don't embed more than a couple of videos in a post, and there will be no such glut. Unfortunately, this solution is not to my liking. I like having a bajillion vids there.

Another, heftier approach would be to embed the YouTube videos only if the end user wants them there. This is what I've decided to do.

Default YouTube Shortcode

This blog is a static website, generated using a toolkit called zola, which allows me to write blogposts in Markdown format, with the use of customizable shortcodes and templates to fill in gaps for generating the types of HTML I desire.

One of the standard built-in shortcodes is for YouTube videos, and its source looks like this:

<div {% if class %}class="{{class}}"{% endif %}>
    <iframe
        src="https://www.youtube.com/embed/{{id}}{% if autoplay %}?autoplay=1{% endif %}"
        webkitallowfullscreen
        mozallowfullscreen
        allowfullscreen>
    </iframe>
</div>

If I ever want to include a YouTube video in my page, I just include include this shortcode, providing args. For example: {{youtube(id="myKewlVid")}}

When the page is rendered, the function call inside the curly braces is replaced by the output of the shortcode.

Shortcodes can be quite complex (as we'll shortly see.)

The outcome of the default template is simple: the YouTube embed path is embedded into my page, and gets loaded by the browser upon page load.

Load the Video on Demand

Suppose we want to write our own, replacement youtube shortcode.

To start, I created a div that will not display, with an empty iframe inside of it. Both of these elements have unique ids, based on youtube's id for the video. (Of course, this won't be valid html if I am embedding the same video multiple times since each element should have a unique id.)

<div class="youtube__vid" id="ytv-{{id}}" style="display:none;">
	<iframe id="ytinner-{{id}}"></iframe>
</div>

When a page containing this code is loaded in the browser, the iframe is rendered as an empty HTML document. Basically, nothing is there.

I have a separate element that is displayed before the video loads. It looks like this:

{% set embedLink = "https://www.youtube-nocookie.com/embed/" ~ id %}
<div class="youtube__placeholder" 
	 id="ytp-{{id}}" 
	 onclick='$("#ytinner-{{id}}").attr("src", "{{embedLink}}");'>	
</div>

This is the important bit:

$("#ytinner-{{id}}").attr("src", "{{embedLink}}");

With this, we populate the src attribute of our iframe with a path to the YouTube embed. As soon as we change the 'src' attribute of our iframe, the youtube video loads.

This is really cool!

From here, we can extend our inline Javascript to cause our placeholder to disappear, and cause the div containing the iframe to appear, and it's mainly a lot of mucking about with Cascading Style Sheets to make it look good.

(If I mucked with them more, I might have achieved this. Instead, they look functional.)

Querying the Youtube API For Video Information

This is all well and good, but our placeholder should probably contain some information about the video.

YouTube embeds by default show an image and video title to entice the user to engage with the content. My janky, lightweight bootleg should do that too.

A simple and naïve approach would be to fill in default values for the title and image.

In fact, we can retrieve the image easily (they're in a pretty standardized location, retrievable based on the id):

{% set embedLink = "https://www.youtube.com/embed/" ~ id %}
{% set ytTitle = "Youtube Video" %}
{% set ytImg = "https://i1.ytimg.com/vi/" ~ id ~ "/maxresdefault.jpg" %}

<div class="youtube__placeholder" 
	 id="ytp-{{id}}" 
	 onclick='$("#ytinner-{{id}}").attr("src", "{{embedLink}}");
			  ToggleVisibility("ytp-{{id}}");
			  ToggleVisibility("ytv-{{id}}");'>	
	<span class="youtube__title">{{ytTitle}}</span>
	<img class="youtube__img" src="{{ytImg}}" alt="Youtube video"/>
	<span class="youtube__why">Click to load the YouTube Embed."</span>
</div>

Of course, this is janky, unsupported & subject to breaking in the future... and also, it doesn't get us any information about the video's title.

An alternative approach would be to retrieve our video and title information from the Youtube Data V3 API.

To access the API, you'll need to register a Google Developer Account, and you will be limited to a limited quota of access requests.

Back in static site generation land, Zola gives its users the flexibility to check for environmental variables and to retrieve data from remote sources.

This means we can follow the standard practice of using our key as an environmental variable, rather than storing it directly (& super insecurely) in our code.

Additionally, if I'm ever building this site locally and running things offline, I can simply not set the environmental variable and use the placeholder image/title values as a fallback.

Here's my approach to this.

{% set key = get_env(name="YOUTUBE_API_KEY", default=false) %}
{% if key != false %}
  {# We have an API key! Let's make an HTTP call while we generate our static site... #}
  {% set yt_api_link = "https://youtube.googleapis.com/youtube/v3/videos?part=snippet&id=" ~ id ~ "&key=" ~ key %}
  {% set yt = load_data(url=yt_api_link, format="json") %}
  {% set vid_info = yt.items | first %}
  {% if "snippet" in vid_info %}
    {# override our defaults with values from the API reponse #}
    {% set ytTitle = vid_info.snippet.title %}
    {% set ytImg = vid_info.snippet.thumbnails.maxres.url %}
  {% endif %}
{% endif %}

We use our lazy defaults, and check whether we have a youtube api key in our environment. If so, we query the Videos part of the Youtube API, providing our key and the video id. The server's response is loaded into Zola.

Since the "videos" API is... plural, we will get a list of results even though we are only ever requesting information for a single video. Consequently, we select the first result.

We then need to double-check that our first result actually has the snippet field. If it doesn't, we'll get a key error and crash Zola.

From that, we can pull out the title & image information in a serious way and use those to populate our youtube placeholder.

Hooray!

Putting It All Together

Here's the final version of the source code that I'm using.

{% set embedLink = "https://www.youtube-nocookie.com/embed/" ~ id %}
{% set key = get_env(name="YOUTUBE_API_KEY", default=false) %}
{% set ytTitle = "Youtube Video" %}
{% set ytImg = "https://i1.ytimg.com/vi/" ~ id ~ "/maxresdefault.jpg" %}

{% if key != false %}
	{# We have an API key! Let's make an HTTP call while we generate our static site... #}
	{% set yt_api_link = "https://youtube.googleapis.com/youtube/v3/videos?part=snippet&id=" ~ id ~ "&key=" ~ key %}
	{% set yt = load_data(url=yt_api_link, format="json") %}
	{% set vid_info = yt.items | first %}
	{% if "snippet" in vid_info %}
		{# override our defaults with values from the API reponse #}
		{% set ytTitle = vid_info.snippet.title %}
		{% set ytImg = vid_info.snippet.thumbnails.maxres.url %}
	{% endif %}
{% endif %}

<div class="youtube">
	<div class="youtube__placeholder" 
		 id="ytp-{{id}}" 
		 onclick='$("#ytinner-{{id}}").attr("src", "{{embedLink}}");
				  ToggleVisibility("ytp-{{id}}");
				  ToggleVisibility("ytv-{{id}}");'>	
		<span class="youtube__title">{{ytTitle}}</span>
		<img class="youtube__img" src="{{ytImg}}" alt="Youtube video"/>
		<span class="youtube__why">Click to load YouTube video.</span>
	</div>
	<div class="youtube__vid" id="ytv-{{id}}" style="display:none;">
		<iframe id="ytinner-{{id}}" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
	</div>
</div>

Oh & an example (subject to change as I update the site)

Rick Astley - Together Forever (Video) Youtube video Click to load YouTube video.