HTML:
<div class="someclass">
<h3>First</h3>
<strong>Second</strong>
<hr>
Third
<br>
Fourth
<br>
<em></em>
...
</div>
从上面的
div
节点我想获取hr
之后的所有子文本节点("Third"
,"Fourth"
,...可能还有更多)
如果我这样做
document.querySelectorAll('div.someclass>hr~*')
我得到
NodeList [ br, br, em, ... ]
- 没有文本节点
与下面
document.querySelector('div.someclass').textContent
我将所有文本节点作为单个字符串
我可以将每个文本节点获取为
var third = document.querySelector('div.someclass').childNodes[6].textContent
var fourth = document.querySelector('div.someclass').childNodes[8].textContent
所以我尝试了
document.querySelector('div.someclass').childNodes[5:] # SyntaxError
slice()
document.querySelector('div.someclass').childNodes.slice(5) # TypeError
有什么方法可以获取从
hr
节点开始的所有子文本节点吗?
更新
我忘了提这个问题是关于网络抓取,而不是网络开发......我无法更改 HTML 源代码
您可以获取内容并使用
hr
进行拆分来获取 hr
之后的 html,然后在 div
中替换此内容,您将能够操纵此 div
来获取您的内容:
var content = document.querySelector('.someclass').innerHTML;
content = content.split('<hr>');
content = content[1];
document.querySelector('.hide').innerHTML = content;
/**/
var nodes = document.querySelector('.hide').childNodes;
for (var i = 0; i < nodes.length; i++) {
console.log(nodes[i].textContent);
}
.hide {
display: none;
}
<div class="someclass">
<h3>First</h3>
<strong>Second</strong>
<hr> Third
<br> Fourth
<br>
<em></em> ...
</div>
<div class="hide"></div>
.childNodes
包括文本和非文本节点。
你的语法错误是因为你不能像javascript中的
[5:]
那样进行数组切片。
而且 NodeList 类似于数组...但不是数组...这就是为什么
slice
不能直接在 childNodes
上工作。
1) 获取你的 NodeList
var nodeList = document.querySelector('.some-class').childNodes;
2)将NodeList转换为实际数组
nodes = Array.prototype.slice.call(nodes);
(注意在现代 ES6 浏览器中你可以这样做
nodes = Array.from(nodes);
此外,现代浏览器还添加了对 NodeList 对象的 .forEach
支持...因此您可以直接使用 .forEach
,而无需在现代浏览器中对 NodeList 进行数组转换)
3)迭代收集你想要的文本节点
这取决于你自己的逻辑。但是您可以迭代节点并测试是否
node.nodeType == Node.TEXT_NODE
来查看任何给定节点是否是文本节点。
var foundHr = false,
results = [];
nodes.forEach(el => {
if (el.tagName == 'HR') { foundHr = true; }
else if (foundHr && el.nodeType == Node.TEXT_NODE) {
results.push(el.textContent);
}
});
console.log(results);
您可以使用这段代码获取
node
下的所有文本节点:
var walker = document.createTreeWalker(node, NodeFilter.SHOW_TEXT, null, false);
var textNode;
var result = [];
while (textNode = walker.nextNode()) {
result.push(textNode);
}
并且您已经获得了
Array
文本节点,因此您可以根据需要 slice()
来使用它:
console.log(result.slice(5));
const { childNodes } = <Element>document.querySelector('.someclass'); // throws an error if element doesn't exist
const siblings = sliceNodeList([], false, 'hr', ...childNodes);
function sliceNodeList(nodes: Node[], deep: boolean, selector: string, node?: Node, ...more: Node[]) {
if (!selector) return nodes;
if (!node) return nodes;
const { nodeType } = node;
const handle = { // not concerned with Attr Nodes
[Node.ELEMENT_NODE]: handleElement,
[Node.TEXT_NODE]: handleOther,
[Node.COMMENT_NODE]: handleOther,
}[ nodeType ];
if (handle) handle.call(this, nodes, deep, selector, node);
if (more.length) sliceNodeList(nodes, deep, selector, ...more);
return nodes;
}
function handleElement(nodes: Node[], deep: boolean, selector: string, node: Element) {
const { childNodes } = node; // not concerned with Attr Nodes
const matches = node.matches(selector);
if (nodes.length) nodes.push(node); // assume we must have already matched
else if (matches) nodes.push(node); // we matched on an element
if (deep) sliceNodeList(nodes, deep, selector, ...childNodes); // keep diving into substructures
return nodes;
}
function handleOther(nodes: Node[], deep: boolean, selector: string, node: Text|Comment) {
if (nodes.length) return [ ...nodes, node ]; // assume we must have already matched
if (node.data === selector) return [ ...nodes, node ]; // we matched on a Text or Comment value
return nodes;
}
基本上,这只是通过检查在映射到正确的节点类型的处理程序时是否有更多同级来使用递归。它通过调用父函数来使用相互递归(如果
deep === true
)来深入嵌套结构。我们在这里并不关心 Attr
Node
,但您可以为此设置一个处理程序。
因为
Text
和 Comment
共享足够多的相同接口 (node.data
),我们能够重用相同的函数。该函数与 handleElement
类似,假设 if (nodes.length)
那么我们已经匹配,并且可以安全可靠地将当前节点添加到集合中。
与
handleElement
不同,Text
或 Comment
无法呼叫 node.matches
。但是,如果节点的值等于 selector
,我们仍然可以在节点上进行匹配 - 使我们能够通过文本进行选择。我们可以更进一步,我们可以使用 ===
或 String.prototype.includes
之类的东西,而不是使用 RegExp
对整个文本(或注释)值使用严格相等。
我们可以使用
for
、while
或其他迭代方法。然而,在我看来,递归是遍历 DOM 的更好方法,因为 DOM 是集成的(Node
实现了复合模式),因此是递归结构。我也只是很快地在 VSCode 中完成了这个,甚至没有在浏览器控制台中运行它,但我一直在编写这个模式,所以它应该非常接近 good-to-go。
希望有帮助。