Оберните теги <a> вокруг текста http

Как найти каждое слово на странице, начинающееся с http://, и обернуть вокруг него теги?

Могу ли я использовать что-то вроде регулярных выражений?

3 ответа

Решение

Я сильно не согласен с тем, что jQuery может быть очень полезен при поиске решения здесь. Конечно, вам нужно разобраться с некоторыми из атрибутов элемента textNode, но, собрав DOM снова вместе после разделения вашего совпавшего узла, можно сделать несколько проще с помощью библиотеки jQuery.

Следующий код документирован для объяснения предпринятых действий. Я написал его как плагин jQuery на случай, если вы просто захотите взять его и переместить в другое место. Таким образом, вы можете указать, для каких элементов вы хотите конвертировать URL, или просто использовать селектор $("body").

(function($) {
    $.fn.anchorTextUrls = function() {
        // Test a text node's contents for URLs and split and rebuild it with an achor
        var testAndTag = function(el) {
            // Test for URLs along whitespace and punctuation boundaries (don't look too hard or you will be consumed)
            var m = el.nodeValue.match(/(https?:\/\/.*?)[.!?;,]?(\s+|"|$)/);

            // If we've found a valid URL, m[1] contains the URL
            if (m) {
                // Clone the text node to hold the "tail end" of the split node
                var tail = $(el).clone()[0];

                // Substring the nodeValue attribute of the text nodes based on the match boundaries
                el.nodeValue = el.nodeValue.substring(0, el.nodeValue.indexOf(m[1]));
                tail.nodeValue = tail.nodeValue.substring(tail.nodeValue.indexOf(m[1]) + m[1].length);

                // Rebuild the DOM inserting the new anchor element between the split text nodes
                $(el).after(tail).after($("<a></a>").attr("href", m[1]).html(m[1]));

                // Recurse on the new tail node to check for more URLs
                testAndTag(tail);
            }

            // Behave like a function
            return false;
        }

        // For each element selected by jQuery
        this.each(function() {
            // Select all descendant nodes of the element and pick out only text nodes
            var textNodes = $(this).add("*", this).contents().filter(function() {
                return this.nodeType == 3
            });


            // Take action on each text node
            $.each(textNodes, function(i, el) {
                testAndTag(el);
            });
        });
    }
}(jQuery));

$("body").anchorTextUrls(); //Sample call

Пожалуйста, имейте в виду, что, учитывая то, как я написал это для заполнения массива textNodes, метод найдет ВСЕ дочерние текстовые узлы, а не только непосредственные дочерние текстовые узлы. Если вы хотите, чтобы он заменял URL-адреса только среди текста в определенном селекторе, удалите вызов.add("*", this), который добавляет всех потомков выбранного элемента.

Вот пример скрипки.

Это одна из тех немногих вещей, которые JQuery не поможет вам напрямую. Вы в основном должны пройти через дерево DOM и исследовать текстовые узлы (nodeType === 3); если вы найдете текстовый узел, содержащий целевой текст, который вы хотите обернуть ("http://.....", какие бы правила вы не хотели применить), вы затем разбиваете текстовый узел (используя splitText) на три части (часть перед строкой, часть, которая является строкой, и часть, которая следует за строкой), затем поместите a Элемент вокруг второго из тех.

Это звучит немного сложно, но это не так уж и плохо. Это просто функция рекурсивного спуска (для работы через DOM), совпадение с регулярным выражением, чтобы найти вещи, которые вы хотите заменить, а затем пару вызовов splitText, createElement, insertBefore, appendChild,

Вот пример, который ищет фиксированную строку; просто добавьте соответствие регулярному выражению для "http://":

walk(document.body, "foo");

function walk(node, targetString) {
  var child;

  switch (node.nodeType) {
    case 1: // Element
      for (child = node.firstChild;
           child;
           child = child.nextSibling) {
        walk(child, targetString);
      }
      break;

    case 3: // Text node
      handleText(node, targetString);
      break;
  }
}

function handleText(node, targetString) {
  var start, targetNode, followingNode, wrapper;

  // Does the text contain our target string?
  // (This would be a regex test in your http://... case)
  start = node.nodeValue.indexOf(targetString);
  if (start >= 0) {
    // Split at the beginning of the match
    targetNode = node.splitText(start);

    // Split at the end of the match
    followingNode = targetNode.splitText(targetString.length);

    // Wrap the target in an element; in this case, we'll
    // use a `span` with a class, but you'd use an `a`.
    // First we create the wrapper and insert it in front
    // of the target text.
    wrapper = document.createElement('span');
    wrapper.className = "wrapper";
    targetNode.parentNode.insertBefore(wrapper, targetNode);

    // Now we move the target text inside it
    wrapper.appendChild(targetNode);

    // Clean up any empty nodes (in case the target text
    // was at the beginning or end of a text ndoe)
    if (node.nodeValue.length == 0) {
      node.parentNode.removeChild(node);
    }
    if (followingNode.nodeValue.length == 0) {
      followingNode.parentNode.removeChild(followingNode);
    }
  }
}

Живой пример


Обновление: выше не обрабатывается, если было несколько совпадений в одном текстовом узле (doh!). И, черт возьми, я сделал совпадение с регулярным выражением - вам придется настроить регулярное выражение и, возможно, выполнить некоторую постобработку для каждого совпадения, потому что то, что здесь, слишком упрощено. Но это начало:

// The regexp should have a capture group that
// will be the href. In our case below, we just
// make it the whole thing, but that's up to you.
// THIS REGEXP IS ALMOST CERTAINLY TOO SIMPLISTIC
// AND WILL NEED ADJUSTING (for instance: what if
// the link appears at the end of a sentence and
// it shouldn't include the ending puncutation?).
walk(document.body, /(http:\/\/[^ ]+)/i);

function walk(node, targetRe) {
  var child;

  switch (node.nodeType) {
    case 1: // Element
      for (child = node.firstChild;
           child;
           child = child.nextSibling) {
        walk(child, targetRe);
      }
      break;

    case 3: // Text node
      handleText(node, targetRe);
      break;
  }
}

function handleText(node, targetRe) {
  var match, targetNode, followingNode, wrapper;

  // Does the text contain our target string?
  // (This would be a regex test in your http://... case)
  match = targetRe.exec(node.nodeValue);
  if (match) {
    // Split at the beginning of the match
    targetNode = node.splitText(match.index);

    // Split at the end of the match.
    // match[0] is the full text that was matched.
    followingNode = targetNode.splitText(match[0].length);

    // Wrap the target in an `a` element.
    // First we create the wrapper and insert it in front
    // of the target text. We use the first capture group
    // as the `href`.
    wrapper = document.createElement('a');
    wrapper.href = match[1];
    targetNode.parentNode.insertBefore(wrapper, targetNode);

    // Now we move the target text inside it
    wrapper.appendChild(targetNode);

    // Clean up any empty nodes (in case the target text
    // was at the beginning or end of a text ndoe)
    if (node.nodeValue.length == 0) {
      node.parentNode.removeChild(node);
    }
    if (followingNode.nodeValue.length == 0) {
      followingNode.parentNode.removeChild(followingNode);
    }

    // Continue with the next match in the node, if any
    match = followingNode
      ? targetRe.exec(followingNode.nodeValue)
      : null;
  }
}

Живой пример

Я не практически, но вы можете попробовать это

$('a([href^="http://"])').each( function(){
        //perform your task
    })
Другие вопросы по тегам