From 1cc9882090461f4facd7d896d15f0dad060b6ea2 Mon Sep 17 00:00:00 2001 From: Tan Yuanhong Date: Fri, 10 Apr 2020 20:17:23 +0800 Subject: [PATCH 1/3] Move dummy span generation to build time using a plugin --- asset/js/setup.js | 13 ++++++------- src/plugins/fixedHeader.js | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 src/plugins/fixedHeader.js diff --git a/asset/js/setup.js b/asset/js/setup.js index c6b54ef24f..85c4ac4bb6 100644 --- a/asset/js/setup.js +++ b/asset/js/setup.js @@ -40,6 +40,12 @@ function setupAnchors() { top: calc(-${headerHeight}px - ${bufferHeight}rem) }`, ); + insertCss(`span.card-container::before { + display: block; + content: ''; + margin-top: calc(-${headerHeight}px - ${bufferHeight}rem); + height: calc(${headerHeight}px + ${bufferHeight}rem); + }`); } jQuery('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { if (heading.id) { @@ -48,13 +54,6 @@ function setupAnchors() { jQuery(heading).on('mouseleave', () => jQuery(heading).find('.fa.fa-anchor').css('visibility', 'hidden')); if (isFixed) { - /** - * Fixing the top navbar would break anchor navigation, - * by creating empty spans above the tag we can prevent - * the headings from being covered by the navbar. - */ - const spanId = heading.id; - heading.insertAdjacentHTML('beforebegin', ``); jQuery(heading).removeAttr('id'); // to avoid duplicated id problem } } diff --git a/src/plugins/fixedHeader.js b/src/plugins/fixedHeader.js new file mode 100644 index 0000000000..1e96858af0 --- /dev/null +++ b/src/plugins/fixedHeader.js @@ -0,0 +1,19 @@ +const cheerio = module.parent.require('cheerio'); + +module.exports = { + // eslint-disable-next-line no-unused-vars + preRender: (content, pluginContext, frontMatter) => content, + // eslint-disable-next-line no-unused-vars + postRender: (content, pluginContext, frontMatter) => { + const $ = cheerio.load(content, { xmlMode: false }); + const isFixed = $('header')[0].attribs === undefined + ? false : $('header')[0].attribs.class === 'header-fixed'; + if (isFixed) { + $('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { + const spanId = heading.attribs.id; + $(heading).prepend(``); + }); + } + return $.html(); + }, +}; From 2e583e293eefd89a2764cf5b03c930c208d3a2fc Mon Sep 17 00:00:00 2001 From: Tan Yuanhong Date: Fri, 10 Apr 2020 20:27:01 +0800 Subject: [PATCH 2/3] Update docs and tests --- docs/userGuide/plugins/fixedheader.mbdf | 5 +++++ docs/userGuide/usingPlugins.md | 1 + .../test_site/expected/markbind/js/setup.js | 13 ++++++------- .../expected/markbind/js/setup.js | 13 ++++++------- .../test_site_convert/expected/markbind/js/setup.js | 13 ++++++------- .../expected/markbind/js/setup.js | 13 ++++++------- .../expected/markbind/js/setup.js | 13 ++++++------- .../test_default/expected/markbind/js/setup.js | 13 ++++++------- .../test_minimal/expected/markbind/js/setup.js | 13 ++++++------- 9 files changed, 48 insertions(+), 49 deletions(-) create mode 100644 docs/userGuide/plugins/fixedheader.mbdf diff --git a/docs/userGuide/plugins/fixedheader.mbdf b/docs/userGuide/plugins/fixedheader.mbdf new file mode 100644 index 0000000000..9d7153cdc2 --- /dev/null +++ b/docs/userGuide/plugins/fixedheader.mbdf @@ -0,0 +1,5 @@ +#### `fixedHeader`: Make Top Navigation Fixed + +This plugin allow you to fix the top navigation bar while keeping the anchor navigation function. + +To enable it, simply add `"fixedHeader"` to your site's plugins, and add `class="header-fixed"` attribute to the `
` element in file `{your_site}/_markbind/headers/header.md`. diff --git a/docs/userGuide/usingPlugins.md b/docs/userGuide/usingPlugins.md index e154828d93..6206474197 100644 --- a/docs/userGuide/usingPlugins.md +++ b/docs/userGuide/usingPlugins.md @@ -228,6 +228,7 @@ MarkBind has a set of built-in plugins that can be used immediately without inst + diff --git a/test/functional/test_site/expected/markbind/js/setup.js b/test/functional/test_site/expected/markbind/js/setup.js index c6b54ef24f..85c4ac4bb6 100644 --- a/test/functional/test_site/expected/markbind/js/setup.js +++ b/test/functional/test_site/expected/markbind/js/setup.js @@ -40,6 +40,12 @@ function setupAnchors() { top: calc(-${headerHeight}px - ${bufferHeight}rem) }`, ); + insertCss(`span.card-container::before { + display: block; + content: ''; + margin-top: calc(-${headerHeight}px - ${bufferHeight}rem); + height: calc(${headerHeight}px + ${bufferHeight}rem); + }`); } jQuery('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { if (heading.id) { @@ -48,13 +54,6 @@ function setupAnchors() { jQuery(heading).on('mouseleave', () => jQuery(heading).find('.fa.fa-anchor').css('visibility', 'hidden')); if (isFixed) { - /** - * Fixing the top navbar would break anchor navigation, - * by creating empty spans above the tag we can prevent - * the headings from being covered by the navbar. - */ - const spanId = heading.id; - heading.insertAdjacentHTML('beforebegin', ``); jQuery(heading).removeAttr('id'); // to avoid duplicated id problem } } diff --git a/test/functional/test_site_algolia_plugin/expected/markbind/js/setup.js b/test/functional/test_site_algolia_plugin/expected/markbind/js/setup.js index c6b54ef24f..85c4ac4bb6 100644 --- a/test/functional/test_site_algolia_plugin/expected/markbind/js/setup.js +++ b/test/functional/test_site_algolia_plugin/expected/markbind/js/setup.js @@ -40,6 +40,12 @@ function setupAnchors() { top: calc(-${headerHeight}px - ${bufferHeight}rem) }`, ); + insertCss(`span.card-container::before { + display: block; + content: ''; + margin-top: calc(-${headerHeight}px - ${bufferHeight}rem); + height: calc(${headerHeight}px + ${bufferHeight}rem); + }`); } jQuery('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { if (heading.id) { @@ -48,13 +54,6 @@ function setupAnchors() { jQuery(heading).on('mouseleave', () => jQuery(heading).find('.fa.fa-anchor').css('visibility', 'hidden')); if (isFixed) { - /** - * Fixing the top navbar would break anchor navigation, - * by creating empty spans above the tag we can prevent - * the headings from being covered by the navbar. - */ - const spanId = heading.id; - heading.insertAdjacentHTML('beforebegin', ``); jQuery(heading).removeAttr('id'); // to avoid duplicated id problem } } diff --git a/test/functional/test_site_convert/expected/markbind/js/setup.js b/test/functional/test_site_convert/expected/markbind/js/setup.js index c6b54ef24f..85c4ac4bb6 100644 --- a/test/functional/test_site_convert/expected/markbind/js/setup.js +++ b/test/functional/test_site_convert/expected/markbind/js/setup.js @@ -40,6 +40,12 @@ function setupAnchors() { top: calc(-${headerHeight}px - ${bufferHeight}rem) }`, ); + insertCss(`span.card-container::before { + display: block; + content: ''; + margin-top: calc(-${headerHeight}px - ${bufferHeight}rem); + height: calc(${headerHeight}px + ${bufferHeight}rem); + }`); } jQuery('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { if (heading.id) { @@ -48,13 +54,6 @@ function setupAnchors() { jQuery(heading).on('mouseleave', () => jQuery(heading).find('.fa.fa-anchor').css('visibility', 'hidden')); if (isFixed) { - /** - * Fixing the top navbar would break anchor navigation, - * by creating empty spans above the tag we can prevent - * the headings from being covered by the navbar. - */ - const spanId = heading.id; - heading.insertAdjacentHTML('beforebegin', ``); jQuery(heading).removeAttr('id'); // to avoid duplicated id problem } } diff --git a/test/functional/test_site_expressive_layout/expected/markbind/js/setup.js b/test/functional/test_site_expressive_layout/expected/markbind/js/setup.js index c6b54ef24f..85c4ac4bb6 100644 --- a/test/functional/test_site_expressive_layout/expected/markbind/js/setup.js +++ b/test/functional/test_site_expressive_layout/expected/markbind/js/setup.js @@ -40,6 +40,12 @@ function setupAnchors() { top: calc(-${headerHeight}px - ${bufferHeight}rem) }`, ); + insertCss(`span.card-container::before { + display: block; + content: ''; + margin-top: calc(-${headerHeight}px - ${bufferHeight}rem); + height: calc(${headerHeight}px + ${bufferHeight}rem); + }`); } jQuery('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { if (heading.id) { @@ -48,13 +54,6 @@ function setupAnchors() { jQuery(heading).on('mouseleave', () => jQuery(heading).find('.fa.fa-anchor').css('visibility', 'hidden')); if (isFixed) { - /** - * Fixing the top navbar would break anchor navigation, - * by creating empty spans above the tag we can prevent - * the headings from being covered by the navbar. - */ - const spanId = heading.id; - heading.insertAdjacentHTML('beforebegin', ``); jQuery(heading).removeAttr('id'); // to avoid duplicated id problem } } diff --git a/test/functional/test_site_special_tags/expected/markbind/js/setup.js b/test/functional/test_site_special_tags/expected/markbind/js/setup.js index c6b54ef24f..85c4ac4bb6 100644 --- a/test/functional/test_site_special_tags/expected/markbind/js/setup.js +++ b/test/functional/test_site_special_tags/expected/markbind/js/setup.js @@ -40,6 +40,12 @@ function setupAnchors() { top: calc(-${headerHeight}px - ${bufferHeight}rem) }`, ); + insertCss(`span.card-container::before { + display: block; + content: ''; + margin-top: calc(-${headerHeight}px - ${bufferHeight}rem); + height: calc(${headerHeight}px + ${bufferHeight}rem); + }`); } jQuery('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { if (heading.id) { @@ -48,13 +54,6 @@ function setupAnchors() { jQuery(heading).on('mouseleave', () => jQuery(heading).find('.fa.fa-anchor').css('visibility', 'hidden')); if (isFixed) { - /** - * Fixing the top navbar would break anchor navigation, - * by creating empty spans above the tag we can prevent - * the headings from being covered by the navbar. - */ - const spanId = heading.id; - heading.insertAdjacentHTML('beforebegin', ``); jQuery(heading).removeAttr('id'); // to avoid duplicated id problem } } diff --git a/test/functional/test_site_templates/test_default/expected/markbind/js/setup.js b/test/functional/test_site_templates/test_default/expected/markbind/js/setup.js index c6b54ef24f..85c4ac4bb6 100644 --- a/test/functional/test_site_templates/test_default/expected/markbind/js/setup.js +++ b/test/functional/test_site_templates/test_default/expected/markbind/js/setup.js @@ -40,6 +40,12 @@ function setupAnchors() { top: calc(-${headerHeight}px - ${bufferHeight}rem) }`, ); + insertCss(`span.card-container::before { + display: block; + content: ''; + margin-top: calc(-${headerHeight}px - ${bufferHeight}rem); + height: calc(${headerHeight}px + ${bufferHeight}rem); + }`); } jQuery('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { if (heading.id) { @@ -48,13 +54,6 @@ function setupAnchors() { jQuery(heading).on('mouseleave', () => jQuery(heading).find('.fa.fa-anchor').css('visibility', 'hidden')); if (isFixed) { - /** - * Fixing the top navbar would break anchor navigation, - * by creating empty spans above the tag we can prevent - * the headings from being covered by the navbar. - */ - const spanId = heading.id; - heading.insertAdjacentHTML('beforebegin', ``); jQuery(heading).removeAttr('id'); // to avoid duplicated id problem } } diff --git a/test/functional/test_site_templates/test_minimal/expected/markbind/js/setup.js b/test/functional/test_site_templates/test_minimal/expected/markbind/js/setup.js index c6b54ef24f..85c4ac4bb6 100644 --- a/test/functional/test_site_templates/test_minimal/expected/markbind/js/setup.js +++ b/test/functional/test_site_templates/test_minimal/expected/markbind/js/setup.js @@ -40,6 +40,12 @@ function setupAnchors() { top: calc(-${headerHeight}px - ${bufferHeight}rem) }`, ); + insertCss(`span.card-container::before { + display: block; + content: ''; + margin-top: calc(-${headerHeight}px - ${bufferHeight}rem); + height: calc(${headerHeight}px + ${bufferHeight}rem); + }`); } jQuery('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { if (heading.id) { @@ -48,13 +54,6 @@ function setupAnchors() { jQuery(heading).on('mouseleave', () => jQuery(heading).find('.fa.fa-anchor').css('visibility', 'hidden')); if (isFixed) { - /** - * Fixing the top navbar would break anchor navigation, - * by creating empty spans above the tag we can prevent - * the headings from being covered by the navbar. - */ - const spanId = heading.id; - heading.insertAdjacentHTML('beforebegin', ``); jQuery(heading).removeAttr('id'); // to avoid duplicated id problem } } From d1412e154c9c73e57730682f257d5a6248372b61 Mon Sep 17 00:00:00 2001 From: Tan Yuanhong Date: Fri, 10 Apr 2020 23:53:12 +0800 Subject: [PATCH 3/3] Integrate the plugin as part of MarkBind --- docs/userGuide/plugins/fixedheader.mbdf | 5 ----- docs/userGuide/usingPlugins.md | 1 - src/Page.js | 26 +++++++++++++++++++++++++ src/plugins/fixedHeader.js | 19 ------------------ 4 files changed, 26 insertions(+), 25 deletions(-) delete mode 100644 docs/userGuide/plugins/fixedheader.mbdf delete mode 100644 src/plugins/fixedHeader.js diff --git a/docs/userGuide/plugins/fixedheader.mbdf b/docs/userGuide/plugins/fixedheader.mbdf deleted file mode 100644 index 9d7153cdc2..0000000000 --- a/docs/userGuide/plugins/fixedheader.mbdf +++ /dev/null @@ -1,5 +0,0 @@ -#### `fixedHeader`: Make Top Navigation Fixed - -This plugin allow you to fix the top navigation bar while keeping the anchor navigation function. - -To enable it, simply add `"fixedHeader"` to your site's plugins, and add `class="header-fixed"` attribute to the `
` element in file `{your_site}/_markbind/headers/header.md`. diff --git a/docs/userGuide/usingPlugins.md b/docs/userGuide/usingPlugins.md index 6206474197..e154828d93 100644 --- a/docs/userGuide/usingPlugins.md +++ b/docs/userGuide/usingPlugins.md @@ -228,7 +228,6 @@ MarkBind has a set of built-in plugins that can be used immediately without inst - diff --git a/src/Page.js b/src/Page.js index 9b007a1602..34af722c83 100644 --- a/src/Page.js +++ b/src/Page.js @@ -1172,9 +1172,35 @@ class Page { this.pluginsContext[pluginName] || {}, this.frontMatter, this.getPluginConfig()); } }); + // After rendering everything, add dummy spans to headings if a fixed header is present. + postRenderedContent = Page._postRenderForFixedHeader(postRenderedContent); return postRenderedContent; } + /** + * Generate dummy spans if fixed header is used. + * @param content HTML content to be rendered. + * @return {string} Rendered HTML content. + * @private + */ + static _postRenderForFixedHeader(content) { + const $ = cheerio.load(content, { xmlMode: false }); + const hasHeader = $('header')[0] !== undefined; + if (hasHeader) { + // Check if the first header has a class named 'header-fixed' + const isFixed = $('header')[0].attribs === undefined + ? false : $('header')[0].attribs.class === 'header-fixed'; + if (isFixed) { + $('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { + const spanId = heading.attribs.id; + $(heading).prepend(``); + }); + } + return $.html(); + } + return content; + } + /** * Collect page content inserted by plugins */ diff --git a/src/plugins/fixedHeader.js b/src/plugins/fixedHeader.js deleted file mode 100644 index 1e96858af0..0000000000 --- a/src/plugins/fixedHeader.js +++ /dev/null @@ -1,19 +0,0 @@ -const cheerio = module.parent.require('cheerio'); - -module.exports = { - // eslint-disable-next-line no-unused-vars - preRender: (content, pluginContext, frontMatter) => content, - // eslint-disable-next-line no-unused-vars - postRender: (content, pluginContext, frontMatter) => { - const $ = cheerio.load(content, { xmlMode: false }); - const isFixed = $('header')[0].attribs === undefined - ? false : $('header')[0].attribs.class === 'header-fixed'; - if (isFixed) { - $('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { - const spanId = heading.attribs.id; - $(heading).prepend(``); - }); - } - return $.html(); - }, -};