AnchorJS

Add deep anchor links to your docs.

What are "deep anchor links"? Here are a few examples:

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
anchors.options.visible = 'always';
anchors.add('h3');

Paragraph Link

{}
anchors.options = {
  visible: 'always',
  placement: 'left',
  icon: '¶'
};
anchors.add('p');

See more examples

Used by

1Password logo ESLint logo Bootstrap logo Middleman logo Tailwind logo Bundler logo

Overview

Examples of deep anchor links from across the web.

AnchorJS lets you drop deep anchor links (like these) onto any webpage, and be on your way.

You don't need to set up IDs or worry about urls. AnchorJS will respect your IDs if you have them, and generate them if you don't.

It uses an attractive link icon by default, but you can customize the display via options and CSS styling. The examples demonstrate a few customization ideas.

Finally, AnchorJS is lightweight, accessible, and dependency-free.

Installation

Download AnchorJS using npm,

npm install anchor-js

... and then include it in your project:

import AnchorJS from 'anchor-js';

const anchors = new AnchorJS();
anchors.add();

You could also include it in your webpage via a CDN like CDNJS or jsDelivr,

<script src="https://cdn.jsdelivr.net/npm/anchor-js/anchor.min.js"></script>
<script>
  anchors.add();
</script>

...or import it globally with ES Modules:

import 'https://cdn.jsdelivr.net/npm/anchor-js/anchor.min.js';

anchors.add();

Basic usage

AnchorJS provides the anchors.add() method which takes a CSS selector (similar to jQuery) for targeting elements you want to deep-link. Here are some usage examples.

/**
 * Example 1
 * Add anchors to all h1's on the page
 */
anchors.add('h1');

/**
 * Example 2
 * Adds anchors to elements that have been assigned the class '.anchored'
 */
anchors.add('.anchored');

/**
 * Example 3
 * If no selector is provided, it falls back to a default selector of:
 * 'h2, h3, h4, h5, h6'
 */
anchors.add();

Don't run it too late!

You need to add anchors to the page early in the page load process if you want browsers to jump to the ID properly.

We recommend you call anchors.add() before the DOM finishes loading...

<!-- Add anchors before the closing body tag. -->
  <script>
    anchors.add();
  </script>
</body>

...or on DOMContentLoaded:

// Add anchors on DOMContentLoaded
document.addEventListener('DOMContentLoaded', function(event) {
  anchors.add();
});

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).

Options

You can set a number of options to customize how your anchors look:

Option Accepted Values Default Value Description
placement right
left
right right appends the anchor to the end of the element.
left places it to the left, in the margin.
visible hover
always
hover hover displays the anchor when hovering over the element.
always will always display the anchor link.
icon (any unicode character) Replace the default link icon with the character(s) provided. These are a few good options: #, , , and §.
class (any string) (none) Adds the provided class(es) to the anchor html.
base (any string) (none) Prepends the base URI to the anchor href. This is useful when adding anchors to pages with changing content.
truncate (any positive number) 64 Truncates the generated ID to the specified character length. Note: the length may not be exactly the same, if there are dangling spaces or hyphens to be trimmed.
ariaLabel (any text) Anchor Allows you to customize or translate the default aria-label ("Anchor"), for screenreaders that encounter the link.
titleText (any text) (none) Adds the text as title text to all anchors.

For example:

/**
 * Example 1
 * Add anchors to all h1s, h2s and h3s inside of #post.
 * Anchors will be always visible.
 */
anchors.options.visible = 'always';
anchors.add('#post h1, #post h2, #post h3');

/**
 * Example 2
 * Provide options as an object before adding anchors to the page.
 * Adds always-visible ¶ anchors in the left margin of each p tag inside .story
 */
anchors.options = {
  placement: 'left',
  visible: 'always',
  icon: '¶'
};
anchors.add('.story > p');

Advanced usage

Pages with a sticky navbar

