簡體   English   中英

僅在不存在的情況下匹配字符串<script> or <a> tags

[英]Matching a string only if it is not in <script> or <a> tags

我正在開發一個瀏覽器插件,該插件用<a href="http://domain.com/$1">$1</a>替換所有“ someString”(由復雜的正則表達式定義)的實例。 通常,只要對主體的innerHTML進行全局替換,就可以正常工作。 但是,當它找到(並替換) <script>標記內的“ someString”(即作為JS變量或其他JS引用)時,它將中斷頁面。 如果“ someString”已經是錨的一部分,它也會中斷。

因此,基本上,我想對“ someString”的所有實例進行全局替換,除非它屬於<script></script><a></a>標記集。

我現在基本上擁有的是:

var body = document.getElementsByTagName('body')[0].innerHTML;
body = body.replace(/(someString)/gi, '<a href="http://domain.com/$1">$1</a>');
document.getElementsByTagName('body')[0].innerHTML = body;

但是顯然那還不夠好。 我已經苦苦掙扎了幾個小時,並且在這里閱讀了所有答案(包括許多堅決要求正則表達式不能與HTML一起使用的答案),所以我願意就如何做到這一點提出建議。 我更喜歡直接使用JS,但如有必要可以使用jQuery。

編輯-示例HTML

<body>
  someString
  <script type="text/javascript">
  var someString = 'blah';
  console.log(someString);
  </script>
  <a href="someString.html">someString</a>
</body>

在這種情況下,僅應替換“ someString”的第一個實例。

好吧,您可以將XPath與Mozilla結合使用(假設您正在為FireFox編寫插件)。 呼叫是document.evaluate 或者,您可以使用XPath庫來實現(那里有一些)...

var matches = document.evaluate(
    '//*[not(name() = "a") and not(name() = "script") and contains(., "string")]',
    document,
    null,
    XPathResult.UNORDERED_NODE_ITERATOR_TYPE
    null
);

然后使用回調函數替換:

var callback = function(node) {
    var text = node.nodeValue;
    text = text.replace(/(someString)/gi, '<a href="http://domain.com/$1">$1</a>');
    var div = document.createElement('div');
    div.innerHTML = text;
    for (var i = 0, l = div.childNodes.length; i < l; i++) {
        node.parentNode.insertBefore(div.childNodes[i], node);
    }
    node.parentNode.removeChild(node);
};
var nodes = [];
//cache the tree since we want to modify it as we iterate
var node = matches.iterateNext();
while (node) {
    nodes.push(node);
    node = matches.iterateNext();
}
for (var key = 0, length = nodes.length; key < length; key++) {
    node = nodes[key];
    // Check for a Text node
    if (node.nodeType == Node.TEXT_NODE) {
        callback(node);
    } else {
        for (var i = 0, l = node.childNodes.length; i < l; i++) {
            var child = node.childNodes[i];
            if (child.nodeType == Node.TEXT_NODE) {
                callback(child);
            }
        }
    }
}

嘗試一下,看看它是否滿足您的需求(已在IE 8和Chrome中進行了測試)。

<script src="jquery-1.4.4.js" type="text/javascript"></script>
<script>
  var pattern = /(someString)/gi;
  var replacement = "<a href=\"http://domain.com/$1\">$1</a>";

  $(function() {
    $("body :not(a,script)")
      .contents()
      .filter(function() { 
        return this.nodeType == 3 && this.nodeValue.search(pattern) != -1;
      })
      .each(function() {
        var span = document.createElement("span");
        span.innerHTML = "&nbsp;" + $.trim(this.nodeValue.replace(pattern, replacement));
        this.parentNode.insertBefore(span, this);
        this.parentNode.removeChild(this);
      });
  });
</script>

該代碼使用jQuery查找文檔的<body>中不在<anchor><script>塊中的所有文本節點,並且包含搜索模式。 找到這些內容后,將注入包含目標節點的已修改內容的跨度,並刪除舊的文本節點。

我看到的唯一問題是IE 8處理的文本節點僅包含空白而不是Chrome,因此有時替換會丟失前導空格,因此在包含正則表達式替換的文本之前插入了不間斷空格。

我知道您不想聽這個,但這聽起來不像是正則表達式的工作。 正則表達式在變得復雜且難以理解之前,不能很好地進行否定匹配。

也許這個正則表達式可能足夠接近:

/>[^<]*(someString)[^<]*</

它捕獲在>和<之間的someString的任何實例。

另一個想法是,如果您確實使用jQuery,則可以使用:contains偽選擇器。

$('*:contains(someString)').each(function(i)
{
    var markup = $(this).html();
    // modify markup to insert anchor tag
    $(this).html(markup)
});

這將獲取文本中包含“ someString”的所有DOM項目。 我認為它不會遍歷<script>標記,所以您應該不錯。

您可以嘗試以下方法:

/(someString)(?![^<]*?(<\/a>|<\/script>))/

我沒有測試每個schenario,但是它基本上是使用負前行查找someString之后的下一個左括號,並且如果該括號是錨或腳本結束標記的一部分,則不匹配。

您的示例似乎在這種提琴上奏效,盡管它當然不能涵蓋所有可能性。 如果<a></a>中的innerHTML包含標簽(例如<b><span> ),或者腳本標簽中的代碼生成html(包含其中包含標簽的字符串),則您還需要更多內容復雜。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM