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: /
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?
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/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?
Share Improve this question edited Jul 26, 2012 at 2:26 Rusty Fausak asked Jul 26, 2012 at 2:19 Rusty FausakRusty Fausak 7,5251 gold badge29 silver badges39 bronze badges 04 Answers
Reset to default 8Try 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>'));
});
Fiddle
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.
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/fr4AL/4/
Thanks to:
- Fabricio's answer
- How do I select text nodes with jQuery?
- http://api.jquery./wrap/
- http://api.jquery./contents/
- Converting HTML string into DOM elements?
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);
});
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();