Pages with a sticky navbar can sometimes cover the headings you are trying to link to.

To prevent this, you can add a few custom styles using the CSS :target pseudo-selector to add margins to targeted headings. Here's an example:

/**
* Create an invisible pseudo-element and make it the height of the
* sticky navbar so the jump takes you to a location above the link
*/
:target::before {
  content: "";
  display: block;
  margin-top: -80px;
  height: 80px;
  width: 1px;
}

Alternatively, you could use the newer scroll-margin-top property (as described in this post):

:target {
  scroll-margin-top: 80px;
}
Note: This may not be supported in all browsers. Please check caniuse.com for details.

Pages with changing content

It's common to have pages with changing content, like a page for your most recent blog posts, or a page for all posts with a given tag.

Adding anchors to pages like these can be problematic because the links could break when the content leaves that page. To avoid this issue, use the base option to ensure these anchors always point to the permalinked url for a post. For example:

// Note: you can often get the permalink url from your site's backend
anchors.options.base = "/2019/1/03/my-post";
anchors.add();

// Anchor url before: "#conclusion"
// Anchor url after: "/2019/1/03/my-post#conclusion"

Generating navigations

AnchorJS doesn't include methods for dynamically generating navigations (like a table of contents or jump nav). This is to keep AnchorJS lightweight and simple for the most common usecases.

However, AnchorJS does expose a list of all anchored elements at anchors.elements. This way, external code can look up the elements and use them to generate navigations (as shown in this example).

You can also use AnchorJS alongside a static navigation with pre-defined IDs (as is done in this example).

Section IDs

In some cases, you might want to link to existing section IDs instead of the heading element itself. You can instruct AnchorJS to do this with the data-anchor-id attribute:

<section id="section-1">
  <h3 data-anchor-id="section-1">Section 1</h3>
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed...</p>
</section>

<!-- ... -->

<script>
  anchors.add('h3');
</script>

This allows you to do things like highlight sections when your users jump to them.

Multiple sets of anchors

You can have multiple sets of anchors on one page, each with their own design. To do so, just create your own instances of the AnchorJS object:

var sidebarAnchors = new AnchorJS();
anchors.add('.main h2'); // The default instance.
sidebarAnchors.add('.sidebar h2'); // The new instance.

You can preset your instance with whatever options you like:

var sidebarAnchors = new AnchorJS({
  placement: 'left',
  icon: '¶'
});
sidebarAnchors.add('.sidebar h2');

Removing anchors

You can remove anchors with the anchors.remove() or anchors.removeAll() methods:

/**
 * Example 1
 * Remove anchors from all h1s on the page.
 */
anchors.remove('h1');

/**
 * Example 2
 * Remove all anchors from the page.
 */
anchors.removeAll();

Removing anchors with .remove() should be uncommon. If you simply want anchors on a more selective group, consider using the CSS :not() pseudo-class when you add them, like so:

/**
 * Example 2
 * Add anchors to all h2s, except for those with a class of "no-anchor".
 */
anchors.add('h2:not(.no-anchor)');

Chaining commands

You can chain commands of add() and remove() (since they return a copy of anchors), but it's usually more performant to lean on CSS when targeting elements:

/**
 * Example 1
 * Adds anchors to `.my-anchors` and `.my-other-anchors` except for those
 * with a class of `no-anchor`.
 */
anchors.add('.my-anchors').add('.my-other-anchors').remove('.no-anchor');

/**
 * Example 2
 * A more performant way to add anchors to the same classes in Example 1 above.
 */
anchors.add('.my-anchors:not(.no-anchor), .my-other-anchors:not(.no-anchor)');

Examples

Basic Link

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
anchors.options.visible = 'always';
anchors.add('h3');

Basic Link - Left

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
anchors.options = {
  visible: 'always',
  placement: 'left'
};
anchors.add('h3');

