Links List for Print Styles

Example of a couple printed pages showing the links list. I should qualify that I started writing this script and CSS, based on another experiment of mine, before I saw Aaron Gustafson’s 2005 ALA post Improving Link Display for Print. He uses similar techniques 12½ years ago that I use here, but with different syntax. Because scripting and styling has changed since then. Just a little.

Anyway, on to the post…

I have long advocated for making useful print styles and providing users with pages that work on paper without eating their printer ink. My default styles typically have a rule to embed the URL of all links directly after the link text. The problem is that it can eat a lot of ink and not be very usable on paper. Collecting all the links at the end of a page as plain text footnotes mitigates this. Users who want to save paper can choose to not print the final page(s) with the links.

I have written a function that you can drop into your site. You can choose how to call the function, such as when a user hits a print button, or when the page finishes, loading, or some other trigger. You can change where it pulls the content and writes the links by modifying the selectors in the querySelectorAll().

See the Pen Links List on Print by Adrian Roselli (@aardrian) on CodePen.

Assumptions

The script looks for all the links that live in the page’s <main> element. If you need to pull links that only live in a nested part of the <main> or you are supporting a pre-HTML5 site, then adjust the selector var links = document.querySelectorAll("main a[href]");.

The script also assumes that the page won’t break when an <aside> is stuffed into the <main> at the end, and that it can take an <h2> and <ol> without causing any problems.

It assumes that stuffing a <sup class="footnote"> after every link will not be a problem. for your page.

This also assumes that your users are American, using American-sized paper and American measurements, and American numbering (no, that’s not a thing).

Finally, since I wrote this for a client with text-heavy pages with no images, if you want to use it on a page with linked images then you will want to tweak the script and styles. I dropped a floated image into the page to show it. You may want to switch between portrait and landscape in your print preview.

Features

The CSS uses column-count with page width media queries in an effort to span the list of links across columns to save paper. You can adjust it to account for longer or shorter links.

You can display the footnotes and link container by toggle the display property on the sup.footnote, #LinkContainer selector. Note that none of those are clickable as this is meant for print.

The script strips the protocol to keep the links shorter. If Gopher is your thing, then adjust var schemeMatch to include it.

The Code

For when the embedded CodePen breaks or you just want to look it over.

JavaScript

There are no dependencies. No libraries. Nothing. Just plain vanilla JavaScript. I scattered comments in there; I hope they are useful.

function getLinks() {
  try {
    // Get all the links
    var links = document.querySelectorAll("main a[href]");

    // Create emtpy arrays for later population.
    var linkHrefs = [];
    var linkNums = [];

    // If there is already a links box, remove it.
    var node = document.getElementById("LinkContainer");
    if (node) {
      if (node.parentNode) {
        node.parentNode.removeChild(node);
      }
    }

    // Define the links container.
    var mainRegion = document.querySelector("main");
    var aside = document.createElement("aside");
    aside.id = "LinkContainer";

    // Define its heading.
    var h2 = document.createElement("h2");
    h2.innerText = "Links in This Page";

    // Define the list container.
    var orderedlist = document.createElement("ol");
    orderedlist.id = "LinkList";

    // Insert the new elements.
    mainRegion.appendChild(aside);
    var LinkContainer = document.getElementById("LinkContainer");
    LinkContainer.appendChild(h2);
    LinkContainer.appendChild(orderedlist);

    // Loop through the links
    for (var i = 0; i < links.length; i++) {
      // Create the superscript footnote reference
      var noteNum = document.createElement("sup");
      noteNum.setAttribute("class", "footnote");
      noteNum.innerText = i + 1;

      // Add it as a following sibling to the link
      links[i].parentNode.insertBefore(noteNum, links[i].nextSibling);

      // Get each link and replace the URL scheme identifier
      //var url = links[i].href.substr(links[i].href.indexOf('://')+3);
      var schemeMatch = /^mailto:|(https?|ftp):\/\//;
      var url = links[i].href.replace(schemeMatch, "");

      // Add a list item for each link.
      var li = document.createElement("li");
      li.innerText = url;
      document.getElementById("LinkList").appendChild(li);
    }

    // Add a class to the body to undo default link print styles
    var thisBody = document.querySelector("body");
    thisBody.setAttribute("class", "linklist");

    // Done
  } catch (e) {
    console.log("getLinks(): " + e);
  }
}

getLinks();

CSS

As I noted above, you can and should tweak this to suit your needs. My use case was pretty specific. This chunk also excludes some of the base styles I use.

sup.footnote {
  padding-left: 0.2em;
  font-size: 75%;
  line-height: 0;
}
#LinkContainer {
  overflow-wrap: break-word;
}
sup.footnote,
#LinkContainer {
  display: none;
}
#LinkContainer ol {
  padding-left: 2em;
}
@media print {
  #LinkContainer {
    display: block;
  }
  sup.footnote {
    display: inline;
  }
}
@media screen and (min-width: 68em), print and (min-width: 6in) {
  #LinkContainer ol {
    column-count: 2;
    column-gap: 4em;
  }
}
@media all and (min-width: 80em), print and (min-width: 9in) {
  #LinkContainer ol {
    column-count: 3;
    column-gap: 4em;
  }
}

Wrap-up

This has a very specific use case. It is probably not a good fit for many sites. It does offer an alternative to when a[href]::after { content: " [" attr(href) "]"; } proves to be too overwhelming on a site.

10 Comments

Reply

12 ½ years! Holy crap!

In response to Aaron Gustafson. Reply

It’s like time didn’t even exist then. Or document.querySelector.

Reply

has easy way to do that? like plugin or library?

Arka web design; . Permalink
In response to Arka web design. Reply

None that I have ever seen.

Reply

I have long used and long loved these techniques. The two-column output makes a lot of sense and saves a lot of space over one-column when pages usually have a lot of links. I have one suggestion to make. Sometimes the target content area will have no links, and when a page like this is printed, the “Links in This Page” heading prints—confusingly with nothing underneath. So before the for loop I created a flag—a new variable ( var listIsEmpty = 1; ) then just after appendChild(li) and within the for loop I cleared the flag ( listIsEmpty = 0; ). Then after the for loop if ( listIsEmpty == 1 ) then LinkContainer.remove();. This was they way I knew, so if there’s a better or easier way, please let me know.

In response to Frank Farm. Reply

That is a great edit. I make the mistaken assumption that any page that has this function would have links, but that may not be the case. Thanks!

Reply

One more suggestion. I noticed that this script seemed to be overwriting existing classes on the body element, so I changed thisBody.setAttribute("class", "linklist"); to thisBody.classList.add("linklist"); and that seemed to fix it.

In response to Frank Farm. Reply

Another great fix, thanks!

Reply

I’ve been doing this for a few years now, and this post really helped. Thank you!

FWIW Recently I developed a way to automatically generate the links in Jekyll when the site builds.

https://www.bfoliver.com/2023/03/28/improvelinkdisplay/

In response to Ben Oliver. Reply

Nice!

Leave a Comment or Response

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>