valeriangalliat / markdown-it-anchor Goto Github PK
View Code? Open in Web Editor NEWA markdown-it plugin that adds an `id` attribute to headings and optionally permalinks.
License: The Unlicense
A markdown-it plugin that adds an `id` attribute to headings and optionally permalinks.
License: The Unlicense
It would be nice to be able to control what attributes are set on the anchor.
For example I'd like to set an aria-label
so screenreader users get a useful name instead of whatever unicode symbol/icon is inside the anchor. This is kind of related to #58.
Rather than continually adding new options for each possible attribute could we support passing a permalinkAttrs
object as an option? E.g.
const md = require('markdown-it')()
.use(require('markdown-it-anchor'), {
permalinkAttrs: { 'aria-label': 'heading permalink' }
})
I think this would be relatively simple to implement:
const linkTokens = [
Object.assign(new state.Token('link_open', 'a', 1), {
attrs: [
['class', opts.permalinkClass],
['href', opts.permalinkHref(slug, state)],
+ ...Object.entries(opts.permalinkAttrs)
]
}),
Object.assign(new state.Token('html_block', '', 0), { content: opts.permalinkSymbol }),
new state.Token('link_close', 'a', -1)
]
Hi there,
I successfully use markdown-it-anchor until I try to make a production build using Webpack, along with UglifyJS plugin.
ERROR in dashboard.js from UglifyJs
Unexpected token: operator (>) [dashboard.js:98703,19]
I think I've identified what breaks minifying: the use of lambda functions (() => {}
)
Shouldn't this package be available as a 100% ES5 version ?
"TypeError: token.attrGet is not a function",
" at /home1/user/61916c91a4e81be8/node_modules/markdown-it-anchor/index.js:79:24",
" at Array.forEach (native)",
" at Array.<anonymous> (/home1/user/61916c91a4e81be8/node_modules/markdown-it-anchor/index.js:71:8)",
" at Core.process (/home1/user/61916c91a4e81be8/node_modules/markdown-it/lib/parser_core.js:51:13)",
" at MarkdownIt.parse (/home1/user/61916c91a4e81be8/node_modules/markdown-it/lib/index.js:493:13)",
" at MarkdownIt.render (/home1/user/61916c91a4e81be8/node_modules/markdown-it/lib/index.js:513:36)",
" at decorate (/home1/user/61916c91a4e81be8/node_modules/aglio-theme-olio/lib/main.js:422:44)",
" at Object.exports.render (/home1/user/61916c91a4e81be8/node_modules/aglio-theme-olio/lib/main.js:646:5)",
" at /home1/user/61916c91a4e81be8/node_modules/aglio/lib/main.js:133:20"
The following works fine. I seem to have installed v2.5.0
some time ago.
`-- [email protected]
`-- [email protected]
`-- [email protected]
`-- [email protected]
This new version fails.
└─┬ [email protected]
└─┬ [email protected]
└─┬ [email protected]
└── [email protected]
Please add enough test to verify the changes, and if some of them are breaking, always increment the major version, not the minor.
Opened a corresponding issue for aglio-theme-olio
so that they always use the exact version at danielgtaylor/aglio#319.
It shows only a white/blank page.
Given I have the following markdown string:
[[toc]]
# Introduction to American Novels
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore.
## Literary Analysis of _Catcher in the Rye_
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore.
## Understanding Race in *Huckleberry Finn*
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore.
This will render the following table of contents:
<div class="table-of-contents">
<ul>
<li>
<a href="#introduction-to-american-novels">Introduction to American Novels</a>
<ul>
<li><a href="#literary-analysis-of-catcher-in-the-rye">Literary Analysis of _Catcher in the Rye_</a></li>
<li><a href="#understanding-race-in-huckleberry-finn">Understanding Race in *Huckleberry Finn*</a></li>
</ul>
</li>
</ul>
</div>
<h1 id="introduction-to-american-novels">Introduction to American Novels</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore.
</p>
<h2 id="literary-analysis-of-catcher-in-the-rye">Literary Analysis of <em>Catcher in the Rye</em></h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore.
</p>
<h2 id="understanding-race-in-huckleberry-finn">Understanding Race in <em>Huckleberry Finn</em></h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore.
</p>
Notice that the ToC headers contain raw / unprocessed markdown (e.g. Literary Analysis of _Catcher in the Rye_
) whereas the actual headers have converted the markdown into HTML (e.g. Literary Analysis of <em>Catcher in the Rye</em>
).
I would expect to see the markdown be converted into HTML in both locations.
Looking at your code, I think the md
object may have access to the underlying rendering functions. For example, I wrote a markdown-it extension which did something like:
this.env.md.renderInline("Some text which _may have formatting_")
And this allowed me to process markdown text within my extension – which I think is what we want in this case as well.
I'm not 100% sure how this would work in your code, but I think it's possible (and probably pretty easy). If you need help, lemme know and could probably make a PR.
If I use markdown-it-toc after, your plugin just do nothing.
And if I use it before, your plugin add anchor, but something seems broken
<h2>
<a id="Installation_3"></a>
<a class="markdownIt-Header-anchor" href="#installation">#</a>
Installation
</h2>
Any idea how to get anchor links + toc ?
My options
markdownIt({
html: true,
linkify: true,
typographer: true,
highlight: (code, lang) => {
code = code.trim()
// language is recognized by highlight.js
if (lang && hljs.getLanguage(lang)) {
return hljs.highlight(lang, code).value
}
// fallback to auto
return hljs.highlightAuto(code).value
})
.use(markdownItToc)
.use(markdownItAnchor, {
permalink: true,
permalinkClass: "markdownIt-Header-anchor",
permalinkSymbol: "#",
permalinkBefore: true,
})
...\node_modules\markdown-it\lib\index.js:477
plugin.apply(plugin, args);
^
TypeError: plugin.apply is not a function
Seems like the babel upgrade uses exports.default
, which node does not load as a function:
$ npm install markdown-it-anchor
$ node
> require("markdown-it-anchor")
{ default:
{ [Function: anchor]
defaults:
{ level: 1,
slugify: [Function: slugify],
permalink: false,
renderPermalink: [Function: renderPermalink],
permalinkClass: 'header-anchor',
permalinkSymbol: '¶',
permalinkBefore: false } } }
Which breaks md.use(require('markdown-it-anchor'))
.
Build failed when integrated with React:
Creating an optimized production build...
Failed to compile.
Failed to minify the code from this file:
./node_modules/markdown-it-anchor/index.js:1
Read more here: http://bit.ly/2tRViJ9
I'm using markdown-it-attrs and I'd like to assign id to header. But anchor will overwrite the id, is it possible to ignore id slug when an id is already in place?
In your documentation you mentioned that by using slugify we can create custo id but it uses jquery.
SO I wonder is there any way to add custom prefix to Id?
Hi!
Any particular reason you use md.renderer
instead of Token.attrPush
?
The reason I ask, is because it would be nice to have access to the id
attribute from other plugins. Like the one I'm creating now: https://github.com/arve0/markdown-it-header-sections/blob/master/index.js#L17
When creating headers with special characters between words, is it expected that the special character will be changed to "-"
Create duplicated Headers (same name) inside a markdown file
# header & header
The anchor link for the header should be:
#header--header
It is generating the following anchor links:
#header-header
I’ve just found that 3.0.0 is out, however, I can’t find any reference to any changes, and I can’t seem to glean from the commit history exactly what’s changed. 2.5.1 and up are also missing from tags, so I can’t tell what changed between them either.
Is 3.0.0 really a major API change?
what a shame~ author, why not you translate Chinese?
One of my anchor is 'constructor', and it's turned into:
id="constructor-function Object() { [native code] }1"
My page url is : http://localhost:8080/#/component/srclock
but the anchor link is : http://localhost:8080/#/ji-chu-yong-fa
'component/srclok' part gone .
What problem is it would be? Who can give me some tips。
Running into "TypeError: plugin.apply is not a function" with version 5.2.3.
TypeError: plugin.apply is not a function
at MarkdownIt.push../node_modules/markdown-it/lib/index.js.MarkdownIt.use (vendors.._src_components_pages_blog__year__month__day__post.._src_components_pages_index.js:13325)
at _callee$ (._src_components_pages_blog__year__month__day__post.js:553)
at tryCatch (commons.app.js:6771)
at Generator.invoke [as _invoke] (commons.app.js:6997)
at Generator.prototype.<computed> [as next] (commons.app.js:6823)
at asyncGeneratorStep (vendors.app.js:31)
at _next (vendors.app.js:53)
at vendors.app.js:60
at new Promise (<anonymous>)
at vendors.app.js:49
Reproducible using this repo.
Hello, I have your package as dependency of aglio-theme-olio,
When I try to gulp build, I have a strange error:
/build/proj/api/node_modules/markdown-it-anchor/index.js:79
var slug = token.attrGet('id');
^
TypeError: token.attrGet is not a function
at /build/proj/api/node_modules/markdown-it-anchor/index.js:79:24
at Array.forEach (native)
at Array.<anonymous> (/build/proj/api/node_modules/markdown-it-anchor/index.js:71:8)
at Core.process (/build/proj/api/node_modules/markdown-it/lib/parser_core.js:51:13)
at MarkdownIt.parse (/build/proj/api/node_modules/markdown-it/lib/index.js:493:13)
at MarkdownIt.render (/build/proj/api/node_modules/markdown-it/lib/index.js:513:36)
at decorate (/build/proj/api/node_modules/aglio-theme-olio/lib/main.js:404:32)
at Object.exports.render (/build/proj/api/node_modules/aglio-theme-olio/lib/main.js:646:5)
at /build/proj/api/node_modules/aglio/lib/main.js:133:20
If I force install 2.6.0 version - all works ok.
I have the problem when I use it.
version: v5.0.1
device:macbook pro
description:
Using the markdown-it-anchor plugin and tui-editor combined, using react, when publishing the edited content, it is found that each release adds an anchor.
tui.Editor.markdownit.use(
mdAnchor,
{
level: [1, 2, 3, 4, 5, 6],
permalink: true,
permalinkSymbol: '#',
slugify: s => slug(s)
}
);
As shown in the figure below, multiple anchors will be generated, but I only need one.
I found the problem is that when you execute renderPermalink , you will continue to add anchors. So, is there any good solution?
... this would allow the caller to organize a table of contents with depth
opts.callback(token, { slug, title, tag:token.tag })
Hello,
After doing something like this in nuxt.config.js
:
router: {
base: process.env.NODE_ENV === 'production'
? '/zeta/'
: '/'
}
All header anchors break and direct the user to the root of the app.
Is there anything I can configure so it works as it should?
Live article that exhibits the behaviour:
https://lobotuerto.com/zeta/blog/vuejs-components-inside-markdown
Try clicking on anything on the TOC, or the § links besides the headers in the main content.
I'm interested in generating data for a table of contents and would like to make use of this plugin if possible. Ideally, I could provide a callback that was called with the heading level (or the token
itself), the title, and the slug. This would be enough to generate a table of contents using the id
attributes added by this plugin.
I could use the existing slugify
option to gather titles, but the return value may not be used as the id
(given the call to uniqueSlug
), and knowing the heading level would make for a nicer table of contents.
I could also use the existing renderPermalink
option, but that would require repeating a lot of what this plugin does to get the same permalink rendering (plus I'd have to redo the title
generation).
Would you be in favor of adding support for a callback(token, slug, title)
option (or the second arg could be info
with slug
and title
properties)?
Doesn't level
works as opposite as its supposed to?
For example, i want to give an id attribute to my main headers, h1, h2 and h3, but i can't use the level
attribute to control that. How i'm supposed to exclude the h4 ones? It should work the opposite :/
level
: 3 <- minimum header level of 3 to apply the functionality.
But right now that's just adding the attributes to h3-h6 tags, which is... weird.
When creating duplicated headers, is it expected that the duplicated anchor links will increase starting from "xxxx-1".
It starts from "xxxx-2".
Create duplicated Headers (same name) inside a markdown file
# header
# header
# header
The anchor link for each header should be:
#header
#header-1
#header-2
It is generating the following anchor links:
#header
#header-2
#header-3
I'm tring to add custom attribute to the anchor link but it is not working. This is the code I'm using:
import markdownIt from 'markdown-it';
import markdownItAnchor from 'markdown-it-anchor';
const md = new markdownIt().use(markdownItAnchor, {
permalinkAttrs: () => ({ "aria-hidden": false })
});
I've cloned this repository and when I run the tests with yarn test
it returns an error.
Have you considered allowing id
's to be applied to paragraphs---not just headings?
You could implement it in a similar way as you do for headings.
slugify
in the same wayid
with as many words as the paragraph has
id
with those four wordsid
from thatConsider the following Markdown source:
# Heading <!-- What `markdown-it-anchor` does already -->
This is a paragraph. <!-- Four words only -->
This is yet another paragraph. <!-- Five words -->
This is yet another paragraph that has more than five words. <!-- More than five words, and the first five are the same as the above example paragraph -->
This could be parsed to the following:
<h1 id="heading">Heading</h1> <!-- Expected default/current behavior -->
<p id="this-is-a-paragraph">This is a paragraph.</p> <!-- Since the Markdown source is a paragraph with only four words, those four words are incorporated into the `id` -->
<p id="this-is-yet-another-paragraph">This is yet another paragraph.</p> <!-- Five words, so the `id` gets those exact five words -->
<p id="this-is-yet-another-paragraph-that">This is yet another paragraph that has more than five words.</p> <!-- The Markdown source has more than five words, so the first five would be incorporated into the `id`. But since the previous paragraph would have that same `id`, `markdown-it-anchor` should add a sixth word. If another `id` with that string exists already, add another word (so the `id` would have seven words), etc. -->
Dave Winer made this concept famous: WinerLinks (there's even a WordPress plugin)!
But I know markdown-it-anchor
could have a more intelligent approach than the WordPress plugin.
I'd like to know your thoughts :)
Hi,
Is it possible to work in Angular?
I did this below:
import * as MarkdownAnchor from 'markdown-it-anchor';
But it doesn't work when running.
Hey, could you please look into it? Thank you.
Bigfoot.js is a JavaScript tool used to render footnote popups in lieu of regular-style 'bounce-around-the-page' footnotes.
I am using Casey Liss's node-based blogging engine Camel, and have found that when viewing the source for a rendered page, the footnote placeholders become <undefined>
in the HTML.
Here is a snippet parsed HTML showing the <undefined>
element, in the place of where a footnote identifier/class should be.
<li id="fn10" class="footnote-item"><undefined>And the pictures in the article only show the flappy box <a href="#fnref10" class="footnote-backref">↩</a><undefined></li>
Ideas?
I'm using markdown-it v10.0.0 and markdown-it-anchor v5.2.7. My code is
const md = require('markdown-it')({ linkify: true, xhtmlOut: true, typographer: true });
md.use(require('markdown-it-anchor'), { permalink: true, permalinkBefore: true, permalinkSymbol: '§' })
the error is: Uncaught TypeError: plugin.apply is not a function
. And the error still exist when the options { permalink: true, permalinkBefore: true, permalinkSymbol: '§' }
is removed.
Does someone know what the problem is?
I followed this, https://babeljs.io/docs/setup/#installation, and then include
md.use(require('markdown-it-anchor/lib'), {})
seems solved const
issue when including into project which do not have babel on node_modules
folder.
For some reason, the anchor links are only working properly in chrome. When I test in Firefox or Safari, the page is scrolled to the top.
Running into "TypeError: plugin.apply is not a function" with version 5.2.4.
./node_modules/markdown-it/lib/index.js.MarkdownIt.use
./node_modules/markdown-it/lib/index.js:496
493 | **/
494 | MarkdownIt.prototype.use = function (plugin /*, params, ... */) {
495 | var args = [ this ].concat(Array.prototype.slice.call(arguments, 1));
> 496 | plugin.apply(plugin, args);
497 | return this;
498 | };
499 |
I installed vuepress today,but the terminal show this error:
Install fail! Error: GET https://registry.npm.taobao.org/markdown-it-anchor/download/markdown-it-anchor-5.1.0.tgz response 404 status
How to fix it?
I use it as the README.md said, but got such errors:
const md = require('markdown-it').use(require('markdown-it-anchor'))({
^
TypeError: require(...).use is not a function
at Object.<anonymous> (D:\workspace\mobi.css\gulpfile.js:15:35)
at Module._compile (module.js:571:32)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:488:32)
at tryModuleLoad (module.js:447:12)
at Function.Module._load (module.js:439:3)
at Module.require (module.js:498:17)
at require (internal/module.js:20:19)
at Liftoff.handleArguments (D:\workspace\mobi.css\node_modules\gulp\bin\gulp.js:116:3)
at Liftoff.<anonymous> (D:\workspace\mobi.css\node_modules\liftoff\index.js:198:16)
I think the README.md should update to something like this:
const md = require('markdown-it')()
.use(require('markdown-it-anchor'), opts)
Seems that _
is valid URL part and should not be replaced with -
.
Of course we can pass own slugify
but I guess that default behavior should not replace it.
If possible, could we get permalinks to render just like GitHub does for Readme's?
e.g. the hover and the entire header is a link itself?
It turns out that the best way to handle a fixed top toolbar for anchors (ie creating a top offset to avoid scrolling to a position under the toolbar), is to use a target anchor rather than a header id for the target.
So I've forked markdown-it-anchor
to accommodate that. Thought you might be interested.
Basically: I've modified the processing logic as follows:
if (slug == null) {
slug = uniqueSlug(opts.slugify(title), slugs)
token.attrPush(['class',opts.headerClassName])
token.attrPush(['style','position:relative;padding-left:16px;margin-left:-16px;'])
if (opts.useTargetlink) {
opts.renderTargetlink(slug, opts, state, tokens.indexOf(token),title,token.tag)
} else {
token.attrPush(['id', slug])
}
}
if (opts.permalink) {
opts.renderPermalink(slug, opts, state, tokens.indexOf(token))
}
some new options:
let defaults = {
level: 1,
slugify,
permalink: false,
renderPermalink,
permalinkClass: 'header-anchor',
permalinkSymbol: '¶',
permalinkBefore: false,
permalinkHref,
// new...
useTargetlink:false,
renderTargetlink,
targetlinkClass: 'target-anchor',
headerClassName:'content-header',
}
To create the renderTargetLink I just copied and modified your renderPermalink.
Congrats on a well-written utility! It was very straightforward to fork and extend it.
Just thought you might be interested in case you want to accommodate this use case in your excellent plugin.
I believe that adding aria-hidden
is incorrect for these links. These links should be accessible and would be useful to screen reader users too!
Notably this throws an error when using the axe-core accessibility linting tool.
Violation of "aria-hidden-focus" with 6 occurrences!
Ensures aria-hidden elements do not contain focusable elements. Correct invalid elements at:
The options to fix here are to remove aria-hidden
(thus, this issue) or to set tabindex="-1"
on the link, which wouldn’t be right either. I want these links to be focusable.
Thank you!
When rendering a document with headers that are below the specified level
, an error is thrown. The below example illustrates the problem.
If the # Title
declaration is removed, the error is no longer thrown. So it appears that headers below the specified level cause this error.
markdownIt = require('markdown-it')
content = """
# Title
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
## Sub Title
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
### Sub Sub Title
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
"""
rendered = markdownIt()
.use require('markdown-it-anchor'), level: 2, permalink: true
.render(content)
console.log rendered
Would be great to see this available packaged up on bower, preferably precompiled to ES5.
Edit: I just saw it's on npm, this only leaves bower then.
Is there an option to use nesting when generating slugs? ie:
# Tacos
## Beef
# Burritos
## Beef
resulting in:
<h1 id="tacos">Tacos</h1>
<h2 id="taco-beef"Beef</h2>
<h1 id="burritos">Burritos</h1>
<h2 id="burritos-beef"Beef</h2>
@valeriangalliat Is it possible to use a Font Awesome character for the permalink symbol? If so, what’s the syntax? http://d.pr/i/15xmx
I tried’<i class="fa fa-link"></i>’
with and without the single quotes, but it gets literally parsed as all that code :/
— Anthony Craig (@ToniWonKanobi) Jul 27, 2015
Currently running "npm audit" on/with current package shows high vulnerability error.
I've added this plugin to my rollup build (by npm) and I get the following error when enabling the permlinks:
TypeError: linkTokens[position[(!opts.permalinkBefore)]](...) is not a function
This happens in line https://github.com/valeriangalliat/markdown-it-anchor/blob/master/index.js#L33
The rollup output of those lines are:
linkTokens[position[!opts.permalinkBefore]](space())
(ref = state.tokens[idx + 1].children)[position[opts.permalinkBefore]].apply(ref, linkTokens);
a simple semicolon after the first line would solve this issue:
linkTokens[position[!opts.permalinkBefore]](space());
.md
## [# 添加权限](#ss)
result
<h2><a href="#ss"># 添加权限</a></h2>
.md
## [# 添加权限role](#ss)
result
<h2 id="role-2"><a href="#ss"># 添加权限role</a></h2>
is there a way to solve?
Right now, the anchor is always added before the heading text:
<!-- Currently -->
<h1><a href="...">P</a>I am a header</h1>
<!-- What I would like to see -->
<h1>I am a header<a href="...">P</a></h1>
This makes is easy to hide the anchor until mouse over and doesn't require shifting headings outside of their container to keep alignment on the left side (for LTR text). Ultimately it should probably just be an option.
Is this something you'd be willing to add? Otherwise I think this plugin is great!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.