Я пытаюсь выяснить, можно ли обернуть все экземпляры строки тегом привязки.

В основном на нашем сайте я хочу найти любой экземпляр myBrand или myBrand.com и обернуть их в якорь, например <a href="/">myBrand</a> или <a href="/">myBrand.com</a>.

Тем не менее, я хочу иметь возможность найти все экземпляры этого независимо от того, как далеко они зарыты в различных элементах на моей странице, и посмотреть, связаны ли они уже, и именно здесь у меня возникла проблема.

//-----Edit-----//

Я отредактировал фрагмент к тому, что у меня есть сейчас, но все еще испытываю трудности

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>
0
Paddy Hallihan 3 Май 2019 в 10:58

3 ответа

Лучший ответ

Одна проблема с использованием innerHTML для замены содержимого html - любые данные или события, назначенные существующим элементам, будут удалены, как только вы установите новый html с помощью innerHTML. И делать это на уровне 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>
1
AvcS 3 Май 2019 в 08:59

Вы можете использовать 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>.

Кроме того, вы можете использовать функцию .parents() jQuery, чтобы проверить родителей-родителей, чтобы учитывать такие случаи, как <a><span>sometext</span></a>, а не только сценарии прямого потомка.

0
GoBusto 3 Май 2019 в 08:08

Вы можете использовать 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
Mamun 3 Май 2019 в 10:09