Styled Link

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
anchors.options.visible = 'always';
anchors.add('h3');
.anchorjs-link {
  color: #aaa;
}
.anchorjs-link:hover {
  color: #ff5231;
}

Paragraph Link

{}
anchors.options = {
  visible: 'always',
  placement: 'left',
  icon: '¶'
};
anchors.add('p');

Octothorp

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
anchors.options = {
  visible: 'always',
  icon: '#'
};
anchors.add('h3');

Unicode Icon 1

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
anchors.options = {
  visible: 'always',
  placement: 'left',
  icon: '§'
};
anchors.add('h3');

Unicode Icon 2

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
anchors.options = {
  visible: 'always',
  icon: '❡'
};
anchors.add('p');

Custom Text

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
anchors.options = {
  visible: 'always',
  icon: '# LINK'
};
anchors.add('h3');
.anchorjs-link {
  font-weight: 200;
  margin-left: 1em !important;
  padding-right: .375em !important;
  padding-left: .375em !important;
  font-size: .5em;
  border: 1px dashed #ffbaac;
  vertical-align: middle;
}

Custom Image

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
// Use an empty string ('') for the icon when styling the background with CSS.
anchors.options = {
  visible: 'always',
  placement: 'left',
  icon: ''
};
anchors.add('h3');
.anchorjs-link {
  width: 14px;
  height: 32px;
  margin-top: 6px;
  margin-left: -1.25em !important;
  background: url("img/mini-logo.png") no-repeat;
}

Link w/CSS Styling

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
anchors.options = {
  visible: 'always',
  placement: 'left'
};
anchors.add('h3');
.anchorjs-link {
  display: inline-block;
  height: 32px;
  width: 32px;
  border-radius: 50%;
  background-color: #ff5231;
  color: #fff;
  margin-top: 4px;
  margin-left: -1.4em !important;
  padding: 0 !important;
}
.anchorjs-link::after {
  position: absolute;
  top: -5px;
  left: 5px;
}

Icon Font

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
// Just pass in the octal code for your icon-font character.
anchors.options = {
  visible: 'always',
  icon: '\uF0C1'
};
anchors.add('h3');
.anchorjs-link {
  font-family: "your-icon-font";
}

SVG Icon

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
anchors.options = {
  visible: 'always',
  icon: ''
};
anchors.add('h3');
.anchorjs-link {
  display: inline-block;
  background: url("img/hyperlink.svg") no-repeat;
  margin-left: 8px !important;
  width: 14px;
  height: 24px;
}

Base64 Icon

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
anchors.options = {
  visible: 'always',
  placement: 'left',
  icon: ''
};
anchors.add('h3');
.anchorjs-link {
  background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIxMCIgdmlld0JveD0iMCAwIDIwIDEwIj48cGF0aCBmaWxsPSIjZmY1MjMxIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNSAwaC0yLjY5NWMuOTM3LjYyNSAxLjgxNiAxLjczOCAyLjA5IDIuNWguNTg1YzEuMjcgMCAyLjUgMS4yNSAyLjUgMi41cy0xLjI3IDIuNS0yLjUgMi41aC0zLjc1QzEwIDcuNSA4LjczIDYuMjUgOC43MyA1YzAtLjQ1LjEzNy0uODc5LjM1Mi0xLjI1SDYuNDA2Yy0uMDk3LjQxLS4xNTYuODItLjE1NiAxLjI1IDAgMi41IDIuNDggNSA0Ljk4IDVIMTVjMi41IDAgNS0yLjUgNS01cy0yLjUtNS01LTV6TTUuNjA1IDcuNUg1LjAyYy0xLjI3IDAtMi41LTEuMjUtMi41LTIuNXMxLjI3LTIuNSAyLjUtMi41aDMuNzVjMS4yMyAwIDIuNSAxLjI1IDIuNSAyLjUgMCAuNDUtLjEzNy44NzktLjM1MiAxLjI1aDIuNjc2Yy4wOTctLjQxLjE1Ni0uODIuMTU2LTEuMjUgMC0yLjUtMi40OC01LTQuOTgtNUg1QzIuNSAwIDAgMi41IDAgNXMyLjUgNSA1IDVoMi42OTVjLS45MzctLjYyNS0xLjgxNi0xLjczOC0yLjA5LTIuNXoiLz48L3N2Zz4=") no-repeat;
  margin-top: 15px;
  height: 16px;
  width: 6px;
  margin-left: -1em !important;
}

