【发布时间】:2015-11-04 13:48:24
【问题描述】:
我正在尝试访问给定元素中的所有文本节点,以便我可以隔离单词并将它们包装在 span 中。
TreeWalker 似乎是这项工作的 API,但我发现它非常不直观。在我看来,spec 和 MDN reference(通常非常擅长解释神秘的 DOM API)都不是不言而喻的。
我的第一个假设是,我只需要传递正确的过滤器作为第二个参数 - 类似于 document.createTreeWalker( element, NodeFilter.TEXT_NODE )。但这似乎一遇到非文本节点就停止了:
wordWrap( document.body )
function wordWrap( element ){
var nodes = document.createTreeWalker( element, NodeFilter.TEXT_NODE )
var node
var text
var word
while( node = nodes.nextNode() ){
text = node.nodeValue.replace( /(^\s+|\s+$)/, '' ).split( /\s+/g )
while( text.length ){
word = document.createElement( 'span' )
word.className = 'word'
word.innerText = text.shift()
node.parentNode.insertBefore( word, node )
if( text.length )
node.parentNode.insertBefore( document.createTextNode( ' ' ), node )
}
node.parentNode.removeChild( node )
}
}
.word {
background: #fee;
padding: 0 .5em 0 0;
}
Contact us at <a href="mailto:email@example.com">email@example.com</a> for submissions &#38; other enquiries.
所以我认为这是使用TreeWalker 的第三个过滤器参数以及NodeFilter 上的额外属性的机会。如果过滤器方法的有效返回值是FILTER_ACCEPT、FILTER_REJECT 和FILTER_SKIP,那么我推断通过在第二个参数中接受元素节点以及文本节点,我可以指定应该接受文本节点,其余的跳过。但这似乎给出了相同的结果 - 在锚点内或锚点之后没有拾取文本节点:
wordWrap( document.body )
function wordWrap( element ){
var nodes = document.createTreeWalker(
element,
NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT,
{ acceptNode : function( node ){
if( node.nodeType === node.TEXT_NODE )
return NodeFilter.FILTER_ACCEPT
else
return NodeFilter.FILTER_SKIP
} }
)
var node
var text
var word
while( node = nodes.nextNode() ){
text = node.nodeValue.replace( /(^\s+|\s+$)/, '' ).split( /\s+/g )
while( text.length ){
word = document.createElement( 'span' )
word.className = 'word'
word.innerText = text.shift()
node.parentNode.insertBefore( word, node )
if( text.length )
node.parentNode.insertBefore( document.createTextNode( ' ' ), node )
}
node.parentNode.removeChild( node )
}
}
.word {
background: #fee;
padding: 0 .5em 0 0;
}
Contact us at <a href="mailto:email@example.com">email@example.com</a> for submissions &#38; other enquiries.
到目前为止,我确信使用 DOM1 方法递归遍历树会更容易,就像在这个 sn-p 中一样:
wordWrap( document.body )
function wordWrap( element ){
textNodes( element ).forEach( function( node ){
var text = node.nodeValue.split( /\s+/g )
var word
while( text.length ){
word = document.createElement( 'span' )
word.className = 'word'
word.innerText = text.shift()
node.parentNode.insertBefore( word, node )
if( text.length )
node.parentNode.insertBefore( document.createTextNode( ' ' ), node )
}
node.parentNode.removeChild( node )
} )
}
function textNodes( element ){
var nodes = []
Array.prototype.forEach.call( element.childNodes, function( child ){
if( child.nodeType === child.TEXT_NODE )
nodes = nodes.concat( child )
else if( child.nodeType === child.ELEMENT_NODE )
nodes = nodes.concat( textNodes( child ) )
} )
return nodes
}
.word {
background: #fee;
padding: 0 .5em 0 0;
}
Contact us at <a href="mailto:email@example.com">email@example.com</a> for submissions &#38; other enquiries.
我错过了什么?
【问题讨论】:
-
我猜
node.parentNode.removeChild( node )是问题所在 - 您正在从 DOM 中删除当前节点,因此 walker 将在分离状态下找不到.nextNode()。 -
@Bergi 这听起来像是我正在寻找的答案!
标签: javascript dom