获取所有标头并递归创建树

时间:2019-06-22 13:06:58

标签: javascript jquery html

我想使用标题创建树。

示例:

<h1>Wow</h1>
<h2>Blablablub</h2>
<p>Lorem Ipsum...</p>
<h1>Lalalala</h1>
<p>Lorem Ipsum...</p>
<h1>Ble</h1>
<h2>Test</h2>
<h3>Third</h3>
<p>Lorem Ipsum...</p>

应创建此列表:

<ul>
    <li>
        <a>Wow</a>
        <ul>
            <li>
                <a>Blablablub</a>
            </li>
        </ul>
    </li>
    <li>
        <a>Lalalala</a>
    </li>
    <li>
        <a>Ble</a>
        <ul>
            <li>
                <a>Test</a>
                <ul>
                    <li>
                        <a>Third</a>
                    </li>
                </ul>
            </li>
        </ul>
    </li>
</ul>

a标记应具有自定义ID,但这对这个问题并不重要。我试图这样做,但我无法弄清楚。这是我尝试过的:

function find_titles(find_num, element, prefix=""){
        temp_list = $("<ul></ul>");
        element.find(`${prefix}h${find_num}`).each(function(i, object){
            let text = $(object).text();
            let id = text.replace(/[^0-9a-zA-Z]/gi, "") + random_chars();

            $(object).attr("id", id);

            if ($(object).next().prop("tagName").toLowerCase() == `h${find_num + 1}`){
                console.log($(object));
                next_titles = find_titles(find_num + 1, $(object), "+ ")[0].innerHTML;
            } else {
                next_titles = "";
            }

            $(`<li><a href="#${id}">${text}</a>${next_titles}</li>`).appendTo(temp_list);
        });
        return temp_list;
    }

编辑

此:

<h1>First</h1>
<h2>Second</h2>
<p>Lorem Ipsum</p>
<h3>Third</h3>

通常应转换为此:

<ul>
    <li>
        <a>First</a>
        <ul>
            <li>
                <a>Second</a>
            </li>
        </ul>
    </li>
    <li>
        <a>Third</a>
    </li>
</ul>

我不在乎第一个是h1 h2还是h3。在文本中,样式仅对样式很重要,而在树中则不重要。

3 个答案:

答案 0 :(得分:3)

您可以先清除数据,以仅获取heading个节点及其编号和文本。之后,您可以循环数据并使用每个级别的数组和索引号基于级别构建树结构。

function tree(data) {
  data = Array.from(data).reduce((r, e) => {
    const number = e.nodeName.match(/\d+?/g);
    if(number) r.push({ text: e.textContent, level: +number })
    return r;
  }, [])

  const result = $("<ul>")
  const levels = [
    [], result
  ]

  data.forEach(({ level, text }) => {
    const li = $("<li>")
    const a = $("<a>", { text, href: text })
    levels[level + 1] = $('<ul>')

    li.append(a)
    li.append(levels[level + 1]);

    levels[level].append(li)
  })

  return result;
}

const result = tree($("body > *"));
$("body").html(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1>Wow</h1>
<h2>Blablablub</h2>
<p>Lorem Ipsum...</p>
<h1>Lalalala</h1>
<p>Lorem Ipsum...</p>
<h1>Ble</h1>
<h2>Test</h2>
<h3>Third</h3>
<p>Lorem Ipsum...</p>

您也可以使用一种reduce方法来执行此操作,如果元素正在前进,则添加到树中。

function tree(data) {
  const result = $("<ul>")
  const levels = [
    [], result
  ]

  Array.from(data).reduce((r, { textContent: text, nodeName }) => {
    const number = nodeName.match(/\d+?/g);
    const level = number ? +number : null;

    if(level) {
      const li = $('<li>').append($("<a>", { text, href: text }))
      r.push({ level: r[level + 1] = $('<ul>') })
      r[level].append(li.append(levels[level + 1]))
    }

    return r;
  }, levels)

  return result;
}

const result = tree($("body > *"));
$("body").html(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1>Wow</h1>
<h2>Blablablub</h2>
<p>Lorem Ipsum...</p>
<h1>Lalalala</h1>
<p>Lorem Ipsum...</p>
<h1>Ble</h1>
<h2>Test</h2>
<h3>Third</h3>
<p>Lorem Ipsum...</p>

答案 1 :(得分:1)

您可以遍历所有H1元素,然后遍历所有下一个标头元素({{1}以外的所有元素)。这是一个示例:

H1
const elements = $('h1').map(function() {
  let container = $('<li>');
  const ret = container;

  container.append($('<a>').text($(this).text()));

  let next = $(this).next('h2, h3, h4, h5');

  while (next.length) {
    const tmp = $('<li>');
    tmp.append($('<a>').text(next.text()));
    container.append(tmp);
    container = tmp;
    next = next.next('h2, h3, h4, h5');
  }
  return ret;
}).get();

const parent = $('<ul>');
parent.append(elements);

console.log(parent[0].innerHTML);

答案 2 :(得分:1)

使用:header选择器和tagName属性

load 'app/models/user.rb'
Object.send(:remove_const, :User)