Оберните теги <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
})