Footnotes with vanilla JavaScript

I have created the following HTML document:

<h1 id="main_chapter">Main chapter (h1)</h2>
<p>p paragraph<span id="footnote"><sup>in source code, footnote text should be displayed; in client mode, footnote number should be displayed like this (span-sup)</sup></span></p>
<p>p paragraph<span id="footnote"><sup>same as above</sup></span></p>
<p>p paragraph<span id="footnote"><sup>same as above</sup></span></p>
<p>p paragraph<span id="footnote"><sup>same as above</sup></span></p>
<p>p paragraph<span id="footnote"><sup>same as above</sup></span></p>

<h2 id="footnotes_chapter">Footnotes chapter (h2)</h2>
  1. Footnote text
  2. Footnote text
  3. Footnote text
  4. Footnote text
  5. Footnote text

In client mode, footnote text from source code should appear only right under <h2> Footnotes chapter (identified by a CSS ID #footnotes_chapter) and be automatically numbered and sorted ascending.

Also, in client mode, numbers representing number of footnote (right under <h2> Footnotes chapter) should appear super to each paragraph that has a footnote.

How can the described automatic numbering and sorting, plus replacement of source code footnote text by respective number be achieved with vanilla JavaScript?
I searched time and again in Google and found no example to achieving that.

If this question is out of scope - please don’t hesitate to close; thanks anyway.

Well, lets start with the basics: You cant use the same ID more than once.
Javascript can’t technically do anything to a source code viewing of the page - a browser does not execute javascript in its Source View set.

What you could do is use javascript to snip out the footnote text and reinsert them into the footnotes:

(Spitball code, untested)

let footnotelist = document.getElementById("footnotes_chapter_ol");
document.querySelectorAll("#main_chapter .footnote").forEach(function(x,i) { 
   let newnode = document.createElement("span"); //Create a span
   let supnode = document.createElement("sup"); //Create a sup
   supnode.appendChild(document.createTextNode(i)); //Put the index in the sup.
   newnode.appendChild(supnode); //Put the sup in the span.
   x.insertBefore(newnode); // Put the span and all of it's contents before this footnote.
   newnode = document.createElement("li"); // Make an LI.
   newnode.appendChild(x); // Move the footnote into the LI.
   footnotelist.appendChild(newnode); //put the LI in the OL.
})

I should ask the original poster what they mean by source code.

Do you mean the <h1>...</h1> view of the content, or do you mean the large headings view of the content?

@Paul_Wilkins by source code there I meant to source code view including all tags; I didn’t mean the reader-view as large headings etc.

@m_hutley thank you; a few years I haven’t used IDs, I remembered something of the sort, yet wasn’t sure…

Thanks - the source code remains untouched by JavaScript. If you want to change the source code that a client web browser gets, you’ll want to use server-side code, such as PHP, to change what gets sent to the client.

I desire to change only what appears to the human client - sorry for not clarifying that.

You can use CSS counters for the automatic numbering as shown in this Sitepoint article.

You would need js to grab the footnote text if you were supplying it in the original paragraph text but I’m sure one of the above JS gurus could build that feature into the above demo :slight_smile:

That tutorial is where you the author create and organise your own footnotes, which isn’t what the OP seems to need.

Requirements

My understanding of what the OP wants (despite the language problems) is:

  • The content is edited with the footnotes directly included with the content.
  • Each footnote is indicated with a footnote class.
  • Footnotes are replaced with a superscripted number.
  • Footnotes are moved to a separate section after all of the chapters, prefixed by that same number.

Content without JavaScript

First I’d present the footnotes so that they don’t look out of place when no JavaScript exists. You don’t want your fancy footnotes breaking when something goes wrong with the scripting.

As a result, I’ve made the footnotes grey and a bit smaller.

.footnote {
  font-size: smaller;
  color: grey;
}

Here’s some content where each footnote has been marked with a footnote classname.

<div class="content">
<h1>Footnotes experiment</h1>
<h2>Main chapter</h2>
<p>This is the first paragraph. <span class="footnote">The first paragraph has a footnote, but doesn't explain much.</span></p>
<p>This is the second paragraph. <span class="footnote">This second footnote is about as useful as the first.</span></p>
<p>The third paragraph <span class="footnote">This footnote is not really needed.</span> is what follows the second.</p>
<p>Where's my cow? Moo! There's my cow! <span class="footnote">A real book of a fictional book that a character had to go home to read to his little boy, with all the right farmyard noises. <a href="https://www.amazon.co.uk/Wheres-Cow-Discworld-Terry-Pratchett/dp/038560937X">Where's My Cow?</a></span></p>

<h2>Next chapter</h2>
<p>The first sentence of chapter two. <span class="footnote">More than one chapter is progress.</span></p>

<h2>Last chapter</h2>
<p>Yet to be implemented. <span class="footnote">A common programming term that means we haven't done this yet.</span></p>
</div>

Add section for footers

Next up is to add a footers section at the end of the content, which can remain in the content section.

<div class="content">
    ...
    <ol id="footers">
    </ol>
</div>

We can now deal with moving the footnotes.

Move footnotes

There are some conflicts that can occur here, but by ordering the tasks appropriately we can simplify what needs to be done.

  • add a superscript number to each footnote
  • link the number and footnote to each other
  • move the footnote to the footnotes section

Oh yes, there was no vanilla JavaScript in my last post. Here’s where we’ll find it.

That list of tasks translates to the following JavaScript, though I’ll do them in just a slightly different order to make it easier to present the information.

var footnotes = document.querySelectorAll(".footnote");
var footers = document.querySelector("#footers");
footnotes.forEach(function (footnote, index) {
  var num = index + 1;
  var sup = addSuperscript(footnote, num);
  linkSupToFootnote(sup, footnote);
  moveFootnoteToFooters(footnote, footers);
  linkFootnoteToSup(footnote, sup);
});

Here’s what things look like before those functions have been implemented. https://jsfiddle.net/pmw57/brns38wk/

We just need to implement those functions.

Add superscript

This task is quite easy, where you just add a superscript element and place the number inside of it, then insert it before the footnote.

function addSuperscript(footnote, num) {
    var sup = document.createElement("sup");
    sup.appendChild(document.createTextNode(num));
    footnote.parentNode.insertBefore(sup, footnote);
    return sup;
}

In the example at https://jsfiddle.net/pmw57/brns38wk/1/ you’ll see that a superscript number now appears before each footnote.

Move footnote to footers

Now that we have a reference to the superscript number, we can move the footnote down to the footers section.

function moveFootnoteToFooters(footnote, footers) {
  var li = document.createElement("li");
  li.appendChild(footnote);
  footers.appendChild(li);
}

The example at https://jsfiddle.net/pmw57/brns38wk/2/ shows how things look now that the footnotes are moved down to the footers section.

We now need to link each superscript number to its appropriate footnote.

Link to footnote

Linking a superscript to the footnote is where we use the superscripted number as a footnote identifier, and put the superscript number inside of a suitable link.

function linkSupToFootnote(sup, footnote) {
  var a = document.createElement("a");
  footnote.setAttribute("id", "footnote" + sup.innerText);
  a.href = "#" + footnote.getAttribute("id");
  sup.childNodes.forEach(function (childNode) {
    a.appendChild(childNode);
  });
  sup.appendChild(a);
}

The example at https://jsfiddle.net/pmw57/brns38wk/3/ shows the linked superscript number now working.

But, it helps to have the same number on the footnote, linking back to the superscript. Which we’ll do now.

Link to superscript

It helps if each footnote has a matching number as the superscript for identification purposes, and if scrolling is needed to get back up to the superscript number, it’s convenient if you can click on the footnote number to scroll back up to the superscript.

We just need to add a linked number in front of the existing footnote to achieve that. For the sake of presentation, I’ve combined together both the number link and trailing space into a documentFragment object, and inserted that combined group before the footnote.

function linkFootnoteToSup(footnote, sup) {
  var docFrag = document.createDocumentFragment();
  a = document.createElement("a");
  sup.setAttribute("id", "footref" + sup.innerText);
  a.href = "#" + sup.getAttribute("id");
  a.appendChild(document.createTextNode(sup.innerText));
  docFrag.appendChild(a);
  docFrag.appendChild(document.createTextNode(" "));
  footnote.insertBefore(docFrag, footnote.firstChild);
}

The example at https://jsfiddle.net/pmw57/brns38wk/4/ shows how things look when the footnotes code is completed and working correctly.

Hover on superscript to view footnote

One last refinement is where you hover on the footnote to read it, which can be achieved using the title attribute.

That can be achieved by making a simple update to the addSuperscript function to set the title attribute.

function addSuperscript(footnote, num) {
    var sup = document.createElement("sup");
    sup.appendChild(document.createTextNode(num));
    sup.setAttribute("title", footnote.innerText);
    footnote.parentNode.insertBefore(sup, footnote);
    return sup;
}

The example at https://jsfiddle.net/pmw57/brns38wk/5/ shows this update where hovering on the superscript number shows the footnote text.

Summary

All in all the full scripting code for this particular example is:

function addSuperscript(footnote, num) {
    var sup = document.createElement("sup");
    sup.appendChild(document.createTextNode(num));
    sup.setAttribute("title", footnote.innerText);
    footnote.parentNode.insertBefore(sup, footnote);
    return sup;
}
function linkSupToFootnote(sup, footnote) {
  var a = document.createElement("a");
  footnote.setAttribute("id", "footnote" + sup.innerText);
  a.href = "#" + footnote.getAttribute("id");
  sup.childNodes.forEach(function (childNode) {
    a.appendChild(childNode);
  });
  sup.appendChild(a);
}
function linkFootnoteToSup(footnote, sup) {
  var docFrag = document.createDocumentFragment();
  a = document.createElement("a");
  sup.setAttribute("id", "footref" + sup.innerText);
  a.href = "#" + sup.getAttribute("id");
  a.appendChild(document.createTextNode(sup.innerText));
  docFrag.appendChild(a);
  docFrag.appendChild(document.createTextNode(" "));
  footnote.insertBefore(docFrag, footnote.firstChild);
}
function moveFootnoteToFooters(footnote, footers) {
  var li = document.createElement("li");
  li.appendChild(footnote);
  footers.appendChild(li);
}
var footnotes = document.querySelectorAll(".footnote");
var footers = document.querySelector("#footers");
footnotes.forEach(function (footnote, index) {
  var num = index + 1;
  var sup = addSuperscript(footnote, num);
  linkSupToFootnote(sup, footnote);
  moveFootnoteToFooters(footnote, footers);
  linkFootnoteToSup(footnote, sup);
});

That is how you can use vanilla JavaScript to move and mange footnotes on the page.

1 Like