Reputation: 7525
Suppose I have an HTML page that looks something like this:
<html><body>
00123
<input value="00123">
00456
</body></html>
And I want to use javascript/jQuery to make it look like this:
<html><body>
<a href="#00123">00123</a>
<input value="00123">
<a href="#00456">00456</a>
</body></html>
Essentially I want to wrap regular expression matched plain strings in the document with HTML anchor tags. In this example, I want to do something like:
$('body').html($('body').html().replace(/(00\d+)/, '<a href="#$1">$1</a>'));
See the jsFiddle with this example: http://jsfiddle.net/NATnr/2/
The problem with this solution is that the text inside the input
element is matched and replaced.
Does anyone know how to only match and replace plain text in a document in this manner using javascript/jQuery?
Upvotes: 5
Views: 4563
Reputation: 7525
The final solution ended up looking like this:
jQuery.fn.linker = function () {
$(this).contents()
.filter(function() { return this.nodeType != Node.TEXT_NODE; })
.each(function () { $(this).linker(); });
$(this).contents()
.filter(function() { return this.nodeType == Node.TEXT_NODE; })
.each(function () {
$(this).replaceWith(
$(this).text().replace(/(00\d+)/g, '<a href="#$1">$1</a>')
);
});
}
$(document).ready(function () {
$('body').linker();
});
See the jsFiddle at work here: http://jsfiddle.net/fr4AL/4/
Thanks to:
Upvotes: 6
Reputation: 70159
Try filtering the body
's contents()
by nodeType to get only the Text Nodes, then replace them with jQuery-generated anchor elements (any extra text in these nodes will be kept as Text Node):
$('body').contents().filter(function() {
return this.nodeType === 3;
}).each(function() {
$(this).replaceWith($(this).text().replace(/(00\d+)/g, '<a href="#$1">$1</a>'));
});
As you know, most often it's not a good idea to parse HTML with Regex (look out for the ponies, they are evil), but if you isolate a part of the HTML you want to parse and it follows a relatively simple pattern, it is a viable option.
edit: Included the g
flag (global modifier) in your Regex to allow for matching multiple anchors inside a single Text Node.
Upvotes: 8
Reputation: 87
Give this a whirl.... Much cleaner!! ;)
$('input').each(function() {
var temp;
temp = $(this).val();
$(this).before('<a href="#' + temp +'">' +temp+ '</a>');
});
$('body').contents().filter(function() {return this.nodeType == 3;}).remove();
Upvotes: 0
Reputation: 25455
This from a related answer to a question by bobince:
You're right to not want to be processing HTML with regex. It's also bad news to be assigning huge chunks of .html(); apart from the performance drawbacks of serialising and reparsing a large amount of HTML, you'll also lose unserialisable data like event listeners, form data and JS properties/references.
Here's a plain JavaScript/DOM one that allows a RegExp pattern to match. jQuery doesn't really give you much help here since selectors can only select elements, and the ‘:contains’ selector is recursive so not too useful to us.
// Find text in descendents of an element, in reverse document order
// pattern must be a regexp with global flag
//
function findText(element, pattern, callback) {
for (var childi= element.childNodes.length; childi-->0;) {
var child= element.childNodes[childi];
if (child.nodeType==1) {
findText(child, pattern, callback);
} else if (child.nodeType==3) {
var matches= [];
var match;
while (match= pattern.exec(child.data))
matches.push(match);
for (var i= matches.length; i-->0;)
callback.call(window, child, matches[i]);
}
}
}
findText(document.body, /\bBuyNow\b/g, function(node, match) {
var span= document.createElement('span');
span.className= 'highlight';
node.splitText(match.index+6);
span.appendChild(node.splitText(match.index+3));
node.parentNode.insertBefore(span, node.nextSibling);
});
Upvotes: 0