Continuous Footnote Numbering in Ghost

Currently, when using Markdown, Ghost creates ‘footnotes’ that are actually section endnotes, with each Markdown card counting as a ‘section’ — this is not exactly ideal.

This issue has come up a number of times on this forum (see, e.g., this thread from 2019).

Instead of simply making a feature request, I have gone ahead and created a script that will do this automatically for anyone who would like to have (more) functional footnotes in Ghost when using Markdown.

Here is 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));
	}
});

I have a short post about this script on my site, but the essence of what it does is quite simple:
It renumbers footnotes across Markdown cards, merges them into a single div, and then ensures all the links work.

I do most of my writing in Markdown, and I do like segmenting longer articles using multiple cards, but I was decidedly not a fan of the mid-article ‘footnotes’ that resulted from this. Until a more extensive footnote system is integrated into Ghost, this seems like a good workaround.

1 Like

Thanks sharing this @mahler. Maybe this approach could form the inspiration for an official solution to the footnotes problem.

At least, it seems like it could be added to a theme that targets content that’s more likely to have footnotes.

1 Like

This is a great solution for markdown fans who want to post to web. Because it won’t run in email, it isn’t a great option for newsletters.

1 Like

Is there a way this could be done server-side in templating logic rather than client-side HTML?

This is true and a good consideration for those who are more focused on a newsletter. I suppose this would need to be integrated into the core in some way to resolve the newsletter issue (or the newsletter version of posts could just hide footnotes, as a[n admittedly sub-optimal] solution).

1 Like