A clear trend in web development today is that sites are composed of many 3rd party widgets. Go to any big site, open up the web inspector and see how much is really loaded from the address you typed!
This is great – everyone delivers one service they do well, instead of each site rolling their own half-baked version of everything from a comment system to a media player. Remember the days before Youtube? To show a video you’d have to host a media file yourself and usually just link to it and hope the user’s browser’s setup correctly; watching it was often done in a tiny, non-fullscreen’able QuickTime window. Now you just drop in a an embed code and get a full-fledged video player right on your page, together with your content.
But with so many components co-existing on a page comes a number of challenges arise:
- The embed code must be short & concise and work with a number of CMS’s
- What if they interfere with each other? Will the failure of one bring down the others?
- Keeping page load time down
This post is the first in a series on how to make a solid embeddable widget using the latest in web development: AWS for delivery, grunt and RequireJS for building and managing, sandboxing Google Analytics with EasyXDM and safe-wrapping 3rd party libraries. Today we will focus on #1.
Our mission today
At Shootitlive we provide media through an HTML5 player and to make our service a breeze to use we set out to make the smallest possible embed code.
We went from this:
Shootitlive is a live media service used mainly by newspapers and bloggers including The Times in the UK and Aftonbladet in Sweden. Our content is delivered from our backend into the Shootitlive Player (SILP), through real-time JSON playlists. The idea is that you embed the player wherever you want to show your media feed. On-the-field photographers then publish photos, videos and audio straight from their camera to the web in a couple of seconds.
The many ways of embedding
The most rudimentary way of embedding still used by many services is the following:
Therefore most providers use this technique instead:
Which is better due to being asynchronous and all.
The second option however is still long and hairy and our experience is that the longer embed code you give to your users – the bigger the chance something terrible happens to it on its travel, whether it’s edited by a user or mangled by a well-intended CMS.
Also it has a lot of specifics pasted on each site – i.e. if we change something in the embedding / loading technique we must have each client update their embed code.
How we do it
In summary, our goals are:
- One can pass arbitrary parameters to each embed
- The embed code is short
- It loads asynchronously
- One can have multiple players on one page
Instead of pointing the embedding site straight to our player source file we decided to add a loader file that sets up the player and deals with all embedding-related issues. Also it enables us to tune our embedding technique and have it affecting all existing embeds.
<script src="//our-cdn.com/shootitlive.load.js?project=13&client=greenfield[more URL encoded parameters]" async></script>
The loader will have to carry out the following:
- find its own
scripttag, or equivalently find all player embeds in the DOM
- parse the parameters for each player through the query string
- create a container
divfor the player next to each script tag
- load the
silp.min.jsscript with the supplied parameters
1: Identifying embed codes
Having multiple players makes things a bit more tricky since the very same script will be run multiple times – once for each player. Therefore we use a global `foundEls` variable to keep track of which embeds have already been found.
Note that the regexp
re determines if a script tag is a Silp embed or not.
This code will identify all embed tags on the page once. If there are two embeds on a page – with HTML content in between that delays the DOM parsing, it will only find the first embed on the first run. However, usually the first inclusion will find all embed tags and store them in `foundEls` while the second run will not do anything. Due to this care must be taken so that the loader is idempotent (I confess, math lingo makes me feel warm inside).
2: Parsing the query string
Our second step is to parse the settings from each script tag and create a container div for the player next to it. One can use the previously mentioned
$.param() to convert a JSON object into a query string. Luckily Ben Alman has written an inverse function
deparam() that gives you back the original JSON object, you can find it here. I made it into a stand-alone version found here.
Equipped with this, we can transform an URL with a query string into a JSON object like this:
3 and 4: Creating a container for the player and feeding the parameters into its initialisation method
Naturally we want the player to be inserted next to the embed code that created it – which is easy since we have a handler to the
<script> element for each embed.
All that remains is actually loading the player, and since it will initialize a player for every element in
settings we only need to load its source once at the end of our loader script:
All in all
You can find the final loader script here. The loader script we use in production is slightly more involved due to backward-compatibility and the such, but the gist illustrates the distilled principle behind it.
The loader script comes with many benefits. For example we could create a custom loader script for a client – containing additional default parameters, extra code for custom templates, etc. Editing this custom loader would instantly affect all existing embeds for that client. We could even point them to a different version of the player. In summary – it provides a great middle-point for rolling out changes quickly without involving the client with technical issues.
If you have questions, want to tell me I don’t know the first thing about this and ought to pick another carrer path or just say hi: please comment or tweet me: @svammel.
The upcoming articles will cover the refactoring and architectural improvements of our player. Topics will include:
- Using RequireJS for an embeddable app – either as many-files, or as a minified single-file version. We have done some pretty sweet solutions with grunt for no-brainer development, testing and deployment.
- Safely using jQuery and other 3rd party libraries in your embeddable through namespacing
- Running Google Analytics in the player without interfering with the parent page’s own analytics
Cheers, The Shootitlive Team