No More Mid-Article ‘Footnotes’ When Using Markdown

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        

There are many ways to add notes to an article, a book, a post, et cetera. You can use footnotes (notes that are placed at the bottom of a page) or you can use endnotes (notes that are placed at the end of a section or the end of the work). Technically, the Web has no standard for footnotes/endnotes (collectively, "notes"), but there are many workarounds.

Typically, notes are inserted into content on the Web via the use of some combination of the <sup>, <a>, and <ol> and <li> elements (respectively, superscript, the link, the ordered list, and the list item). Hence, this footnote[1] (once rendered — more on that, infra), will actually be the following code: <sup><a id="fnref1" href="#fn1">[1]</a></sup>. (Feel free to inspect the page’s code to verify.)


  1. This is a demonstration footnote. ↩︎

However, in Ghost[1], there is a bit of a wrinkle when using Markdown: Footnotes are treated, more or less, as section endnotes instead of as true endnotes (or even true footnotes, if we think of an article as the electronic equivalent of a single ‘page’), which is to say that your footnotes, so carefully placed within your Markdown content, will be rendered in the middle of your article or post (instead of at the end) — not exactly ideal.

At present, Ghost has no workaround for this. The issue has been raised a number of times over on the Ghost forum, but, as this is not considered a bug, the only real response is ‘working as intended’.[2] There are workarounds, of course:

  1. Convert your Markdown content into HTML (there are many free online converters).
  2. Paste your Markdown content as a giant (or not so giant) blob into a single Markdown card, so the end of that card is, in fact, the end of the article.
  3. Accept the mediocrity of ‘inline’ ‘footnotes’.

Except, there is one other option: Code an actual solution.


  1. And likely other platforms as well. ↩︎

  2. It's not a bug, if you call it a feature. ↩︎

Which is precisely what I’ve done. The following code does a number of things; in simplified fashion, it:

  1. Iterates through your page and renumbers all your footnotes (so that they are sequential for the article, not per section).
  2. Iterates through your page, collects all your footnotes, and merges them into a single new div (class: endnotes) at the end of your article.
  3. Reworks all the footnote reference and back-reference links to ensure that they still work (i.e., you can get to the footnote from the reference and back to the reference from the footnote).

Here’s the code:

$(document).ready(function () {
	// Check if the body element has the class 'tag-scripting-footnotes-renumber'
	if ($('body').hasClass('tag-scripting-footnotes-renumber')) {
		// Initialize variables to keep track of footnote counts
		var footnoteCount = 0;
		var referenceCount = 0;

		// Create and append the endnotes div if it doesn't exist
		if ($('div.endnotes').length === 0) {
			var endnotesDiv = $('<div class="endnotes"></div>');
			$('div.post-content').append(endnotesDiv);
		}

		// Create an ordered list for footnotes
		var olFootnotes = $('<ol class="footnotes-list"></ol>');

		// Iterate through each footnote reference and renumber them
		$('sup.footnote-ref a').each(function () {
			// Increment the footnote count
			footnoteCount++;

			// Generate the IDs and HREFs for the footnote and its reference
			var footnoteId = 'fn' + footnoteCount;
			var footnoteRefId = 'fnref' + footnoteCount;

			// Update the ID, HREF, and content for the footnote reference
			$(this).attr('id', footnoteRefId).attr('href', '#' + footnoteId).text('[' + footnoteCount + ']');
		});

		// Iterate through each footnote and move it to the endnotes div
		$('section.footnotes').each(function () {
			var section = $(this);
			// Iterate through the footnotes in this section
			section.find('ol.footnotes-list li.footnote-item').each(function () {
				// Increment the reference count for the back references
				referenceCount++;

				// Generate the IDs for the footnote and its back reference
				var footnoteId = 'fn' + referenceCount;
				var backReferenceId = 'fnref' + referenceCount;

				// Update the ID for the footnote
				$(this).attr('id', footnoteId);

				// Create a back reference element
				var backReference = $('<a class="footnote-backref" href="#' + backReferenceId + '">↩</a>');

				// Remove any existing back references within the p element
				$(this).find('p a.footnote-backref').remove();

				// Append the back reference to the footnote item
				$(this).find('p').append(backReference);

				// Append the footnote to the ordered list
				olFootnotes.append($(this).detach());
			});
		});

		// Append the ordered list of footnotes to the endnotes div
		$('div.endnotes').append($('<section class="footnotes"></section>').append(olFootnotes));
	}
});

Usage notes: This code does not execute unless the post in question has a tag of “Scripting: Footnotes Renumber” on it. (Of course, Ghost converts that punctuation into a simple dash, so do feel free to use anything that turns into tag-scripting-footnotes-renumber once rendered by Ghost.) Alternatively, you could modify the conditional in the code (if ($('body').hasClass('tag-scripting-footnotes-renumber'))) to check for something else (or just remove it and have the code execute on all of your posts).

I have tested this on my own site (this post is actually using the script), and it works quite well (at least under Ghost v5.65.0). I have not been able to find any bugs or strange behavior, but please let me know if you do. Hopefully the Ghost development team will integrate this (or some equivalent) into Ghost core in the near future (see this thread on the Ghost forum).

Inline ‘footnotes’ are, after all, a bit ridiculous.