Search

Footnotes Progressively Enhanced to Popovers

Michelle Barker is onto an excellent idea in Progressively Enhanced Popover Toggletips. Her idea is that footnotes are the perfect sort of thing to make popovers. You know popovers: It’s like when I put a superset1 number in a sentence (like I just did) and then link it down to the bottom of the post to explain something in more detail which would have been distracting detail to put in the paragraph itself. But “jumping down” like <a href="#footnote-1"> is also distracting. Why not just open the extra information in a little popup? Indeed!

I like the idea of “doing both”. As Michelle says:

We could also hide the list when anchor positioning is supported, but I think it’s also more user-friendly to include references as a list in addition to within the document.

But then I read:

A small downside is that this approach requires a bit of content duplication.

The way Michelle did it was to put an <ol> of footnotes at the bottom as usual, and then also put the [popover] footnotes up in the content as well, which is the duplication.

The duplication isn’t that big of a deal. It just could be a little annoying for some readers (uh, yes, I already read this thank you). It mostly sucks because it makes them harder to maintain. You’d have to make identical changes manually in two places. If that’s no problem, Michelle’s technique is sound and you could use it as-is!

But… it got me thinking that there must be a way to re-use the same content for both!

Article Series

Attempt 1) Duplicates via JavaScript

My first take was that we gotta stop the manual content duplication. That’ll be a huge pain to maintain. So let’s do the duplication of the footnote list in JavaScript, hide it both visually and from screen readers as the original list is still there, and use the duplicates for the popovers.

Since that original list is still there, we can test the browser support for popovers and just entirely bail out if the support isn’t there.

// browser supports popovers
if (HTMLElement.prototype.hasOwnProperty("popover")) {

}Code language: JavaScript (javascript)

Then we’ll:

  1. Clone the existing list of footnotes
  2. Adjust the IDs so we don’t have duplicate IDs
  3. Add the popover attributes
if (HTMLElement.prototype.hasOwnProperty("popover")) {
  // find the list of footnotes and clone it
  const references = document.querySelector(".references");
  const duplicateReferences = references.cloneNode(true);
  
  // hide the duplicate from screen readers before re-injecting it into DOM
  duplicateReferences.setAttribute("aria-hidden", true);
  references.parentNode.insertBefore(duplicateReferences, references.nextSibling);
	
  // loop over each footnote to make a popover
  const popovers = duplicateReferences.querySelectorAll("li");
  popovers.forEach(popover => {
    popover.id = `ref_${popover.id}`;
    popover.setAttribute("popover", true);
  });
}Code language: JavaScript (javascript)

Looking in the web inspector we can see now that both lists are there:

The <button> elements which trigger the popups are still up above in the content, referring to these new popovers that now exist. In fact both the link and the button are both there:

<a class="popover-replace" href="#1"><sup>1</sup></a>
<button popovertarget="ref_1"><sup>1</sup></button>Code language: HTML, XML (xml)

People will only ever see/use one or the other. There is an <a> link there by default, then if popovers are supported, it’s replaced by the button. That was already in Michelle’s code:

[popovertarget] {
  display: none;
}

@supports (anchor-name: --ref_1) {
  [popovertarget] {
    display: inline;
    ...
  }
}Code language: CSS (css)

So that’s this!

Attempt 2) The Original List can be Popovers Too

That previous attempt required JavaScript, which doesn’t bother me really as it’s progressive enhancement, but it’s not always clear where JavaScript like that that spreads across different areas of a page would go, and may be a bit more fragile.

Can’t the footnotes at the bottom of the page… just be the popovers themselves? They can! Mostly!

The first trouble is that adding the popover attribute immediately makes them hidden. So the trick is to un-hide them and style them as you want them as a list. Then when they are open, which we know with the :popover-open pseudo-class, we can style them like popovers.

/* closed */
[popover] {
  display: list-item;
  position: relative;
  border: 0;
  padding: 0;
  width: auto;
  overflow: visible;

  /* open */
  &:popover-open {
    display: block;
    margin: 0;
    padding: 1rem;
    background: lavender;
    border-radius: 0.5rem;
    max-width: 15rem;
    ...
   }
}Code language: CSS (css)

The first side-effect here is that when any of these popovers are open, the footnote in the list below disappears. The content literally transports to the new popover location. So that causes a bit of reflow and you might not like it for that reason alone. But it works!

There is a second side-effect here. The list of footnotes-become-popovers are forced to be display: list-item so they are visible and have numbers and such. If you leave them as that when you move them into popover position, the little number marker will come with it. You can hide it:

[popover] {
  &:popover-open {
    &::marker {
      content: "";
    }
  }
}Code language: CSS (css)

If you make the popover, say, display: block instead, this immediately breaks the automatic list numbering. You’ll need to bring your own list numbering that is based on counters instead.

.references {
  counter-reset: item;
  > li {
    counter-increment: item;
    &::marker {
      content: counter(item) ") ";
    }
  }
}Code language: CSS (css)

Others?

There are upsides and downsides to both of the above techniques. Mostly they focus on solving content duplication.

There is another thing they fail at though. Michelle notes:

I’m making use of the :has pseudo-class to position the little arrows attached to the bubbles when the popover is open, by styling the ::before pseudo-element of the button.

My two techniques fail at having the little arrow be present because the DOM position of the button with target and popover aren’t next to each other anymore. You might imagine the little pointer off the popover bubble being part of the bubble itself, but no, Michelle cleverly made it part of the button instead, so it’s always pointing at the correct place. I love that.

If you wanted to preserve that, you could write JavaScript that duplicates the footnote popovers like I did in the first example, but then place them within the content (likely with aria-hidden to avoid duplicate content awkwardness) like in Michelle’s original demo.

I’ll leave that as an excercise for you readers if you wish to take it on. I’d love to see it, or any other interesting variations.

Article Series


  1. My favorite part about footnotes is the perfect excuse to use the <sup> element. ↩︎

It's time to take your JavaScript to the next level

Frontend Masters logo

Frontend Masters is the best place on the web to really learn JavaScript. We have a complete learning path from the biggest and best teachers in JavaScript to help you make the most out of the web's biggest language.

One response to “Footnotes Progressively Enhanced to Popovers”

  1. Avatar Chris Coyier says:

    It also occurs to me that the element() function in CSS (only ever implemented as -moz-element() I think?) might be an interesting thing to think about here. Browser support isn’t good and I don’t even think it’s standards-track anymore, but this feels like a solid use case if it ever were to be.

    https://developer.mozilla.org/en-US/docs/Web/CSS/element

Leave a Reply

Your email address will not be published. Required fields are marked *

Frontend Masters ❤️ Open Source

Did you know? Frontend Masters Donates to open source projects. $313,806 contributed to date.