将数组添加到多维数组或对象中

tot*_*oob 20 javascript

我正在将由a生成的内容解析wysiwyg为React中的目录小部件.

到目前为止,我正在遍历标题并将它们添加到数组中.

如何将它们全部放入一个多维数组或对象(最好的方法),使它看起来更像:

h1-1
    h2-1
        h3-1

h1-2
    h2-2
        h3-2

h1-3
    h2-3
        h3-3
Run Code Online (Sandbox Code Playgroud)

然后我可以在UI中使用有序列表进行渲染.

const str = "<h1>h1-1</h1><h2>h2-1</h2><h3>h3-1</h3><p>something</p><h1>h1-2</h1><h2>h2-2</h2><h3>h3-2</h3>";

const patternh1 = /<h1>(.*?)<\/h1>/g;
const patternh2 = /<h2>(.*?)<\/h2>/g;
const patternh3 = /<h3>(.*?)<\/h3>/g;

let h1s = [];
let h2s = [];
let h3s = [];

let matchh1, matchh2, matchh3;

while (matchh1 = patternh1.exec(str))
    h1s.push(matchh1[1])

while (matchh2 = patternh2.exec(str))
    h2s.push(matchh2[1])
    
while (matchh3 = patternh3.exec(str))
    h3s.push(matchh3[1])
    
console.log(h1s)
console.log(h2s)
console.log(h3s)
Run Code Online (Sandbox Code Playgroud)

lap*_*tou 13

我不了解你,但我讨厌用正则表达式解析HTML.相反,我认为让DOM处理这个问题更好:

const str = `<h1>h1-1</h1>
  <h3>h3-1</h3>
  <h3>h3-2</h3>
  <p>something</p>
  <h1>h1-2</h1>
  <h2>h2-2</h2>
  <h3>h3-2</h3>`;

const wrapper = document.createElement('div');
wrapper.innerHTML = str.trim();

let tree = [];
let leaf = null;

for (const node of wrapper.querySelectorAll("h1, h2, h3, h4, h5, h6")) {
  const nodeLevel = parseInt(node.tagName[1]);
  const newLeaf = {
    level: nodeLevel,
    text: node.textContent,
    children: [],
    parent: leaf
  };

  while (leaf && newLeaf.level <= leaf.level)
    leaf = leaf.parent;

  if (!leaf)
    tree.push(newLeaf);
  else
    leaf.children.push(newLeaf);

  leaf = newLeaf;
}

console.log(tree);
Run Code Online (Sandbox Code Playgroud)

这个答案不需要h3遵循h2; 如果你愿意,h3可以关注h1.如果要将其转换为有序列表,也可以这样做:

const str = `<h1>h1-1</h1>
      <h3>h3-1</h3>
      <h3>h3-2</h3>
      <p>something</p>
      <h1>h1-2</h1>
      <h2>h2-2</h2>
      <h3>h3-2</h3>`;

const wrapper = document.createElement('div');
wrapper.innerHTML = str.trim();

let tree = [];
let leaf = null;

for (const node of wrapper.querySelectorAll("h1, h2, h3, h4, h5, h6")) {
  const nodeLevel = parseInt(node.tagName[1]);
  const newLeaf = {
    level: nodeLevel,
    text: node.textContent,
    children: [],
    parent: leaf
  };

  while (leaf && newLeaf.level <= leaf.level)
    leaf = leaf.parent;

  if (!leaf)
    tree.push(newLeaf);
  else
    leaf.children.push(newLeaf);

  leaf = newLeaf;
}


const ol = document.createElement("ol");

(function makeOl(ol, leaves) {
  for (const leaf of leaves) {
    const li = document.createElement("li");
    li.appendChild(new Text(leaf.text));

    if (leaf.children.length > 0) {
      const subOl = document.createElement("ol");
      makeOl(subOl, leaf.children);
      li.appendChild(subOl);
    }

    ol.appendChild(li);
  }
})(ol, tree);

// add it to the DOM
document.body.appendChild(ol);

// or get it as text
const result = ol.outerHTML;
Run Code Online (Sandbox Code Playgroud)

由于HTML是由DOM而不是正则表达式解析的,因此,如果h1标记具有属性,则此解决方案不会遇到任何错误.


Ovi*_*lha 8

您可以简单地收集所有内容h*,然后迭代它们以构建一个树,如下所示:

使用ES6(我推断这可以从您使用的constlet)

const str = `
    <h1>h1-1</h1>
    <h2>h2-1</h2>
    <h3>h3-1</h3>
    <p>something</p>
    <h1>h1-2</h1>
    <h2>h2-2</h2>
    <h3>h3-2</h3>
`
const patternh = /<h(\d)>(.*?)<\/h(\d)>/g;

let hs = [];

let matchh;

while (matchh = patternh.exec(str))
    hs.push({ lev: matchh[1], text: matchh[2] })

console.log(hs)

// constructs a tree with the format [{ value: ..., children: [{ value: ..., children: [...] }, ...] }, ...]
const add = (res, lev, what) => {
  if (lev === 0) {
    res.push({ value: what, children: [] });
  } else {
    add(res[res.length - 1].children, lev - 1, what);
  }
}

// reduces all hs found into a tree using above method starting with an empty list
const tree = hs.reduce((res, { lev, text }) => {
  add(res, lev-1, text);
  return res;
}, []);

console.log(tree);
Run Code Online (Sandbox Code Playgroud)

但是因为你的html标题本身不是树形结构(我想这是你的用例),这只能在某些假设下工作,例如你不能拥有一个,<h3>除非<h2>它上面有一个<h1>以上.它还假设一个较低级别的标题将始终属于一个更高级别的最新标题.

如果您想进一步使用树结构来为例如渲染TOC的代表性有序列表,您可以执行以下操作:

// function to render a bunch of <li>s
const renderLIs = children => children.map(child => `<li>${renderOL(child)}</li>`).join('');

// function to render an <ol> from a tree node
const renderOL = tree => tree.children.length > 0 ? `<ol>${tree.value}${renderLIs(tree.children)}</ol>` : tree.value;

// use a root node for the TOC
const toc = renderOL({ value: 'TOC', children: tree });

console.log(toc);
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你.


imh*_*tap 5

您想要做的是被称为(a的变体)文档大纲,例如.从文档标题创建嵌套列表,尊重其层次结构.

使用DOM和DOMParser API的浏览器的简单实现如下(放入HTML页面并在ES5中编码以便于测试):

<!DOCTYPE html>
<html>
<head>
<title>Document outline</title>
</head>
<body>
<div id="outline"></div>
<script>

// test string wrapped in a document (and body) element
var str = "<html><body><h1>h1-1</h1><h2>h2-1</h2><h3>h3-1</h3><p>something</p><h1>h1-2</h1><h2>h2-2</h2><h3>h3-2</h3></body></html>";

// util for traversing a DOM and emit SAX startElement events
function emitSAXLikeEvents(node, handler) {
    handler.startElement(node)
    for (var i = 0; i < node.children.length; i++)
        emitSAXLikeEvents(node.children.item(i), handler)
    handler.endElement(node)
}

var outline = document.getElementById('outline')
var rank = 0
var context = outline
emitSAXLikeEvents(
    (new DOMParser()).parseFromString(str, "text/html").body,
    {
        startElement: function(node) {
            if (/h[1-6]/.test(node.localName)) {
                var newRank = +node.localName.substr(1, 1)

                // set context li node to append
                while (newRank <= rank--)
                    context = context.parentNode.parentNode

                rank = newRank

                // create (if 1st li) or
                // get (if 2nd or subsequent li) ol element
                var ol
                if (context.children.length > 0)
                    ol = context.children[0]
                else {
                    ol = document.createElement('ol')
                    context.appendChild(ol)
                }

                // create and append li with text from
                // heading element
                var li = document.createElement('li')
                li.appendChild(
                  document.createTextNode(node.innerText))
                ol.appendChild(li)

                context = li
            }
        },
        endElement: function(node) {}
    })
</script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

我首先将你的片段解析为a Document,然后遍历它以创建类似SAX的startElement()调用.在该startElement()函数中,针对最近创建的列表项(如果有的话)的等级检查标题元素的等级.然后在正确的层次结构级别附加新的列表项,并且可能将ol元素创建为它的容器.需要注意的算法,因为它是不会从"跳楼"的工作h1,以h3在层次结构,但可以很容易适应.

如果你想在node.js上创建一个大纲/内容表,那么代码可以在服务器端运行,但是需要一个像样的HTML解析库(对于node.js来说,DOMParser polyfill,可以这么说).还有https://github.com/h5o/h5o-jshttps://github.com/hoyois/html5outliner包用于创建轮廓,但我还没有测试过.据推测,这些软件包还可以处理角落情况,例如标题元素iframequote文档大纲中通常不需要的元素.

创建HTML5大纲的主题历史悠久; 见例如.http://html5doctor.com/computer-says-no-to-html5-document-outline/.HTML4的实践是不使用分段根(在HTML5用语中)包装元素,用于在同一层次结构级别进行切片和放置标题和内容,这种做法称为"平面地球标记".SGML具有RANK用于处理特征H1,H2等排的元件,并且可以由推断省略section的元件,从而自动创建一个轮廓,从在简单的情况下HTML4样"平土标记"(例如,其中仅section或另一单个元件允许作为切片根).