Как правильно сгенерировать структуру данных для этих вложенных списков HTML - PullRequest
0 голосов
/ 31 октября 2018

Скажи, что у меня есть HTML как это:

<body>
  <div>
    <div>
      <div>
        <p>
          <b>Title A</b>
        <p>
        <table>
          <tbody>
            <tr>
              <td>Item 1:</td>
            </tr>
            <tr>
              <td>
                <p><b>Content title 1.1</b></p>
                <p>Content 1.1</p>
                <p>Content 1.1b</p>
                <p>Content 1.1c</p>
                <p>Content 1.1d (arbitrary paragraph number follows content title)</p>
                <p>Content ...</p>
                <p><b>Content title 1.2</b></p>
                <p>Content 1.2</p>
                <p>Content 1.2b</p>
                <p>Content 1.2...</p>
                ...
              </td>
            </tr>
            <tr>
              <td>Item 2:</td>
            </tr>
            <tr>
              <td>
                <p><b>Content title 2.1</b></p>
                <p>Content 2.1</p>
                <p><b>Content title 2.2</b></p>
                <p>Content 2.2</p>
                ...
              </td>
            </tr>
            ...
          </tbody>
        </table>

        <p>
          <b>Title B</b>
        <p>
        <table>
          <tbody>
            <tr>
              <td>Item 1b:</td>
            </tr>
            <tr>
              <td>
                <p><b>Content title 1b.1</b></p>
                <p>Content 1b.1</p>
                <p><b>Content title 1b.2</b></p>
                <p>Content 1b.2</p>
                ...
              </td>
            </tr>
            <tr>
              <td>Item 2b:</td>
            </tr>
            <tr>
              <td>
                <p><b>Content title 2b.1</b></p>
                <p>Content 2b.1</p>
                <p><b>Content title 2b.2</b></p>
                <p>Content 2b.2</p>
                ...
              </td>
            </tr>
            ...
          </tbody>
        </table>

        ...

Это не очень семантическая структура, и нет такой раскладки, как вы хотели бы для окончательной структуры данных (ниже). Однако в HTML достаточно информации для восстановления вложенной структуры данных.

Очевидный, первый подход к преобразованию этого в структуру данных состоит в том, чтобы просто начать с верхнего / первого узла и двигаться вниз. Но для этого требуется много ручного кода, вам нужно вручную выяснить все детали того, как восстановить вложение и тому подобное, что в конечном итоге занимает довольно много времени.

Поэтому вместо этого мне интересно, как это сделать так: собрать каждый уровень данных в наборы. Затем просто подключите комплекты соответствующим образом.

Так что бы выглядело что-то так:

var titles = document.querySelector(
  'body > div > div > div > p > b'
)
var items = document.querySelector(
  'body > div > div > div > table tr:nth-child(odd) td'
)
var itemContentTitles = document.querySelector(
  'body > div > div > div > table tr:nth-child(even) p b'
)
// somehow select all children after the content title, 
// up until the next content title or til 
// there are no more elements.
var itemContents = document.querySelector(
  'body > div > div > div > table tr:nth-child(even) p'
)

Как только у вас есть данные в этих массивах, основная проблема заключается в том, как правильно сплетать их . Конечный результат, которым вы хотите быть таким:

[
  {
    text: 'Title A',
    items: [
      {
        text: 'Item 1',
        items: [
          {
            text: 'Content title 1.1',
            items: [
              { text: 'Content 1.1' },
              { text: 'Content 1.1b' },
              { text: 'Content 1.1c' },
              { text: 'Content 1.1d' }
              ...
            ]
          },
          {
            text: 'Content title 1.2',
            items: [
              { text: 'Content 1.2' },
              { text: 'Content 1.2b' },
              { text: 'Content 1.2...' }
              ...
            ]
          }
        ]
      },
      {
        text: 'Item 2',
        items: [
          {
            text: 'Content title 2.1',
            items: [
              { text: 'Content 2.1' }
              ...
            ]
          },
          {
            text: 'Content title 2.2',
            items: [
              { text: 'Content 2.2' }
              ...
            ]
          }
        ]
      }
    ]
  },

  {
    text: 'Title B',
    items: [
      ...
    ]
  }
]

Вопрос в том, как перейти к массивам элементов, которые были выбраны с помощью некоторых базовых селекторов CSS (и поэтому не требует тонны пользовательского программирования), к этой вложенной структуре данных JSON.

Кажется, что вы должны иметь возможность перебирать каждый элемент в каждом списке, а затем проверять его отношение к родителю. Конечно, мы бы указали, какой список следует применять в качестве дочерних к другому списку. Таким образом, мы могли бы сделать:

function assignChildren(parents, children) {

}

Затем выполните:

assignChildren(titles, items)

Но нам, вероятно, на самом деле нужно начинать снизу вверх и делать:

assignChildren(itemContentTitles, itemContents)

Хитрость здесь в том, как решить проблему, зная, какой элемент itemContents принадлежит к itemContentTitle. Мы знаем из HTML, как они связаны. Это в основном это:

itemContent
  .parentNode
  .parentNode
  .previousElementSibling
    .firstElementChild
      .firstElementChild

Но вопрос в том, как мы можем решить эту проблему в общем , чтобы не требовалось писать собственный код. То есть вы просто делаете это:

assignChildren(itemContentTitles, itemContents)

... И получите вложенную структуру JSON. Кажется, это будет работать примерно так:

Начать с элемента itemContents (один из элементов дочернего списка). Тогда мы знаем родительский селектор, который нашел родительские элементы. Так что перемещайтесь вверх, назад и вниз как-нибудь, чтобы соответствовать родительскому селектору для этого элемента. Вот где это начинает становиться все сложнее, и у меня возникают проблемы с поиском того, как это сделать, не сканируя в основном весь DOM и не проверяя полный сгенерированный путь селектора для каждого элемента (что-то вроде). Хотите знать, как это сделать. Не обязательно быть супер производительным, но хорошая производительность - это всегда плюс.

Причина, по которой вы делаете это таким образом, заключается в том, что тогда вам потребуется только указать селекторы для различных частей вложенной структуры JSON и то, как каждый селектор связан с его родителем. Вам не придется писать собственный код для каждого отдельного селектора при навигации по DOM.

Главный вопрос заключается в том, как сделать ткачество детей для родителей в приведенном выше сценарии.

...