JS-查找字符串的所有实例并包装在锚定标记中

时间:2019-05-03 07:58:30

标签: javascript html

我正在尝试确定是否可以使用锚标记包装字符串的所有实例。

基本上我想在我们的网站上找到myBrandmyBrand.com的任何实例,并将它们包装在诸如<a href="/">myBrand</a><a href="/">myBrand.com</a>之类的锚点中。

但是,我希望能够找到所有此类实例,无论它们位于页面上各个元素中的深度如何,并查看它们是否已链接,这就是我遇到的问题。

// -----编辑----- //

我已经将代码段修改为现在的内容,但是仍然遇到困难

document.querySelectorAll('body').forEach(function(el){
  var text = el.innerHTML.match(/myBrand/g);
  
  console.log(text.parentElement); // how do i get the parent?
  
  if(isAnchor(text.parentElement)){
  	//
  }else{
	  var newtext = el.innerHTML.replace(/myBrand/g, '<a href="/">myBrand</a>');
  	el.innerHTML = newtext;
  }
});

function isAnchor(str){
    return /^\<a.*\>.*\<\/a\>/i.test(str);
}
<body>

myBrand

<h1>myBrand.com</h1>

<p>This is a div that mentions myBrand</p>

<table>
<thead>
    <tr>
    <td>This is myBrand's table header</td>
    </tr>
</thead>
<tbody>
  <tr>
  <td>myBrand.com</td>
  </tr>
</tbody>
</table>

<a href="/">myBrand.com</a>

</body>

3 个答案:

答案 0 :(得分:1)

您可以将replace与RegEx一起使用,以如下方式设置innerHTML属性:

document.body.innerHTML = document.body.innerHTML.replace(/myBrand(?!\.)/g, '<a href="/">myBrand</a>');
document.body.innerHTML = document.body.innerHTML.replace(/myBrand.com/g, '<a href="/">myBrand.com</a>');
<body>

  myBrand

  <h1>myBrand.com</h1>

  <p>This is a div that mentions myBrand</p>

  <table>
    <thead>
      <tr>
        <td>This is myBrand's table header</td>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>myBrand.com</td>
      </tr>
    </tbody>
  </table>

  <a href="/">myBrand.com</a>

</body>

答案 1 :(得分:1)

使用innerHTML替换html内容的一个问题是,一旦您使用innerHTML设置新的html,分配给现有元素的任何数据或事件都将被删除。在document.body级别执行此操作是一个非常糟糕的主意。

因此,我写了一个使用文本节点的解决方案。文本节点不能有事件,因此删除文本节点并添加新节点不会有任何副作用(选择除外,在这种情况下我们可以忽略)。

function iterateTextNodes(container, callback) {
    Array.from(container.childNodes).forEach(node => {
        if (node.nodeType === node.TEXT_NODE) {
            callback(node);
        } else if (node.tagName !== 'A') {
            /* Do not go inside anchor tags checking for text nodes,
               this get rids of the problem with nodes that are already
               inside anchor tags */
            iterateTextNodes(node, callback);
        }
    });
}

function getAnchor(href, content) {
    const anchor = document.createElement('A');
    anchor.appendChild(document.createTextNode(content));
    anchor.setAttribute('href', href);
    return anchor;
}

iterateTextNodes(document.body, function (node) {
    const parentNode = node.parentNode;
    // Check for myBrand.com first and then check for myBrand, to handle all the cases
    node.nodeValue.split('myBrand.com').forEach((string, i) => {
        i && parentNode.insertBefore(getAnchor('/', 'myBrand.com'), node);
        string.split('myBrand').forEach((string, i) => {
            i && parentNode.insertBefore(getAnchor('/', 'myBrand'), node);
            parentNode.insertBefore(document.createTextNode(string), node);
        });
    });
    parentNode.removeChild(node);
});
<body>

myBrand

<h1>myBrand.com</h1>

<p>This is a div that mentions myBrand</p>

<table>
<thead>
    <tr>
    <td>This is myBrand's table header</td>
    </tr>
</thead>
<tbody>
  <tr>
  <td>myBrand.com</td>
  </tr>
</tbody>
</table>

<a href="/">myBrand.com</a>

</body>

答案 2 :(得分:0)

您可以为此使用XPath

function textContaining(phrase) {
  return document.evaluate(
    "//text()[contains(., '" + phrase + "')]",
    document,
    null,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
    null
  )
}

然后您可以像这样遍历结果:

var result = textContaining(phrase)
for (var i = 0; i < result.snapshotLength; ++i) {
  var originalNode = result.snapshotItem(i)
  // ...
}

一旦有了匹配的#text个节点列表,您只需要过滤掉.parentElement是一个<a>标签的节点即可。

或者,您可以使用jQuery .parents()功能来检查父母的父母,从而解决<a><span>sometext</span></a>之类的情况,而不仅仅是直接子女的情况。