Hugo + js = footnote pop-up

Hugo + js = footnote pop-up

Example on how to enhance Hugo’s default footnotes. By default Hugo will put the footnotes at the bottom of the article and provide “backrefs” to them. This works well, however if you click on a footnote you still jump to the end of the page (and back if you want to continue reading), which i find not ideal from a UX (user experience) perspective. Instead i prefer them to show in a pop-up.

This article is a (modified) fork of hugo-footnotes-popup, all credit goes to Vojtech Pavlovsky.

This article was written for Hugo 0.70.

On the web one can provide a better UX than a glorified book layout, instead of jumping all over the place have the footnotes pop-up. At the end of the article there is still a list of all footnotes, to provide an overview and from there you can still jump back into the text (if desired). For footnote pop-ups there are roughly 2 options, when you clicking on a footnote link in the text show it as a pop-up at the bottom of the viewport, here you can see a live example1 on how it works2 (<- large footnote). Or show a pop-up at the cursor’s position, also known as XKCD style footnotes .

This is what it looks like.

Footnote pop-up

Footnote pop-up

Code

The code is straight forward, create the footnote pop-up HTML, patch the generated links by Hugo, on click update the content of the footnote pop-up and show it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
window.addEventListener("load", function () {
    const popupWrapper = document.getElementById("ex-popup-wrapper");
  
    // Create main container that will hold the footnote content
    const popupContentWrapper = document.getElementById("ex-footer-content");
    const popupContent = popupContentWrapper.appendChild(document.createElement("div"));
    popupContent.id = "popup-content";
    const popupIndex = popupContentWrapper.insertBefore(document.createElement("div"), popupContent);
    popupIndex.id = "popup-index";
  
    // Close button
    document.getElementById("ex-footer-close").addEventListener("click", () => {
      popupWrapper.style.display = "none";
    });
  
    // Patch the existing footnotes
    document.querySelectorAll(".footnote-ref").forEach(fnRef =>
      fnRef.addEventListener("click", event => {
        event.preventDefault();
  
        const index = fnRef.hash.substring(4);
        popupIndex.innerHTML = index + ".";
  
        const footnote = document.getElementById("fn:" + index).cloneNode(true);
        const backrefElement = footnote.querySelector(".footnote-backref");
        backrefElement.parentNode.removeChild(backrefElement); // Remove the backref since it does not make sense in the expanded footnote
        popupContent.innerHTML = footnote.innerHTML.trim();
  
        popupWrapper.style.display = "grid";
      })
    );
  
    window.addEventListener("scroll", () => {
      popupWrapper.style.display = "none";
    });
  });  

Footnote pop-up HTML. Add this at the bottom of the HTML tree, after your content.

1
2
3
4
5
6
<div id="ex-footnote">
    <div id="ex-popup-wrapper">
        <div id="ex-footer-close">{{ partial "inline-svg" "times" }}</div>
        <div id="ex-footer-content"></div>
    </div>
</div>

The accompanying SCSS.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#ex-footnote {
  position: fixed;
  font-size: $font-size-small;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 99999;
  max-height: 35%;
}

#ex-popup-wrapper {
  display: none;
  grid-template-columns: auto 50px;
  grid-template-rows: minmax(0, 1fr);
  padding-top: 1em;
  background-color: var(--bg-color);
  border-top: 5px solid var(--primary-color);
  max-height: calc(35vh);
}

#ex-footer-content {
  grid-column: 1;
  grid-row: 1;
  display: flex;
  padding-top: 1em;
  padding-bottom: 1em;
}

#ex-footer-close {
  grid-column: 2;
  grid-row: 1;
  text-align: center;
  font-size: 2em;
  cursor: pointer;

  &:hover {
    color: var(--primary-color);
  }
}

#popup-content {
  text-align: left;
  margin-left: $font-size-small;
  overflow-y: auto;

  p {
    margin: 0;
  }
}

#popup-index {
  padding: 0 0 0 1rem;
  flex-shrink: 0;
}

Possible improvements

The footnote pop-up can be closed by either scrolling or clicking on the close button. From a UX point of view it might be worthwhile3 to also let the user close it when clicking outside of the pop-up since this is an intuitive action.

Returns links will be shown in the footnote list at the bottom of the page to return to the footnote in the text. Hugo supports 2 markdown renderers, BlackFriday and the default Goldmark. In Goldmark, the default, overwriting the return to content symbol/icon is not supported, this was resolved with SCSS.

