/software

🖼️ Automatic captioning of images with js

I like to caption my images and I like to write in Markdown. However, it is currently not possible to make captioned images, i.e. make an HTML figure using just Markdown - at least at the current spec (version 0.27) at the time of this writing.

One way to do this, since Markdown supports HTML, is to just write a “<figure>” tag everywhere instead of writing with the Markdown “![]()” image syntax. However, I want to use the “![]()” syntax! So, here is a nice trick for captioning images using only the Markdown syntax with a little Javascript.

This picture of worms has a caption!

Use jQuery

The trick is to use Javascript to find images with non-zero alt attributes and convert them to figures with the caption filled in with the content of the alt attribute. Someone came up with this before me, and here is their script:

$("img").each(
function() {
    if ($(this).attr("alt")) {
        $(this).wrap(
            '<figure class="image"></figure>'
        ).after(
            '<figcaption>' +
            $(this).attr(
                "alt") +
            '</figcaption>'
        );
    }
}); // from https://blog.kchung.co/adding-image-captions-to-ghost/

Use vanilla Javascript

The above code works great, but it requires JQuery. I don’t want to use JQuery so I carefully followed oneuijs/You-Dont-Need-jQuery and I was able to convert this to just plain Javascript:

function ready(fn) {
    if (document.attachEvent ? document.readyState === "complete" :
        document.readyState !== "loading") {
        var elements = document.querySelectorAll("img");
        Array.prototype.forEach.call(elements, function(el, i) {
            if (el.getAttribute("alt")) {
                const caption = document.createElement('figcaption');
                var node = document.createTextNode(el.getAttribute("alt"));
                caption.appendChild(node);
                const wrapper = document.createElement('figure');
                wrapper.className = 'image';
                el.parentNode.insertBefore(wrapper, el);
                el.parentNode.removeChild(el);
                wrapper.appendChild(el);
                wrapper.appendChild(caption);
            }
        });
    } else {
        document.addEventListener('DOMContentLoaded', fn);
    }
}
window.onload = ready;

Of course this looks a little more complicated, but it works just the same.

Smooth experience

The only issue here is that when you load a page you will see all the figures “jump” into place as the captions are written to them, about one hundred milliseconds after the page loads. To avoid this, you can cover up the page until it is totally ready with a div:

<div id="loadingMask" style="width: 100%; height: 100%; position: fixed; background: #fff;"></div>

Then you can add a fade out to this div in the Javascript ready() function:

el = document.getElementById('loadingMask');
fadeOut(el);

The fadeOut() function is adapted from the youmightnotneedjquery.com fadeIn() function:

function fadeOut(el) {
  el.style.opacity = 1;

  var last = +new Date();
  var tick = function() {
    el.style.opacity = +el.style.opacity - (new Date() - last) / 80;
    last = +new Date();
    // console.log(el.style.opacity);

    if (el.style.opacity > 0) {
      (window.requestAnimationFrame && requestAnimationFrame(tick)) || setTimeout(tick, 16);
    } else {
        el.style.display='none';
    }
  };

  tick();
}

Go ahead and copy that to whatever site you want! The full code is here.

September 27, 2017

🥘 Recursive recipes 🦐 crawdad - the best web scraper