CSS Icon

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
anchors.options = {
  visible: 'always',
  placement: 'left',
  icon: ''
};
anchors.add('h3');
/* See also: nicolasgallagher.com/pure-css-gui-icons */
.anchorjs-link {
  border-color: #ff5231 #ff5231 transparent;
  border-width: 15px 7px 6px;
  border-style: solid;
  margin-top: 10px;
  font-size: 22px;
  padding-right: 0 !important;
  padding-left: 0 !important;
  margin-left: -1em !important;
}

Emoji

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
anchors.options = {
  visible: 'always',
  placement: 'left',
  icon: '⚓'
};
anchors.add('p');
.anchorjs-link {
  margin-left: -2.05em !important;
}

Hover examples

Basic Hover

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
anchors.add('h3');

Color Transition

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
anchors.add('h3');
.anchorjs-link:hover {
  color: #2500ad;
}
*:hover > .anchorjs-link {
  transition: color .25s linear;
}

Shift Out

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
anchors.options.placement = 'left';
anchors.add('h3');
.anchorjs-link {
  transition: all .25s linear;
}
*:hover > .anchorjs-link {
  margin-left: -1.375em !important;
}

Arrow

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
anchors.options.icon = '# LINK';
anchors.add('h3');
/* Based on https://codepen.io/corysimmons/pen/NPBXbe */
/* Vendor prefixes not included */
.anchorjs-link {
  position: relative;
  top: 4px;
  height: 36px;
  flex: 1;
  background-color: #ff5231;
  color: #fff;
  font-family: Helvetica, Arial, sans-serif;
  font-weight: 200;
  font-size: 1rem;
  margin-right: -6%;
  padding-right: 6%;
  padding-left: 42px !important;
  line-height: 38px;
  transition: all .5s ease;
  transform: translateX(100%);
}
.anchorjs-link::before {
  position: absolute;
  display: block;
  left: 0;
  width: 0;
  height: 0;
  content: "";
  border: 18px solid #fdfdfd; /* Background color */
  border-right-color: #ff5231;
  transition: all .5s ease;
}
h2 {
  display: flex;
}
*:hover > .anchorjs-link {
  transform: translateX(0);
}
*:hover > .anchorjs-link:hover {
  background: #ff806a;
}
*:hover > .anchorjs-link:hover::before {
  border-right-color: #ff806a;
}

Tooltip

Lorem ipsum dolor consectetur amet nulla elit. Vivamus luctus urna sed urna ultricies. Vivamus luctus urna sed.

{}
anchors.options = { icon: 'Permalink' };
anchors.add('h3');
/* tooltip box */
.anchorjs-link::after {
  display: inline-block;
  transition: opacity .25s linear;
  font-family: Verdana, sans-serif;
  font-size: .75ex;
  font-weight: 100;
  padding: .5ex 1.5ex;
  background-color: #444;
  color: #fff;
  border-radius: .6ex;
  vertical-align: .8ex;
}
/* tooltip arrow */
.anchorjs-link::before {
  content: "";
  display: inline-block;
  border-top: .3ex solid transparent;
  border-right: .5ex solid #444;
  border-bottom: .3ex solid transparent;
  vertical-align: .35ex;
}
.anchorjs-link:hover::after {
  background-color: #666;
}
.anchorjs-link:hover::before {
  border-right-color: #666;
}