Как создать вложенный список элементов тегов заголовков в том же порядке из DOM - PullRequest
0 голосов
/ 02 августа 2020

Итак, у меня есть документ HTML, содержащий любое количество h1, h2, h3, h4 и т.д. c. теги, с вложением.

Пример.

<h2>About cats</h2>
<p>Some Info about cats</p>

<h2>About dogs</h2>
<p>Some Info about cats</p>
<h3>Breed 1</h3>
<p>About breed 1</p>
<h3>Breed 2</h3>
<p>About breed 2</p>

<h2>About birds</h2>
<p>Info about birds</p>

Теперь то, что я хочу, - это выполнить некоторый обход DOM, получить все теги заголовков, добавить к ним атрибут id, сделав их содержимое змеиным футляром

<h2 id="about-dogs" >About Dogs</h2>

Затем создайте элемент списка со следующим содержимым.

Вложение будет выполнено в соответствии с положением и тегом заголовка. Означает, что каждый заголовок будет вложен в первый заголовок более высокого уровня и т. Д.

Итак, если есть только один h1, он сформирует дерево с h1 как root и самым низким уровнем заголовки как листья.

<ul>
   <li><a href="#about-cats" >About cats</a></li>
   <li><a href="#about-dogs">About dogs</a></li>
      <ul>
        <li><a href='#breed-1' >Breed 1</a></li>
        <li><a href='#breed-2' >Breed 1</a></li>
      </ul>
    <li><a href='#about-birds' >About birds</a></li>
</ul>

Ответы [ 2 ]

1 голос
/ 02 августа 2020

ДЕМО

function getHeaders() {
  const hTags = ["h1", "h2", "h3", "h4", "h5", "h6"];
  const elements = document.querySelectorAll(hTags.join());
  const headers = [];

  elements.forEach(el => {
    const text = el.innerText;
    const id = text
      .toLowerCase()
      .split(" ")
      .join("-");

    el.setAttribute("id", id);

    headers.push({
      id,
      text,
      level: hTags.indexOf(el.tagName.toLowerCase())
    });
  });

  return headers;
}

function buildTree(headers) {
  const list = [];
  let nextLevelHeaders = [];
  let lastLevel = -1;

  if (headers.length === 0) {
    return "";
  }

  const buildSubTree = () => {
    if (nextLevelHeaders.length > 0) {
      list[list.length - 1] += buildTree(nextLevelHeaders);
    }
  };

  headers.forEach(h => {
    if (lastLevel !== -1 && lastLevel < h.level) {
      nextLevelHeaders.push(h);
      return;
    }

    buildSubTree();

    lastLevel = h.level;
    list.push(`<a href="#${h.id}">${h.text}</a>`);
    nextLevelHeaders = [];
  });

  buildSubTree();

  const listHTML = list.map(i => `<li>${i}</li>`).join("");
  return `<ul>${listHTML}</ul>`;
}

const headers = getHeaders();

document.querySelector("#root").innerHTML = buildTree(headers);
<div id="root"></div>

<h3>About horses (h3)</h3> <!-- corner case -->
<p>Some Info about horses</p>

<h2>About cats (h2)</h2>
<p>Some Info about cats</p>

<h2>About dogs (h2)</h2>
<p>Some Info about cats</p>
<h3>Breed 1 (h3)</h3>
<p>About breed 1</p>
<h3>Breed 2 (h3)</h3>
<p>About breed 2</p>
<h4>Breed 2.1 (h4)</h4>
<p>About breed 2.1</p>
<h4>Breed 2.2 (h4)</h4>
<p>About breed 2.2</p>
<h3>Breed 3 (h3)</h3>
<p>About breed 3</p>
<h3>Breed 4 (h3)</h3>
<p>About breed 4</p>

<h2>About birds (h2)</h2>
<p>Info about birds</p>

<h4>Bird <b>one</b> (h4)</h4><!-- corner case -->
<p>Info about birds</p>
1 голос
/ 02 августа 2020

Хорошо, я думаю, что мы можем добиться большего, но пока этот код делает то, что вы ожидаете, при условии, что в вашем html есть элемент с id = "list" для поместите свой список в

посмотрите его в действии на codepen

<nav id="list">
</nav>
let headers = document.querySelectorAll('h1, h2, h3')

let list = document.createElement('ul')
document.querySelector('#list')
  .appendChild(list)
  // list is now a ul in the nav section

let currentListLevel = 0
let currentListUl = list
let lastListItem = list
headers.forEach(h => {
  let text = h.innerText
  let level = h.tagName.slice(-1)
  console.log(level + ':' + text)
  let snakeCase = text.toLowerCase()
    .trim()
    .replace(' ', '-')
  
  h.id = snakeCase // now title has id
  
  let link = document.createElement('a')
  // create the link
  link.appendChild(document.createTextNode(text))
  // give it the text of the header
  link.href = '#' + snakeCase
  // give it the reference to the header
  let li = document.createElement('li')
  li.appendChild(link)
  
  if (level === currentListLevel) {
    currentListUl.appendChild(li)
    lastListItem = li
  } else if (level > currentListLevel) {
    currentListLevel = level
    let ul = document.createElement('ul')
    ul.level = level // store the level in a property
    ul.appendChild(li)
    lastListItem.appendChild(ul)
    currentListUl = ul
    lastListItem = li
  } else if (level < currentListLevel) {
    while (level < currentListLevel) {
      currentListUl = currentListUl.parentNode
      level = currentListUl.level
    }
    currentListUl.appendChild(li)
    lastListItem = li
    currentListLevel = level
  }
  
})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...