在 JS replaceAll 操作期间忽略给定字符串中的 html 标记(特别是标记)

Ignore html tag (specifically a tag) from given string during JS replaceAll operation

我有这样的情况,我正在遍历 URL 数组(例如 [www.whosebug.com, www.ex.com])并在循环期间将那些 URL 与给定的字符串一一匹配并替换使用锚标记使其可点击。

我可以使用 JS replaceAll 方法来做到这一点,但是在给定字符串中多次出现相同的 url 它甚至匹配标签中的 url。

例如,如果给定的字符串是 "Check it out at www.stack.com/abc and bookmark the www.stack.com, www.overflow.com" 并且给定的 URL 数组是 [www.stack.com/abc, www.stack.com]

在第一次替换迭代期间,它将是 "Check it out at <a href="www.stack.com/abc">www.stack.com/abc</a> and bookmark the www.stack.com"

然后在第二次迭代时出现问题,它甚至会替换标签中的字符串。我想在 replaceAll 方法期间忽略 html 标记。有人可以帮我解决这个问题吗?

我尝试使用以下正则表达式忽略标签,但它不适用于锚标签之间的内容。

exString.replaceAll(new RegExp(url + "(?![^<>]*>)", "gi"), replaceText);

虽然 mplungjan 提供的解决方案很聪明而且效果很好,但我想 post 一个替代方案。

来自已接受答案的算法将输入字符串处理成单词数组,然后继续遍历每个 URL 上的每个单词。然后它需要查看是否有任何单词以符号结尾,如果是则截断。 这会有点消耗,因为可以想象 50 个单词 X 5 个可能的 URLs = 250 个组合和 O(n^2) 计算。然后想象可能有 20 个可能的 URL 和 20 个输入文本,每个文本包含 15 个以上的单词。最后,提及该算法可能存在区分大小写的问题。

此解决方案使用了 mplungjan 方法中的大量思想,但相反,它只会通过 RegEx 快速缩小实际需要处理的范围,然后再次循环以应用实际匹配的范围。此外,RegEx 更正了可能的区分大小写问题。

let str = 'Check it out at www.stack.com/abc and bookmark the www.stack.com, www.overflow.com.';
let urls = ["www.stack.com", "www.stack.com/abc", "www.not-here.com"];
let arReplace = [];

// sort by longest URLs (prevents processing identical root domains on sub-domains)
urls = urls.sort((a, b) =>{
  if(b.length > a.length)
    return 1
  return -1
});

// find URLs and apply replacement tokens
urls.forEach((url) => {
  if(str.match(new RegExp('\b' + url + '\b', 'i'))){
    arReplace.push(url);
    str = str.replace(new RegExp('\b' + url + '\b', 'gi'), '%ZZ' + (arReplace.length - 1) + 'ZZ%')
  }
});

// replace tokens
arReplace.forEach((url, n)  =>{
    str = str.replace(new RegExp('%ZZ' + n + 'ZZ%', 'g'), '<a href="' + url + '">' + url + '</a>')
});
document.body.innerHTML = str

Fiddle link: https://jsfiddle.net/e05o9cra/

那我们分头合体吧

const div = document.getElementById("text");
let str = div.textContent;
let arr = str.split(/ /)
console.log(arr)

const urls = ["www.stack.com/abc", "www.stack.com"];
arr.forEach((word,i) => {
  const punctuation = word.match(/(\W$)/)
  if (punctuation) word = word.slice(0,-1)
  const idx = urls.indexOf(word);
  if (idx !=-1) arr[i] = arr[i].replace(word,`<a href="${word}">${word}</a>`)
})
console.log(arr)
div.innerHTML = arr.join(" ")
<div id="text">Check it out at www.stack.com/abc and bookmark the www.stack.com, www.overflow.com.</div>