Ben Oliver

technology

Improving link display for print - Jekyll edition

28 March 2023

When I’m making websites I often like to (and sometimes have to) think about print styles. That is, how the page looks when you click ‘print’. What’s the best way to display links when printed on a piece of paper?

By default the browser puts the link next to where it appears on the page. It looks like this:

The full URL next to a link
A List Apart1

What I like instead is to put a footnote at the bottom of the page (print only) for each link. So it looks like this:

A printed version of an article on this site with a list of links at the bottom

For a long time I used code from Adrian Rosselli2 who in turn improved on the 2005 article from Aaron Gustafson at A List Apart3.

The above uses javascript, and it’s always niggled me that I could probably generate it all when I build the website, avoiding the use any javascript at all. Here’s how I did it for Jekyll.

First, put the following code in the _plugins directory and call it link-numbering.rb

require 'nokogiri'
require 'uri'

module Jekyll
  class LinkNumbering < Liquid::Block

    def initialize(tag_name, markup, tokens)
      super
    end

    def render(context)
      output = super
      doc = Nokogiri::HTML.fragment(output)

      site = context.registers[:site]

      # Get the page object from the context
      page = context.registers[:page]
      page_links = context.registers[:page_links] ||= {}
      page_link_counter = page_links[page['id']] ||= 0

      doc.css('a:not(.noprint)').each do |link|
        page_link_counter += 1
        sup = Nokogiri::XML::Node.new('sup', doc)
        sup['class'] = 'footnote'
        sup.content = page_link_counter.to_s
        link.after(sup)
        # Add the link to the page links hash
        page_links[page['id']] = page_link_counter
      end

      # Generate the list of links
      link_list = doc.css('a:not(.noprint)').map { |link| link['href'] }
      if link_list.any?
        link_container = Nokogiri::XML::Node.new('aside', doc)
        link_container['id'] = 'LinkContainer'

        link_list_element = Nokogiri::XML::Node.new('ol', doc)
        link_list_element['id'] = 'LinkList'

        link_list.each do |link|
          link_item_element = Nokogiri::XML::Node.new('li', doc)
          # Check if the link is absolute or relative using URI module
          site_url = site.config['url']
	  uri = URI.parse(link)
	  if uri.relative?
	    # Prepend the site URL if the link is relative
	    link = "#{site_url}#{link}"
	  end
          link_item_element.content = link
          link_list_element.add_child(link_item_element)
        end

        link_container.add_child(link_list_element)
        doc.add_child(link_container)
      end

      doc.to_html
    end
  end
end

Liquid::Template.register_tag('link_numbering', Jekyll::LinkNumbering)

In short:

  1. It counts up the links that don’t have the class noprint and are inside a link_numbering block
  2. It puts a number after every applicable link in the page
  3. It generates a list of links into an <aside id="LinkContainer"> element.

Then in your template you need to wrap your content e.g.:


{% link_numbering %}
    {{ content }}
{% endlink_numbering %}

You then need to hide the stuff in your CSS:

sup.footnote, #LinkContainer {
    @apply hidden;
}

Then make it show up when you click print, also in your CSS:

@media print {
    #LinkContainer{
        @apply block;
    }
}

I’ve excluded all the extra styling because you probably want to customise it, but you can see mine for yourself in the CSS of this page, and in Rosselli’s original post (he breaks the list into two columns which is a nice idea).

Remember, if there are any links in your post that you don’t want to appear in the list of links, just use the noprint class e.g. <a href="www.example.com" class="noprint">Example link</a>

Reply by email