1
2
3
4
5
6
7
8
.footnote-backref {
    visibility: hidden;

    &::before {
        content: "#{$footnoteBackSymbol}";
        visibility: visible;
    }
}

  1. see this is what i mean. ↩︎

  2. This is a rediculus long footnote as example how they look like. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse non vestibulum augue, vitae consectetur metus. Ut purus erat, mollis id lectus eget, mattis tincidunt diam. Curabitur convallis ex auctor, egestas nunc vitae, efficitur quam. Quisque semper malesuada metus. Sed metus ante, sagittis sed ornare eget, malesuada vel felis. Curabitur sem est, pharetra eget tortor pulvinar, blandit bibendum diam. Morbi malesuada lacinia efficitur. Curabitur tristique, velit sit amet commodo posuere, lorem nibh volutpat nisl, sed fermentum magna tortor a lorem. Praesent et vestibulum nunc. Suspendisse dapibus mauris sit amet eleifend finibus. Cras vestibulum viverra ante et porta. Donec et lacus egestas, aliquam massa a, pellentesque magna. Quisque est metus, luctus ac molestie ac, luctus nec est. Nulla justo felis, pretium non vulputate in, ultrices quis dolor. Nulla bibendum nulla justo, in aliquet mauris aliquet vel. Phasellus at dolor ut sem lacinia aliquet. In pulvinar in enim pretium congue. Nam pulvinar ante nec magna venenatis ornare. Ut vitae odio vitae magna consequat venenatis. Nunc faucibus, orci porta vehicula mollis, est magna tempor erat, quis volutpat felis ipsum at enim. Mauris ut turpis vitae lacus gravida rutrum vitae a sapien. Aenean quis metus placerat mi feugiat hendrerit. Aliquam feugiat magna tortor, vel tincidunt tortor convallis nec. Mauris nunc eros, pulvinar rhoncus aliquam a, pharetra sit amet magna. Aenean pretium purus ac dui tempus, at luctus felis ultrices. Etiam ullamcorper id nulla volutpat consectetur. Nam ut nibh pretium tellus scelerisque pharetra. Suspendisse tincidunt dui interdum, luctus tortor id, volutpat lectus. Proin sed enim dapibus, luctus est vitae, bibendum metus. Fusce ligula lectus, auctor ac ligula id, tincidunt placerat diam. In rhoncus quam dolor. Pellentesque vel purus sodales, feugiat mi ut, tempus ipsum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse non vestibulum augue, vitae consectetur metus. Ut purus erat, mollis id lectus eget, mattis tincidunt diam. Curabitur convallis ex auctor, egestas nunc vitae, efficitur quam. Quisque semper malesuada metus. Sed metus ante, sagittis sed ornare eget, malesuada vel felis. Curabitur sem est, pharetra eget tortor pulvinar, blandit bibendum diam. Morbi malesuada lacinia efficitur. Curabitur tristique, velit sit amet commodo posuere, lorem nibh volutpat nisl, sed fermentum magna tortor a lorem. Praesent et vestibulum nunc. Suspendisse dapibus mauris sit amet eleifend finibus. Cras vestibulum viverra ante et porta. Donec et lacus egestas, aliquam massa a, pellentesque magna. Quisque est metus, luctus ac molestie ac, luctus nec est. Nulla justo felis, pretium non vulputate in, ultrices quis dolor. Nulla bibendum nulla justo, in aliquet mauris aliquet vel. Phasellus at dolor ut sem lacinia aliquet. In pulvinar in enim pretium congue. Nam pulvinar ante nec magna venenatis ornare. Ut vitae odio vitae magna consequat venenatis. Nunc faucibus, orci porta vehicula mollis, est magna tempor erat, quis volutpat felis ipsum at enim. Mauris ut turpis vitae lacus gravida rutrum vitae a sapien. Aenean quis metus placerat mi feugiat hendrerit. Aliquam feugiat magna tortor, vel tincidunt tortor convallis nec. Mauris nunc eros, pulvinar rhoncus aliquam a, pharetra sit amet magna. Aenean pretium purus ac dui tempus, at luctus felis ultrices. Etiam ullamcorper id nulla volutpat consectetur. Nam ut nibh pretium tellus scelerisque pharetra. Suspendisse tincidunt dui interdum, luctus tortor id, volutpat lectus. Proin sed enim dapibus, luctus est vitae, bibendum metus. Fusce ligula lectus, auctor ac ligula id, tincidunt placerat diam. In rhoncus quam dolor. Pellentesque vel purus sodales, feugiat mi ut, tempus ipsum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse non vestibulum augue, vitae consectetur metus. Ut purus erat, mollis id lectus eget, mattis tincidunt diam. Curabitur convallis ex auctor, egestas nunc vitae, efficitur quam. Quisque semper malesuada metus. Sed metus ante, sagittis sed ornare eget, malesuada vel felis. Curabitur sem est, pharetra eget tortor pulvinar, blandit bibendum diam. Morbi malesuada lacinia efficitur. Curabitur tristique, velit sit amet commodo posuere, lorem nibh volutpat nisl, sed fermentum magna tortor a lorem. Praesent et vestibulum nunc. Suspendisse dapibus mauris sit amet eleifend finibus. Cras vestibulum viverra ante et porta. Donec et lacus egestas, aliquam massa a, pellentesque magna. Quisque est metus, luctus ac molestie ac, luctus nec est. Nulla justo felis, pretium non vulputate in, ultrices quis dolor. Nulla bibendum nulla justo, in aliquet mauris aliquet vel. Phasellus at dolor ut sem lacinia aliquet. In pulvinar in enim pretium congue. Nam pulvinar ante nec magna venenatis ornare. Ut vitae odio vitae magna consequat venenatis. Nunc faucibus, orci porta vehicula mollis, est magna tempor erat, quis volutpat felis ipsum at enim. Mauris ut turpis vitae lacus gravida rutrum vitae a sapien. Aenean quis metus placerat mi feugiat hendrerit. Aliquam feugiat magna tortor, vel tincidunt tortor convallis nec. Mauris nunc eros, pulvinar rhoncus aliquam a, pharetra sit amet magna. Aenean pretium purus ac dui tempus, at luctus felis ultrices. Etiam ullamcorper id nulla volutpat consectetur. Nam ut nibh pretium tellus scelerisque pharetra. Suspendisse tincidunt dui interdum, luctus tortor id, volutpat lectus. Proin sed enim dapibus, luctus est vitae, bibendum metus. Fusce ligula lectus, auctor ac ligula id, tincidunt placerat diam. In rhoncus quam dolor. Pellentesque vel purus sodales, feugiat mi ut, tempus ipsum. ↩︎

  3. most users will intuitively not scroll and therefore are required to manually click on the close button which can be annoying/improved upon ↩︎

Noticed an error in this post? Corrections are appreciated.

© Nelis Oostens