说我有这样的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时,您不必为每个选择器都编写自定义代码。
主要问题是在上述情况下如何将孩子编织给父母。