Adding anchor links to the site

Posted on October 5, 2019 | pandoc blog setup haskell

Anchor links are cool. They make it easy to get the link to a specific section in webpage to share with others. Just hover over a heading and get the clickable link with the URL of the heading. This page lists my experiments with adding Anchor links to this blog.

Using AnchorJS #

I used AnchorJS in my first attempt to add anchor links. It was very easy to integrate.

  1. Include anchor.js script in the html page.

    <script src="/js/jquery-3.4.1.min.js"></script>
    <script src="js/apply-styles.js"></script>
    <!-- For anchor links -->
    <script src="js/anchor-4.2.0.min.js"></script>
  2. and call anchors.add() in the $(document).ready call back. This was the location I used for the other style updates.

    /* apply-styles.js */
     $(document).ready(function() {
       anchors.add();
     }
  3. There is no Step 3

Moving away from AnchorJS #

AnchorJS worked splendidly for my blog (for the audience of 1 😃). However the anchorJS website includes an explicit warning against calling anchors.add() in the $(document).ready call back.

https://www.bryanbraun.com/anchorjs/#dont-run-it-too-late

Don’t add anchors on later events, like $(document).ready() or window.onload as some browsers will attempt to jump to your ID before AnchorJS can add it to the page. For more details, see github issue #69.

This got me thinking. As my blog is static website, I figured I can add the anchors when building the website and remove the runtime component.

This blog is built using Hakyll which uses Pandoc to convert from Markdown to HTML. I implemented a Pandoc filter to add anchors to headers

-- Create an anchor based on the string
-- add anchorjs-link class for CSS manipulations
-- Use "#" as the display for the anchor
-- Append linkId to "#" to create the link target
addAnchor :: String -> Inline
addAnchor linkId = Link ("", ["anchorjs-link"], []) [Str " #"] ("#" ++ linkId, "")

-- Add a anchor link to each html header
-- Leave everything else as is
modHeader :: Block -> Block

-- We are matching against the header pattern from pandoc-types
-- We take the linkId and use it to add anchor links
--         Header Int Attr [Inline]
modHeader (Header n y@(linkId, _, _) xs) = (Header n y (xs ++ [(addAnchor linkId)]))
-- fallback for all others
modHeader x                              = x

-- Walk the Pandoc AST and apply modHeader on each node
anchorLinks :: Pandoc -> Pandoc
anchorLinks = walk modHeader

This script also needs CSS changes to hide the anchors by default and reveal them on hover.

/* Make the anchor transparent by default */
.anchorjs-link {
    opacity: 0;
    text-decoration: none;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* Make the anchor opaque on hover */
*:hover > .anchorjs-link,
.anchorjs-link:focus  {
    opacity: 1;
}

Here are the links to haskell source and css file.

References #

  1. AnchorJS - https://www.bryanbraun.com/anchorjs/
  2. Use of Mouse-Over Paragraph Marker in Headlines for Permalink - https://ux.stackexchange.com/questions/36304/use-of-mouse-over-paragraph-marker-in-headlines-for-permalink