简体   繁体   English

用元素替换 TextNode 中间的文本

[英]Replace text in the middle of a TextNode with an element

I want to insert html tags within a text node with TreeWalker, but TreeWalker forces my html brackets into & lt;我想在带有 TreeWalker 的文本节点中插入 html 标签,但 TreeWalker 将我的 html 括号强制插入 & lt; & gt; > no matter what I've tried.不管我试过什么。 Here is the code:这是代码:

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;
}

Using \< or " instead of ' won't help. My workaround is to copy all of the DOM tree to a string and to replace the html body with that. It works on very simple webpages and solves my first problem, but is a bad hack and won't work on anything more than a trivial page. I was wondering if I could just work straight with the text node rather than use a workaround. Here is the code for the (currently buggy) workaround:使用 \< 或 " 而不是 ' 不会有帮助。我的解决方法是将所有 DOM 树复制到一个字符串并用它替换 html 正文。它适用于非常简单的网页并解决了我的第一个问题,但是糟糕的 hack,除了一个微不足道的页面之外什么都不会工作。我想知道我是否可以直接使用文本节点而不是使用解决方法。这是(当前有问题的)解决方法的代码:

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;

Edit: I realize a better workaround would be to custom tag the text nodes ((Customtag_Start_Here) etc.), copy the whole DOM to a string, and use my customs tags to identify text nodes and modify them that way.编辑:我意识到更好的解决方法是自定义标记文本节点((Customtag_Start_Here)等),将整个 DOM 复制到一个字符串,并使用我的自定义标记来识别文本节点并以这种方式修改它们。 But if I don't have to, I'd rather not.但如果我不必这样做,我宁愿不这样做。

To 'change' a text node into an element, you must replace it with an element . 要将文本节点“更改”为元素,必须将其替换为element For example: 例如:

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

If you want to retain part of the text node, and inject an element "in the middle", you need to create another text node and insert it and the element into the tree at the appropriate places in the tree. 如果要保留文本节点的一部分,并在“中间”注入元素,则需要创建另一个文本节点并将其和该元素插入树中树的适当位置。


Edit : Here's a function that might be super useful to you. 编辑 :这是一个功能,可能对您超级有用。 :) :)

Given a text node, it runs a regex on the text values. 给定一个文本节点,它将在文本值上运行一个正则表达式。 For each hit that it finds it calls a custom function that you supply. 对于找到的每个匹配,它将调用您提供的自定义函数。 If that function returns a string, then the match is replaced. 如果该函数返回一个字符串,则替换匹配项。 However, if that function returns an object like: 但是,如果该函数返回如下对象:

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

then it will split the text node around the match and inject an element in that location. 那么它将在匹配项周围拆分文本节点,并在该位置插入一个元素。 You can also return an array of strings or those objects (and can recursively use arrays, strings, or objects as the content property). 您还可以返回字符串数组或那些对象(并且可以递归地使用数组,字符串或对象作为content属性)。

Demo: http://jsfiddle.net/DpqGH/8/ 演示: 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+"");
  }
}

It's not quite perfectly generic, as it does not support namespaces on attributes. 它不是很完美的通用名称,因为它不支持属性上的名称空间。 But hopefully it's enough to get you going. 但希望这足以使您前进。 :) :)


You would use it like so: 您可以这样使用它:

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;
}

or if you want something closer to your original regex: 或者,如果您想要更接近原始正则表达式的内容:

  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]
      }
    ];
  });

Function that returns the parent element of any text node including partial match of passed string: Function 返回任何文本节点的父元素,包括传递的字符串的部分匹配:

function findElByText(text, mainNode) {
    let textEl = null;
    const traverseNodes = function (n) {
        if (textEl) {
            return;
        }
        for (var nodes = n.childNodes, i = nodes.length; i--;) {
            if (textEl) {
                break;
            }
            var n = nodes[i], nodeType = n.nodeType;
            // Its a text node, check if it matches string
            if (nodeType == 3) {
                if (n.textContent.includes(text)) {
                    textEl = n.parentElement;
                    break;
                }
            }
            else if (nodeType == 1 || nodeType == 9 || nodeType == 11) {
                traverseNodes(n);
            }
        }
    }
    traverseNodes(mainNode);
    return textEl;
}

Usage:用法:

findElByText('Some string in document', document.body);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM