Introduction
I recently had a chance to do a small redesign of my personal site. One of the goals, besides making everything look cleaner, was to embed Changelog's Podcast Player, so that it would play 85th episode of JS Party, in which I talked about one of my projects.
It seems pretty straight forward:
<audio data-theme="night" data-src="https://changelog.com/jsparty/85/embed" src="https://cdn.changelog.com/uploads/jsparty/85/js-party-85.mp3" preload="none" class="changelog-episode" controls></audio><p><a href="https://changelog.com/jsparty/85">JS Party 85: Building PizzaQL at the age of 16</a> – Listen on <a href="https://changelog.com/">Changelog.com</a></p><script async src="//cdn.changelog.com/embed.js"></script>
And works just fine on a static HTML site. However, in my case, I was dealing with a React application, powered by Next.js with Static Site Generation (SSG). I also focused much on performance and accessibility.
That's exactly why I decided to make this tutorial!
Tutorial
First of all, we need to embed an audio
tag into our app. It's fairly easy:
<audio
controls
data-theme="night"
data-src="https://changelog.com/jsparty/85/embed"
src="https://cdn.changelog.com/uploads/jsparty/85/js-party-85.mp3"
preload="none"
className="changelog-episode"
/>
Make sure to replace
data-src
andsrc
attributes so that they link to an episode, which you want to embed. It does not need to be the JS Party podcast.
We now need to load a script, which will add an iframe
with the player itself. Normally, we would obtain it from https://cdn.changelog.com/embed.js, but instead, we are going to self-host it. Here are the reasons why:
- Self-hosting assets (like fonts or scripts) is significantly faster;
- The default Changelog's script does not add a
title
attribute to the creatediframe
- this will lower our site accessibility score on tools like Lighthouse; - The default Changelog's script sometimes throws
Uncaught TypeError: Cannot read property 'postMessage' of null.
.*
*This does not break the functionality of the player and therefore we can easily fix it using a try...catch statement.
In order to edit the embed script, we first need to unminify the original one from Changelog's CDN. We can then modify it like so:
!(function (e) {
function t(t) {
var r = t.getAttribute("data-src"),
i = t.getAttribute("data-theme") || "night",
n = e.createElement("iframe");
n.setAttribute("src", r + "?theme=" + i + "&referrer=" + e.location.href),
n.setAttribute("width", "100%"),
n.setAttribute("height", "220"),
n.setAttribute("scrolling", "no"),
n.setAttribute("frameborder", "no"),
+ n.setAttribute("title", "Podcast"),
t.parentNode.replaceChild(n, t),
(this.id = +new Date()),
(this.src = n.src),
(this.iframe = n);
}
var r = "https://changelog.com",
i = e.getElementsByClassName("changelog-episode"),
n = [],
a = function (e, t) {
- (t.context = "player.js"), (t.version = "0.0.11"), (t.listener = e.id), e.iframe.contentWindow.postMessage(JSON.stringify(t), r);
+ try {
+ (t.context = "player.js"), (t.version = +"0.0.11"), (t.listener = e.id), +e.iframe.contentWindow.postMessage(JSON.stringify(t), r);
+ } catch {}
};
window.addEventListener("message", function (e) {
if (e.origin !== r) return !1;
var t = JSON.parse(e.data);
if ("player.js" !== t.context) return !1;
if ("ready" === t.event) for (var i = n.length - 1; i >= 0; i--) n[i].src === t.value.src && a(n[i], { method: "addEventListener", value: "play" });
if ("play" === t.event) for (i = n.length - 1; i >= 0; i--) n[i].id !== t.listener && a(n[i], { method: "pause" });
});
for (var s = i.length - 1; s > -1; s--) n.push(new t(i[s]));
})(document);
We are going to load the script in a useEffect
hook so that it won't run on the server (where a browser API, like document
and window
, isn't available):
useEffect(() => {
const script = document.createElement('script');
script.src = '/embed.js'; // Since I'm using Next.js I can place the file in the `public` folder and refer to it like so.
script.async = true; // Make sure the script isn't render-blocking.
script.defer = true;
document.body.append(script);
}, []);
That's it! You now have a beautiful player on your site.
Credits
The approach of using the useEffect
hook was taken from React NodeGui website source code.
Thank you for reading my post! I hope you will find it useful ;)
Top comments (0)