在没有 class 或 id 标签的情况下添加/前缀文本以列出项目
Prepend / prefix text to list items without class or id tags
我正在使用 Docsify.js markdown parser framework,它会根据文档中的标题自动创建侧边栏(除非您手动创建侧边栏)。
我有 some CSS that numbers list elements,但想将其转换为 JS,因为在页面滚动时添加 类 时存在渲染问题(即添加 .active
)。
最初,我正在尝试使用此代码段,但它没有将其输出为自动递增的分层数字系统:
生成的边栏格式如下:
var li = document.getElementsByTagName( 'li' );
for( var i = 0; i < li.length; i++ ) {
var prefix = '1.';
li[i].innerHTML = prefix + ' Title ' + i;
prefix++;
}
<aside class="sidebar">
<div class="sidebar-nav">
<ul>
<li>Title 1</li>
<ul>
<li>Title 2</li>
<ul>
<li>Title 3</li>
<ul>
<li>Title 4</li>
<ul>
<li>Title 5</li>
<ul>
<li>Title 6</li>
</ul>
</ul>
</ul>
</ul>
</ul>
<li>Title 1</li>
<li>Title 1</li>
<ul>
<li>Title 2</li>
<li>Title 2</li>
<ul>
<li>Title 3</li>
<ul>
<li>Title 4</li>
<ul>
<li>Title 5</li>
<ul>
<li>Title 6</li>
</ul>
</ul>
</ul>
</ul>
</ul>
</ul>
</div>
</aside>
我知道 HTML 结构无效,因为 <ul>
是 <ul>
的后代,但这是输出的代码,我无法控制它。
但是,我希望能够用章节对标题进行编号 sub-sections:
1. Title 1
1.1. Title 2
1.1.1. Title 3
1.1.1.1. Title 4
1.1.1.1.1. Title 5
1.1.1.1.1.1. Title 6
2. Title 1
3. Title 1
3.1. Title 2
3.2. Title 2
3.2.1. Title 3
3.2.1.1. Title 4
3.2.1.1.1. Title 5
3.2.1.1.1.1. Title 6
我正在努力寻找一种方法来定位第一个 <li>
(或 H1),然后能够通过 .nextElementSibling
访问下一个 <ul>
继续循环并添加编号。
据我目前所了解的是:document.querySelectorAll( 'div.sidebar-nav > ul' )
而且没有什么可以继续的了!
我想我真的无法理解 javascript 这里的内容,希望我能够在循环遍历 <li>
和 <ul>
元素添加到数字前。
以下是JavaScript应用嵌套索引号。最多只有6个header标签,6个级别,所以我们可以使用递归解决方案:
let startLevel = 1;
let endLevel = 5;
function indexsify() {
let children = document.querySelectorAll('#sidebar > ul');
let numbers = new Array(7).fill(0);
let depth = 0;
children.forEach((element, index) => {
recurse(element, ++depth, numbers);
});
}
function recurse(element, depth, numbers) { //ul
let children = Array.from(element.children);
children.forEach((element, index) => {
if (element.localName.toUpperCase() === 'LI') {
numbers[depth]++;
addNumberString(element, depth, numbers);
} else if (element.localName.toUpperCase() === 'UL') {
if (depth < endLevel) {
recurse(element, depth + 1, numbers, startLevel);
numbers.fill(0, depth + 1); //reset all next numbers
}
}
});
}
function addNumberString(element, depth, numbers) {
let strNumber = "";
numbers.forEach((num, index) => {
if (index > depth || index <= startLevel) return;
strNumber += `${num}.`;
});
element.firstElementChild.innerText = strNumber +
element.firstElementChild.innerText;
}
indexsify();
ul,
li {
list-style-type: none;
}
<div id="sidebar">
<ul>
<li><a>Home</a></li>
<ul>
<li><a>Chapter a</a></li>
<ul>
<li><a> Section a</a></li>
<li><a>Section b</a></li>
</ul>
<li><a>Chapter b</a></li>
<li><a>Chapter c</a></li>
<ul>
<li><a>Section a</a></li>
<li><a>Section b</a></li>
<ul>
<li><a>Sub-section a</a></li>
</ul>
</ul>
<li><a>Chapter D</a></li>
</ul>
</ul>
</div>
修改 markdown 本身: 根据 Docsify plugin documentation 没有直接规定来影响边栏内容。您的插件使用 hook.afterEach(function(html, next)
并且侧边栏是单独生成的。所以你也试图操纵生成的侧边栏。您正在尝试执行两次类似的操作。
为什么不使用 hook.beforeEach(function(content)
并自己操作 markdown。这样,您只需执行一次编号操作。
这是一个 demo site and the code sandbox link 用于以下操作降价内容的示例插件:
<!DOCTYPE html>
<html>
<body>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/docsify@4/themes/vue.css"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/docsify/themes/dark.css"
/>
<div id="app">Please wait...</div>
<script>
window.$docsify = {
el: "#app",
loadSidebar: true,
maxLevel: 4,
subMaxLevel: 5,
homepage: "readme.md"
};
</script>
<script>
//plugin
let myPlugin = function (hook, vm) {
hook.init(function () {
//remove '-' before Table of content entries
let customStyles = document.createElement("style");
customStyles.type = "text/css";
customStyles.textContent = `.app-sub-sidebar li::before {
content: '' !important;
padding-right: 4px;
float: left;
}`;
document.body.appendChild(customStyles);
});
//update markdown content before docsify parsing
hook.beforeEach(function (content) {
let lines = content.split("\n");
let numbers = new Array(6).fill(0);
let depth = 0;
lines.forEach((line, index) => {
let level = getLevel(line);
//if not a header continue to next line
if (level === -1) return;
if (level > depth) {
depth++; //increase depth
} else {
depth = level; //decrease depth
numbers.fill(0, depth + 1); //set all next depth to 0
}
numbers[depth]++;
let strNumber = "";
numbers.forEach((num, index) => {
if (index > depth || index < startLevel) return;
strNumber += `${num}.`;
});
if (depth < endLevel) {
lines[index] =
levels[level] + strNumber + line.substr(depth + 1, line.length);
}
});
//update original content
content = lines.join("\n");
return content;
});
let levels = ["# ", "## ", "### ", "#### ", "##### ", "###### "];
let startLevel = 1;
let endLevel = 4;
let regEx = new RegExp(`^#{1,${endLevel}}\s+.*`);
function getLevel(line) {
if (!regEx.test(line)) return -1; //not a header line
if (line.startsWith(levels[0])) return 0; //h1
if (line.startsWith(levels[1])) return 1;
if (line.startsWith(levels[2])) return 2;
if (line.startsWith(levels[3])) return 3;
if (line.startsWith(levels[4])) return 4;
if (line.startsWith(levels[5])) return 5; //h6
}
};
window.$docsify.plugins = [myPlugin];
</script>
<script src="https://cdn.jsdelivr.net/npm/docsify@4"></script>
</body>
</html>
我们需要覆盖 hook.init(function ())
中的默认值 CSS 以删除内容 table 中的前导 -
。
旧答案:您可以直接在锚点上添加数字<a>
标签:
.sidebar ul>li {
counter-increment: item;
}
.sidebar ul>li:first-child {
counter-reset: item;
}
.sidebar ul>li a::before {
content: counters(item, ".") " ";
}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify@4/themes/vue.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify/themes/dark.css" />
<div id="app">Please wait...</div>
<script>
window.$docsify = {
el: "#app",
loadSidebar: false,
homepage: 'https://gist.githubusercontent.com/OnkarRuikar/bb1d986f279dddceea9004a4bde3844b/raw/80fe153d6b8c1bb2b8e7035be7df1bb908779061/readme.md'
}
</script>
<script src="https://cdn.jsdelivr.net/npm/docsify@4"></script>
如果您在主要部分为标题生成编号,那么它们可能会自动填充到边栏中。您可以使用 markdown setting to write the script. Or you can try something like: https://github.com/markbattistella/docsify-autoHeaders
您想根据其类型对外部 UL 的每个子级“做某事”——在当前级别递增索引,然后添加一个标签(如果它是 LI);否则递归到下一个级别,如果它是 UL。鉴于 OP 提供的 HTML,这段代码产生了完全期望的结果:
function addLabels(element, prefix) {
var index = 0;
Array.from(element.children).forEach(element => {
if (element.localName.toUpperCase() === 'LI') {
index += 1;
element.innerText = prefix + index + '. ' + element.innerText;
} else if (element.localName.toUpperCase() === 'UL') {
addLabels(element, prefix + index + '.');
}
});
}
document.querySelectorAll('div.sidebar-nav > ul').forEach(
element => addLabels(element, '')
);
另外,您提到了使用 nextElementSibling 从 LI 获取 UL 的想法。那会起作用,但我认为您最终会得到不太健壮、性能较差且不是特别清晰的代码。但为了记录,这可能看起来像这样:
function addLabels2(element, prefix) {
Array.from(element.querySelectorAll(':scope > li')).forEach((element, index) => {
var label = prefix + (index+1) + '.';
var sibling = element.nextElementSibling;
element.innerText = label + ' ' + element.innerText;
if (sibling && sibling.localName.toUpperCase() === 'UL') {
addLabels2(sibling, label);
}
});
}
document.querySelectorAll('div.sidebar-nav > ul').forEach(
element => addLabels2(element, '')
);
我正在使用 Docsify.js markdown parser framework,它会根据文档中的标题自动创建侧边栏(除非您手动创建侧边栏)。
我有 some CSS that numbers list elements,但想将其转换为 JS,因为在页面滚动时添加 类 时存在渲染问题(即添加 .active
)。
最初,我正在尝试使用此代码段,但它没有将其输出为自动递增的分层数字系统:
生成的边栏格式如下:
var li = document.getElementsByTagName( 'li' );
for( var i = 0; i < li.length; i++ ) {
var prefix = '1.';
li[i].innerHTML = prefix + ' Title ' + i;
prefix++;
}
<aside class="sidebar">
<div class="sidebar-nav">
<ul>
<li>Title 1</li>
<ul>
<li>Title 2</li>
<ul>
<li>Title 3</li>
<ul>
<li>Title 4</li>
<ul>
<li>Title 5</li>
<ul>
<li>Title 6</li>
</ul>
</ul>
</ul>
</ul>
</ul>
<li>Title 1</li>
<li>Title 1</li>
<ul>
<li>Title 2</li>
<li>Title 2</li>
<ul>
<li>Title 3</li>
<ul>
<li>Title 4</li>
<ul>
<li>Title 5</li>
<ul>
<li>Title 6</li>
</ul>
</ul>
</ul>
</ul>
</ul>
</ul>
</div>
</aside>
我知道 HTML 结构无效,因为 <ul>
是 <ul>
的后代,但这是输出的代码,我无法控制它。
但是,我希望能够用章节对标题进行编号 sub-sections:
1. Title 1
1.1. Title 2
1.1.1. Title 3
1.1.1.1. Title 4
1.1.1.1.1. Title 5
1.1.1.1.1.1. Title 6
2. Title 1
3. Title 1
3.1. Title 2
3.2. Title 2
3.2.1. Title 3
3.2.1.1. Title 4
3.2.1.1.1. Title 5
3.2.1.1.1.1. Title 6
我正在努力寻找一种方法来定位第一个 <li>
(或 H1),然后能够通过 .nextElementSibling
访问下一个 <ul>
继续循环并添加编号。
据我目前所了解的是:document.querySelectorAll( 'div.sidebar-nav > ul' )
而且没有什么可以继续的了!
我想我真的无法理解 javascript 这里的内容,希望我能够在循环遍历 <li>
和 <ul>
元素添加到数字前。
以下是JavaScript应用嵌套索引号。最多只有6个header标签,6个级别,所以我们可以使用递归解决方案:
let startLevel = 1;
let endLevel = 5;
function indexsify() {
let children = document.querySelectorAll('#sidebar > ul');
let numbers = new Array(7).fill(0);
let depth = 0;
children.forEach((element, index) => {
recurse(element, ++depth, numbers);
});
}
function recurse(element, depth, numbers) { //ul
let children = Array.from(element.children);
children.forEach((element, index) => {
if (element.localName.toUpperCase() === 'LI') {
numbers[depth]++;
addNumberString(element, depth, numbers);
} else if (element.localName.toUpperCase() === 'UL') {
if (depth < endLevel) {
recurse(element, depth + 1, numbers, startLevel);
numbers.fill(0, depth + 1); //reset all next numbers
}
}
});
}
function addNumberString(element, depth, numbers) {
let strNumber = "";
numbers.forEach((num, index) => {
if (index > depth || index <= startLevel) return;
strNumber += `${num}.`;
});
element.firstElementChild.innerText = strNumber +
element.firstElementChild.innerText;
}
indexsify();
ul,
li {
list-style-type: none;
}
<div id="sidebar">
<ul>
<li><a>Home</a></li>
<ul>
<li><a>Chapter a</a></li>
<ul>
<li><a> Section a</a></li>
<li><a>Section b</a></li>
</ul>
<li><a>Chapter b</a></li>
<li><a>Chapter c</a></li>
<ul>
<li><a>Section a</a></li>
<li><a>Section b</a></li>
<ul>
<li><a>Sub-section a</a></li>
</ul>
</ul>
<li><a>Chapter D</a></li>
</ul>
</ul>
</div>
修改 markdown 本身: 根据 Docsify plugin documentation 没有直接规定来影响边栏内容。您的插件使用 hook.afterEach(function(html, next)
并且侧边栏是单独生成的。所以你也试图操纵生成的侧边栏。您正在尝试执行两次类似的操作。
为什么不使用 hook.beforeEach(function(content)
并自己操作 markdown。这样,您只需执行一次编号操作。
这是一个 demo site and the code sandbox link 用于以下操作降价内容的示例插件:
<!DOCTYPE html>
<html>
<body>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/docsify@4/themes/vue.css"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/docsify/themes/dark.css"
/>
<div id="app">Please wait...</div>
<script>
window.$docsify = {
el: "#app",
loadSidebar: true,
maxLevel: 4,
subMaxLevel: 5,
homepage: "readme.md"
};
</script>
<script>
//plugin
let myPlugin = function (hook, vm) {
hook.init(function () {
//remove '-' before Table of content entries
let customStyles = document.createElement("style");
customStyles.type = "text/css";
customStyles.textContent = `.app-sub-sidebar li::before {
content: '' !important;
padding-right: 4px;
float: left;
}`;
document.body.appendChild(customStyles);
});
//update markdown content before docsify parsing
hook.beforeEach(function (content) {
let lines = content.split("\n");
let numbers = new Array(6).fill(0);
let depth = 0;
lines.forEach((line, index) => {
let level = getLevel(line);
//if not a header continue to next line
if (level === -1) return;
if (level > depth) {
depth++; //increase depth
} else {
depth = level; //decrease depth
numbers.fill(0, depth + 1); //set all next depth to 0
}
numbers[depth]++;
let strNumber = "";
numbers.forEach((num, index) => {
if (index > depth || index < startLevel) return;
strNumber += `${num}.`;
});
if (depth < endLevel) {
lines[index] =
levels[level] + strNumber + line.substr(depth + 1, line.length);
}
});
//update original content
content = lines.join("\n");
return content;
});
let levels = ["# ", "## ", "### ", "#### ", "##### ", "###### "];
let startLevel = 1;
let endLevel = 4;
let regEx = new RegExp(`^#{1,${endLevel}}\s+.*`);
function getLevel(line) {
if (!regEx.test(line)) return -1; //not a header line
if (line.startsWith(levels[0])) return 0; //h1
if (line.startsWith(levels[1])) return 1;
if (line.startsWith(levels[2])) return 2;
if (line.startsWith(levels[3])) return 3;
if (line.startsWith(levels[4])) return 4;
if (line.startsWith(levels[5])) return 5; //h6
}
};
window.$docsify.plugins = [myPlugin];
</script>
<script src="https://cdn.jsdelivr.net/npm/docsify@4"></script>
</body>
</html>
我们需要覆盖 hook.init(function ())
中的默认值 CSS 以删除内容 table 中的前导 -
。
旧答案:您可以直接在锚点上添加数字<a>
标签:
.sidebar ul>li {
counter-increment: item;
}
.sidebar ul>li:first-child {
counter-reset: item;
}
.sidebar ul>li a::before {
content: counters(item, ".") " ";
}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify@4/themes/vue.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify/themes/dark.css" />
<div id="app">Please wait...</div>
<script>
window.$docsify = {
el: "#app",
loadSidebar: false,
homepage: 'https://gist.githubusercontent.com/OnkarRuikar/bb1d986f279dddceea9004a4bde3844b/raw/80fe153d6b8c1bb2b8e7035be7df1bb908779061/readme.md'
}
</script>
<script src="https://cdn.jsdelivr.net/npm/docsify@4"></script>
如果您在主要部分为标题生成编号,那么它们可能会自动填充到边栏中。您可以使用 markdown setting to write the script. Or you can try something like: https://github.com/markbattistella/docsify-autoHeaders
您想根据其类型对外部 UL 的每个子级“做某事”——在当前级别递增索引,然后添加一个标签(如果它是 LI);否则递归到下一个级别,如果它是 UL。鉴于 OP 提供的 HTML,这段代码产生了完全期望的结果:
function addLabels(element, prefix) {
var index = 0;
Array.from(element.children).forEach(element => {
if (element.localName.toUpperCase() === 'LI') {
index += 1;
element.innerText = prefix + index + '. ' + element.innerText;
} else if (element.localName.toUpperCase() === 'UL') {
addLabels(element, prefix + index + '.');
}
});
}
document.querySelectorAll('div.sidebar-nav > ul').forEach(
element => addLabels(element, '')
);
另外,您提到了使用 nextElementSibling 从 LI 获取 UL 的想法。那会起作用,但我认为您最终会得到不太健壮、性能较差且不是特别清晰的代码。但为了记录,这可能看起来像这样:
function addLabels2(element, prefix) {
Array.from(element.querySelectorAll(':scope > li')).forEach((element, index) => {
var label = prefix + (index+1) + '.';
var sibling = element.nextElementSibling;
element.innerText = label + ' ' + element.innerText;
if (sibling && sibling.localName.toUpperCase() === 'UL') {
addLabels2(sibling, label);
}
});
}
document.querySelectorAll('div.sidebar-nav > ul').forEach(
element => addLabels2(element, '')
);