This website is now entirely JS-free

October 21, 2019

For the last two years or so, I’ve had a little script on my website that does a simple thing: it took all <img> tags’ alt attribute’s text, and added a caption below the images with that same text. I've been meaning to get rid of this script, and I've finally done so.

If you’re wondering why this script was a thing in the first place, allow me to explain. I write my posts in Markdown, and the Markdown is automatically converted to HTML. So when I add an image’s description, I didn’t want to have to add my own caption elements below them, in order for them to show up without having the user hover over the image, or use a screen reader.1

So, for this, I ended up using cash, a small JS library that could easily let me find these images and append a new DOM element with the appropriate text. The library itself comes in at about 8 KB. Frankly, that’s 8 KB too many for such a small thing.2

Here’s what the code looked like:

// Instead of using jQuery, we'll load the much more lightweight cash-dom
import $ from 'cash-dom';

$(function () {
    // Load captions for images and display them
    $(".content img").each(function () {
        $(this).after("<p class='image_caption'>" + this.alt + "</p>")
    });
});

Having to include an entire library in order to add captions below my images was a little overkill for me. I strongly believe that JavaScript on most sites doesn’t make any sense, especially so if you’re doing minor stuff.

You have to maintain your libraries, and unless you have an actual web app I don’t think it’s worth the maintenance; this website doesn’t really need to run any JS, frankly. (I used to have a combination of jQuery and Google Analytics on this website as well, prior to the last website refresh.)

So, let’s evaluate — what did the JS on my website do? Silly stuff, really:

  • Check if any <img> tags were present with a valid alt attribute; and add a <p class='image-caption'></p> tag with the appropriate text below the image.
  • Append a script to ensure the code highlighting loads if a post contains a <code> or <pre> element.

This script is now gone.

If you’re looking at one of my older posts right now, you may find out that the captions are still there. How fortunate! The appending of captions now occurs server-side with PHP, with a simple preg_replace. (I even added simple unit tests to ensure it works as desired!3)

Here’s my current, simple implementation:

function captionize(string $html) : string
{
    return preg_replace(
        '/<img .*?alt="(.*?)".*?>/',
        '$0<span class="caption">$1</span>',
        $html
    );
}

Is this a fool-proof approach? No, not quite — most people don’t recommend using regular expressions for manipulating HTML. But for what I’m trying to accomplish (saving a couple of KB by omitting JS, and doing this serverside) this will do just fine.

There’s also additional benefits to the caption being baked into the website’s HTML and not being added client-side, after the fact: the caption is now correctly included in the RSS and JSON feeds of my website, and gets indexed by search engines.

So now, my website is entirely script-free. I think the ideal amount of JavaScript on any website is the minimal amount. My back-end still contains some scripts, obviously, because the functionality is enhanced by the presence of the scripts. For visitors who just need to see the posts and my personal about page — they don’t need to load any JS now. Because that’s the minimal amount for this particular use case. None.

See also: Harry Roberts on Imgur’s waterfall being absolutely ridiculous. (I can’t believe there’s people out there defending this. Just because you can, doesn’t mean you should. This particular tweet made me decide I needed to get rid of my JS on principle, even though there’s no such thing as comparing client-side React vs. my silly little script.4)


  1. I feel like you’re missing out on image descriptions if they’re not visible. Also, I could have added them as separate paragraph elements, but that would impede accessibility. 

  2. For comparison’s sake: the fonts weigh in 17 KB per font face (up to 60 KB total) — this is much more than my silly script, but it makes a lot more sense to include these. Remember also that I originally came from a 80 KB JS file when it was originally minified jQuery that was included. What was I thinking? 

  3. Since the conversion of Markdown to HTML also deals with special character escaping, I don’t have to worry about my regular expression breaking my web pages. 

  4. Don’t even get me started on React. It has a reason to exist, and it’s a wonderful piece of engineering. So is node and npm. I’d wager that on average, all of these parts of the JS ecosystem are not helpful in reducing file sizes of JS on websites. Transpilers and/or bundlers also don’t help. (This is why I prefer simple websites and native apps.) If I had to pick something, I’d pick Svelte, it looks very promising.