How to make gifs on your website click-to-play with vanilla JavaScript


Many websites today are built with extensive frameworks and a whole lot of JavaScript library dependencies to power their frontend, think of single page applications such as Facebook or Youtube. However, if - for example - your website is built using a static site generator (as is this blog with Hugo), you probably don’t want to rely on importing heavy JavaScript frameworks for such easy functionality (at least as perceived by users). Furthermore, there are more and more ambitions in using Vanilla JS for functions instead of importing heavy frameworks, due to the fact that browsers now provide much more sophisticated functions to modify the DOM than it was the case some years ago. As I had to do some research to make the gifs in a recently published post click-to-play by only using vanilla JavaScript I will show you the method I finally ended up using.

Actually, the basic idea is really simple: show a static image when paused and swap it with the actual gif once the user wants it to play. Consider the following example (try it for yourself: click to start the gif, click again to stop it):

We achieved this functionality with just these few lines of HTML and vanilla JavaScript:

<img src="*/mario.png" onclick="playGif(this)" />

<script>
 function playGif(gif_img) {            
  if (gif_img.src.endsWith(".gif"))
  {
   gif_img.src = gif_img.src.substring(0, gif_img.src.length - 3) + "png";
  } else 
  {
   gif_img.src = gif_img.src.substring(0, gif_img.src.length - 3) + "gif";
  }    
 }
</script>

Basically, we just swap the extension of the image’s source path. For this we saved the first frame of the gif as png file alongside the gif. So, if the image currently shows the static png file (i.e. the animation is stopped) we replace it with the gif and vice-versa. The browser will take care of everything else.

In this state, the solution is just lacking a bit of user comfort and it requires that we wire the image’s onclick handler by hand. It would be nice, if the user knew that he can start the animation by clicking or that there is an animation at all. The easiest way to do this, is to display some kind of play button on top of the image when it is stopped. To access the elements in JavaScript with more ease and to facilitate CSS styling, we enclose our gif img in a separate div and assign it a class with an appropriate name (e.g gif-player). Additionally, we add an img for the play-btn which should be overlayed to the image.

<div class="gif-player">    
    <img class="gif" src="mario.png"/>    
    <img class="gif-play-btn" src="play.png" />    
</div>

So, when we want to horizontally center the gif on the page and the play button on top of the image we could do with this CSS (of course, there is quite a large amount of possibilities to center things with CSS):

.gif-player {
  position: relative;
  margin-left: auto;
  margin-right: auto;
  width: 400px;
  height: 200px;    
}

.gif {  
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);     
}

.gif-play-btn {
  pointer-events: none;    
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

Notice, that I added pointer-events: none to the gif-play-btn. The result is that cursor events pass-through this element. So clicks on the button are handled by the image below. This makes our play control JavaScript code a bit easier. To wire everything up we only need to add a click handler to each img with the gif class inside a gif player div (getElementsByClassName is your friend). This handler can be something along the lines of:

function clickFunc() {        
 gif = this;
 gif_btn = gif.parentElement.getElementsByClassName("gif-play-btn")[0];              

 if (gif.classList.contains("gif-stopped")) {
    gif.classList.remove("gif-stopped");
    gif.classList.add("gif-playing");
    gifImgPath = gif.src.substring(0, gif.src.length - 3) + "gif";
    gif.setAttribute("src", gifImgPath);                
    gif_btn.style.opacity = 0;
 } else {
    gif.classList.remove("gif-playing");
    gif.classList.add("gif-stopped");            

    staticImgPath = gif.src.substring(0, gif.src.length - 3) + "png";
    gif.setAttribute("src", staticImgPath);    
    gif_btn.style.opacity = 1;
 }
};

And we add it like this:

function initGifPlayer(element) {    
 gif = element.getElementsByClassName("gif")[0];    
 gif.classList.add("gif-stopped");            
 gif.onclick = clickFunc;    
}

gifs = document.getElementsByClassName("gif-player");
[].forEach.call(gifs, function(gif) {
    initGifPlayer(gif);
});

In this example there are two additional points to consider. First, we change the opacity of the button instead of changing its display style. This allows us to animate the appearing and disappearing, respectively. Second, we add the classes gif-playing and gif-stopped to the gif depending on its playstate, so that we can style it differently. When everything is put together and we defined some nice opacity transitions the result can look like this:

As you can easily see, with the presented approach there is no way to pause the gif. We always show the first frame and once the image source is swapped the browser will start the gif at the beginning. As of now, if you really need this kind of functionality, you have to resort to more complicated solutions. Or you ditch gifs completely and replace them with mp4 files which are not only smaller in most cases but also come with a broader capability set thanks to HTML5’s <video> tag.

image attributions: play button: pixsector freebie