将缺少项的项标题到树结构

  • 本文关键字:结构 标题 javascript
  • 更新时间 :
  • 英文 :


我正在编写一个函数,该函数查询作为div直接子级的所有标题元素,并将它们作为项存储在数组(data(中,其中标记名较低的标题是其上方标题的item。换句话说,为标题创建一个树结构。data数组中的每个标题都是一个对象,具有以下属性:

text:一个字符串,包含标题的内部文本。

items:一个数组,可能包含副标题项。

number:表示文档中标题位置的字符串

id:一个字符串,由元素的文本及其父文本组成,由两个下划线"分隔__&";。

为了得到正确的名称和数字,我必须将每个标题级别的最后一个存储在一个数组中,以便在遍历项目时访问它们。这就是我想到的:

const contentContainer = document.getElementById('content');
// Query headings that are direct children of the content container. 
const headings = contentContainer.querySelectorAll(":scope > h1,:scope > h2,:scope > h3,:scope > h4,:scope > h5,:scope > h6");
// String helper function
function kebabCase(string) {
return (string
.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/[s_]+/g, '-').toLowerCase()
)
}
// Contains the list of headings and their items.
const data = [];
// Array that stores the last depth of each of the last headings at each depth.
const indexHelper = new Array(6).fill(-1);
// Array that stores the last name of each of the last headings at each depth.
const nameHelper = new Array(6).fill('');
// Generates a datalist of headings and sets an id for each heading in the DOM.
function processTag(depth, element) {
const listItem = {
text: element.innerText,
items: []
};
const name = kebabCase(element.innerText);
// Increment the indexHelper at depth position and set the current name.
indexHelper[depth]++;
nameHelper[depth] = name;
// Reset rest of array that is bigger than current depth.
indexHelper.fill(-1, depth + 1);
// Push the element to the right place in the array.
let dataPushString = 'data';
for (let i = 0; i < depth; i++) {
dataPushString += "[indexHelper[".concat(i, "]]?.items?");
i < depth - 1 && (dataPushString += '.');
}
dataPushString += '.push(listItem)';
eval(dataPushString);
// Generate an id based on the names of the element and its parents.
let id = '';
for (let i = 0; i < depth; i++) {
id += nameHelper[i] + "__";
}
let number = '';
for (let i = 0; i < depth; i++) {
number += "(indexHelper[".concat(i + 1, "] +1) + "." ");
i < depth - 1 && (number += ' + ');
}
listItem.id = id + name;
listItem.number = eval(number) && eval(number);
}
function processData() {
for (let i = 0; i < headings.length; i++) {
const element = headings[i];
const depth = Number(element.tagName.substring(1) - 1);
processTag(depth, element);
}
}
processData();
const pre = document.getElementById('pre');
pre.innerHTML = JSON.stringify(data, null, 2);
<pre id="pre"></pre>
<div id="content">
<h1>Title</h1>
<h3>This heading is problematic</h3>
<h2>H2</h2>
<h2>Another h2</h2>
<h3>Depth h3 </h3>
<h4>Fourth level</h4>
<h3>Back to three</h3>
<h2>Back to two</h2>
</div>

正如您在上面的示例中看到的,格式错误的<h3>This heading is problematic</h3>没有添加到data中。原因是,我正在循环该项,并试图将其添加到不存在的父项中。这是通过将字符串连接到右边"来完成的;"深度";使用eval((,这也是一个坏主意。

我的问题有两个:如何确保有问题的标题添加到data中,即使它的(直接(父标题不存在?我该如何避免不使用eval((?

我希望得到的结果是:

[
{
"text": "Title",
"items": [
{
"text": "",
"items": [
{
"text": "This heading is problematic",
"items": [],
"id": "title____this-heading-is-problematic",
"number": "2.1.1"
},
],
"id": "title__",
"number": "1."
},
{
"text": "H2",
"items": [],
"id": "title__h2",
"number": "2."
},
{
"text": "Another h2",
"items": [
//...etc...

我认为您的代码可以简化。

您只需要一个堆栈,其中最上面的项是当前父候选者,即";我们遇到的最后一个标题";。对于我们遇到的每一个标题,都会根据需要从堆栈中弹出尽可能多的项目,这样新标题就可以逻辑地放在下面。然后将新标题推到堆栈上,清洗、漂洗,重复。

const kebabCase = s => s.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/[s_]+/g, '-').toLowerCase();
const headings = document.querySelector('#content').querySelectorAll('h1,h2,h3,h4,h5,h6');
const stack = [{text: 'root', level: 0, items:[]}];
[...headings].forEach(h => {
const self = {
text: h.textContent.trim(),
level: +h.nodeName.replace('H', ''),
items: [],
};
while (self.level <= stack[stack.length-1].level) stack.pop();
stack[stack.length-1].items.push(self);
stack.push(self);
self.ord = stack[stack.length-2].items.length;
self.num = stack.map(i => i.ord).slice(1).join('.') + '.';
self.id = stack.map(i => kebabCase(i.text)).slice(1).join('__');
});
document.querySelector('#pre').textContent = JSON.stringify(stack[0].items, null, 2);
<pre id="pre"></pre>
<div id="content">
<h1>Title</h1>
<h3>This heading is problematic</h3>
<h2>H2</h2>
<h2>Another h2</h2>
<h3>Depth h3 </h3>
<h4>Fourth level</h4>
<h3>Back to three</h3>
<h2>Back to two</h2>
</div>

当然,当标题层次结构一开始就不正确时,大纲编号是一个定义问题。

最新更新