# markdown-it-anchor [![npm version](http://img.shields.io/npm/v/markdown-it-anchor.svg?style=flat-square)](https://www.npmjs.org/package/markdown-it-anchor) > Header anchors for [markdown-it]. [markdown-it]: https://github.com/markdown-it/markdown-it English | [δΈζ (v7.0.1)](./README-zh_CN.md) ## Usage ```js const md = require('markdown-it')() .use(require('markdown-it-anchor'), opts) ``` See a [demo as JSFiddle](https://jsfiddle.net/9ukc8dy6/). The `opts` object can contain: | Name | Description | Default | |------------------------|---------------------------------------------------------------------------|----------------------------| | `level` | Minimum level to apply anchors, or array of selected levels. | 1 | | `slugify` | A custom slugification function. | See [`index.js`](index.js) | | `uniqueSlugStartIndex` | Index to start with when making duplicate slugs unique. | 1 | | `permalink` | A function to render permalinks, see [permalinks] below. | `undefined` | | `callback` | Called with token and info after rendering. | `undefined` | | `tabIndex` | Value of the `tabindex` attribute on headings, set to `false` to disable. | `-1` | [permalinks]: #permalinks All headers greater than the minimum `level` will have an `id` attribute with a slug of their content. For example, you can set `level` to 2 to add anchors to all headers but `h1`. You can also pass an array of header levels to apply the anchor, like `[2, 3]` to have an anchor on only level 2 and 3 headers. If a `permalink` renderer is given, it will be called for each matching header to add a permalink. See [permalinks] below. The `callback` option is a function that will be called at the end of rendering with the `token` and an `info` object. The `info` object has `title` and `slug` properties with the token content and the slug used for the identifier. Finally, we set by default [`tabindex="-1"`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex) on headers. This marks the headers as focusable elements that are not reachable by keyboard navigation. The effect is that screen readers will read the title content when it's being jumped to. Outside of screen readers, the experience is the same as not setting that attribute. You can override this behavior with the `tabIndex` option. Set it to `false` to remove the attribute altogether, otherwise the value will be used as attribute value. ## User-friendly URLs Starting from v5.0.0, markdown-it-anchor dropped the [`string`](https://github.com/jprichardson/string.js) package keeping it's core value of being an unopinionated and secure library. Yet, users looking for backward compatibility may want the old `slugify` function: ```sh npm install string ``` ```js const string = require('string') const slugify = s => string(s).slugify().toString() const md = require('markdown-it')() .use(require('markdown-it-anchor'), { slugify }) ``` Another popular library for this is [`@sindresorhus/slugify`](https://github.com/sindresorhus/slugify), which have better Unicode support and other cool features: ```sh npm install @sindresorhus/slugify ``` ```js const slugify = require('@sindresorhus/slugify') const md = require('markdown-it')() .use(require('markdown-it-anchor'), { slugify: s => slugify(s) }) ``` ## Explicit `id`s You might want to explicitly set the `id` attribute of your headings from the Markdown document, for example to keep them consistent across translations. markdown-it-anchor is designed to reuse any existing `id`, making [markdown-it-attrs](https://www.npmjs.com/package/markdown-it-attrs) a perfect fit for this use case. Make sure to load it before markdown-it-anchor! Then you can do something like this: ```markdown # Your title {#your-custom-id} ``` The anchor link will reuse the `id` that you explicitly defined. ## Table of contents Looking for an automatic table of contents (TOC) generator? Take a look at [markdown-it-toc-done-right](https://www.npmjs.com/package/markdown-it-toc-done-right) it's made from the ground to be a great companion of this plugin. ## HTML headings markdown-it-anchor doesn't parse HTML blocks, so headings defined in HTML blocks will be ignored. If you need to add anchors to both HTML headings and Markdown headings, the easiest way would be to do it on the final HTML rather than during the Markdown parsing phase: ```js const { parse } = require('node-html-parser') const root = parse(html) for (const h of root.querySelectorAll('h1, h2, h3, h4, h5, h6')) { const slug = h.getAttribute('id') || slugify(h.textContent) h.setAttribute('id', slug) h.innerHTML = `
All renderers share a common set of options: | Name | Description | Default | |---------------|---------------------------------------------------|------------------------------------| | `class` | The class of the permalink anchor. | `header-anchor` | | `symbol` | The symbol in the permalink anchor. | `#` | | `renderHref` | A custom permalink `href` rendering function. | See [`permalink.js`](permalink.js) | | `renderAttrs` | A custom permalink attributes rendering function. | See [`permalink.js`](permalink.js) | For the `symbol`, you may want to use the [link symbol](http://graphemica.com/π), or a symbol from your favorite web font. ### Header link This style wraps the header itself in an anchor link. It doesn't use the `symbol` option as there's no symbol needed in the markup (though you could add it with CSS using `::before` if you like). It's so simple it doesn't have any behaviour to custom, and it's also accessible out of the box without any further configuration, hence it doesn't have other options than the common ones described above. You can find this style on the [MDN] as well as [HTTP Archive] and their [Web Almanac], which to me is a good sign that this is a thoughtful way of implementing permalinks. This is also the style that I chose for my own [blog]. [MDN]: https://developer.mozilla.org/en-US/docs/Web [HTTP Archive]: https://httparchive.org/reports/state-of-the-web [Web Almanac]: https://almanac.httparchive.org/en/2020/table-of-contents [blog]: https://www.codejam.info/ | Name | Description | Default | |-------------------|-----------------------------------------------------------------------|---------------------------------------| | `safariReaderFix` | Add a `span` inside the link so Safari shows headings in reader view. | `false` (for backwards compatibility) | | | See [common options](#common-options). | | ```js const anchor = require('markdown-it-anchor') const md = require('markdown-it')() md.use(anchor, { permalink: anchor.permalink.headerLink() }) ``` ```html `) ``` While this still needs extra work like handling duplicated slugs and IDs, this should give you a solid base. That said if you really want to use markdown-it-anchor for this even though it's not designed to, you can do like npm does with their [marky-markdown](https://github.com/npm/marky-markdown) parser, and [transform the `html_block` tokens](https://github.com/npm/marky-markdown/blob/master/lib/plugin/html-heading.js) into a sequence of `heading_open`, `inline`, and `heading_close` tokens that can be handled by markdown-it-anchor: ```js const md = require('markdown-it')() .use(require('@npmcorp/marky-markdown/lib/plugin/html-heading')) .use(require('markdown-it-anchor'), opts) ``` While they use regexes to parse the HTML and it won't gracefully handle any arbitrary HTML, it should work okay for the happy path, which might be good enough for you. You might also want to check [this implementation](https://github.com/valeriangalliat/markdown-it-anchor/issues/105#issuecomment-907323858) which uses [Cheerio](https://www.npmjs.com/package/cheerio) for a more solid parsing, including support for HTML attributes. The only edge cases I see it failing with are multiple headings defined in the same HTML block with arbitrary content between them, or headings where the opening and closing tag are defined in separate `html_block` tokens, both which should very rarely happen. If you need a bulletproof implementation, I would recommend the first HTML parser approach I documented instead. ## Browser example See [`example.html`](example.html). ## Permalinks Version 8.0.0 completely reworked the way permalinks work in order to offer more accessible options out of the box. You can also [make your own permalink](#custom-permalink). Instead of a single default way of rendering permalinks (which used to have a poor UX on screen readers), we now have multiple styles of permalinks for you to chose from. ```js const anchor = require('markdown-it-anchor') const md = require('markdown-it')() md.use(anchor, { permalink: anchor.permalink[styleOfPermalink](permalinkOpts) }) ``` Here, `styleOfPermalink` is one of the available styles documented below, and `permalinkOpts` is an options object.aria-label
variantaria-describedby
and aria-labelledby
variants