【问题标题】:Replace text in the middle of a TextNode with an element用元素替换 TextNode 中间的文本
【发布时间】:2014-04-03 11:26:13
【问题描述】:

我想使用 TreeWalker 在文本节点中插入 html 标记,但 TreeWalker 将我的 html 括号强制放入 & lt; >不管我试过什么。代码如下:

var text;
var tree = document.createTreeWalker(document.body,NodeFilter.SHOW_TEXT);
while (tree.nextNode()) {
    text = tree.currentNode.nodeValue;
    text = text.replace(/(\W)(\w+)/g, '$1<element onmouseover="sendWord(\'$2\')">$2</element>');
    text = text.replace(/^(\w+)/, '<element onmouseover="sendWord(\'$1\')">$1</element>');
    tree.currentNode.nodeValue = text;
}

使用 \

var text;
var newHTML = "";
var tree = document.createTreeWalker(document.body);
while (tree.nextNode()) {
    text = tree.currentNode.nodeValue;
    if (tree.currentNode.nodeType == 3){
        text = text.replace(/(\W)(\w+)/g, '$1<element onmouseover="sendWord(\'$2\')">$2</element>');
        text = text.replace(/^(\w+)/, '<element onmouseover="sendWord(\'$1\')">$1</element>');
        }
    newHTML += text
}
document.body.innerHTML = newHTML;

编辑:我意识到更好的解决方法是自定义标记文本节点((Customtag_Start_Here)等),将整个 DOM 复制到字符串,并使用我的自定义标记来识别文本节点并以这种方式修改它们。但如果我不必这样做,我宁愿不要。

【问题讨论】:

  • 您不能在文本节点中注入 HTML...
  • 我可以告诉文本节点它不想再成为文本节点了吗(即告诉它有很多文本节点子节点)?
  • @e2r2i2k2 不可以。可以替换,可以拆分,但不能更改。
  • 请注意,如果您在行走过程中创建新的文本节点,那么您也会行走并访问它们。根据我的回答,最简单的方法是收集所有文本节点然后遍历它们。

标签: javascript html dom textnode


【解决方案1】:

要将文本节点“更改”为元素,您必须replace it with an element。例如:

var text = tree.currentNode;
var el = document.createElement('foo');
el.setAttribute('bar','yes');
text.parentNode.replaceChild( el, text );

如果要保留部分文本节点,并在“中间”注入一个元素,则需要将create another text nodeinsert it 以及该元素放入树中适当位置的树中。


编辑:这是一个可能对您非常有用的功能。 :)

给定一个文本节点,它会在文本值上运行一个正则表达式。对于它发现的每个命中,它都会调用您提供的自定义函数。如果该函数返回一个字符串,则匹配被替换。但是,如果该函数返回如下对象:

{ name:"element", attrs{onmouseover:"sendWord('foo')"}, content:"foo" }

然后它将分割匹配周围的文本节点并在该位置注入一个元素。您还可以返回一个字符串数组或那些对象(并且可以递归地使用数组、字符串或对象作为content 属性)。

演示:http://jsfiddle.net/DpqGH/8/

function textNodeReplace(node,regex,handler) {
  var mom=node.parentNode, nxt=node.nextSibling,
      doc=node.ownerDocument, hits;
  if (regex.global) {
    while(node && (hits=regex.exec(node.nodeValue))){
      regex.lastIndex = 0;
      node=handleResult( node, hits, handler.apply(this,hits) );
    }
  } else if (hits=regex.exec(node.nodeValue))
    handleResult( node, hits, handler.apply(this,hits) );

  function handleResult(node,hits,results){
    var orig = node.nodeValue;
    node.nodeValue = orig.slice(0,hits.index);
    [].concat(create(mom,results)).forEach(function(n){
      mom.insertBefore(n,nxt);
    });
    var rest = orig.slice(hits.index+hits[0].length);
    return rest && mom.insertBefore(doc.createTextNode(rest),nxt);
  }

  function create(el,o){
    if (o.map) return o.map(function(v){ return create(el,v) });
    else if (typeof o==='object') {
      var e = doc.createElementNS(o.namespaceURI || el.namespaceURI,o.name);
      if (o.attrs) for (var a in o.attrs) e.setAttribute(a,o.attrs[a]);
      if (o.content) [].concat(create(e,o.content)).forEach(e.appendChild,e);
      return e;
    } else return doc.createTextNode(o+"");
  }
}

它不是完全通用的,因为它不支持属性上的命名空间。但希望这足以让你继续前进。 :)


你会这样使用它:

findAllTextNodes(document.body).forEach(function(textNode){
  replaceTextNode( textNode, /\b\w+/g, function(match){
    return {
      name:'element',
      attrs:{onmouseover:"sendWord('"+match[0]+"')"},
      content:match[0]
    };
  });
});

function findAllTextNodes(node){
  var walker = node.ownerDocument.createTreeWalker(node,NodeFilter.SHOW_TEXT);
  var textNodes = [];
  while (walker.nextNode())
    if (walker.currentNode.parentNode.tagName!='SCRIPT')
      textNodes.push(walker.currentNode);
  return textNodes;
}

或者如果您想要更接近原始正则表达式的内容:

  replaceTextNode( textNode, /(^|\W)(\w+)/g, function(match){
    return [
      match[1], // might be an empty string
      {
        name:'element',
        attrs:{onmouseover:"sendWord('"+match[2]+"')"},
        content:match[2]
      }
    ];
  });

【讨论】:

  • 我知道现在该怎么做,但是上面的代码会吐出一个很长的DomException....message: Node was not found 错误。我会继续玩弄它以使其正常工作。
  • @e2r2i2k2 查看我的编辑,了解一种用元素“替换”文本节点中的文本并让它们自动为您拆分的方法。并确保查看它的应用程序的演示 URL。
  • 哇,谢谢!这个功能对我来说有很多东西可以玩。对于您的原始代码,我发现了问题。最后一行应该是text.parentNode.replaceChild( el, text ); 两个节点变量被颠倒了。
  • 如果文本节点的父节点为空怎么办?如何用 html 元素替换该文本节点?
最近更新 更多