Javascript Regex - affect only non html code? (ie text) - javascript

I have a function that wraps text with a span
$(".ui-content").html(function (i, v)
{
return v.replace(/(CANADA)/gi, '<span class="query">$1</span>');
});
However when I pass in the term "div" or "span" or "a" which are html, the page gets messed up badly of course.
What can I change the regex to remove only text that is not part of a html code like <strong> but does work on A strong person is a helpful friend

Same concept, as #elclanrs, but with an imitation of negative look behind:
$(".ui-content").html(function (i, v) {
return v.replace(/([^</])(strong)/gi, ' <span class="query">$2</span> ');
});​
http://jsfiddle.net/39VMe/
Also a good article about how to treat certain limitations of JavaScript regexp: http://blog.stevenlevithan.com/archives/mimic-lookbehind-javascript

Node manipulations to the rescue, as always!
function toArray(obj) {
var r = [];
for(var i = 0; i < obj.length; i++) {
r.push(obj[i]);
}
return r;
}
$('.ui-content').each(function() {
var stack = [this];
var c, n;
while(c = stack.pop()) {
var childNodes = toArray(c.childNodes);
for(var i = 0; n = childNodes[i]; i++) {
if(n.nodeType === 1) {
stack.push(n);
} else if(n.nodeType === 3) {
var matches = n.nodeValue.split(/(CANADA)/i);
for(var i = 0; i < matches.length; i++) {
var newNode;
if(/CANADA/i.test(matches[i])) {
newNode = document.createElement('span');
newNode.className = 'query';
newNode.appendChild(document.createTextNode(matches[i]));
} else {
newNode = document.createTextNode(matches[i]);
}
n.parentNode.insertBefore(newNode, n);
}
n.parentNode.removeChild(n);
}
}
}
});
Here's a demo jsFiddle.

I know people usually insist in not using regex for parsing html but hey this seems to work just fine. This uses a negative lookahead to replace only the word "strong" and not actual <strong> tags:
/strong(?!>)/
Edit:
Use this regex which is more flexible and allows for attributes:
/strong(?!(>|\s\w+=))/g;
In any case, it all depends on your HTML. If regex don't do it then you'll have to parse properly.
Demo: http://jsfiddle.net/mGLjb/2/

Related

How can I write a replace method for String in JavaScript?

I heard, that string in JavaScript has immutability.
So, how can I write a method to replace some character in string?
What I want is:
String.prototype.replaceChar(char1, char2) {
for (var i = 0; i < this.length; i++) {
if (this[i] == char1) {
this[i] = char2;
}
}
return this;
}
Then, I can use it like this:
'abc'.replaceChar('a','b'); // bbc
I know it will not work, because the immutability of string.
But in native code, I can use the native replace method like this:
'abc'.replace(/a/g,'b');
I don't really know how to solve this problem.
You can use the following approach:
String.prototype.replaceAll = function(search, replacement) {
return this.replace(new RegExp(search, 'g'), replacement);
};
You can use array, too:
String.prototype.replaceChar = function (char1, char2) {
newstr=[];
for (i = 0; i < this.length; i++) {
newstr.push(this[i]);
if (newstr[i] == char1) {
newstr[i] = char2
}
}
return newstr.join("");
}
console.log('abca'.replaceChar('a','G'));
If you want a solution without regex (as a way to learn), you can use the following:
String.prototype.replaceChar = function(char1, char2) {
var s = this.toString();
for (var i = 0; i < s.length; i++) {
if (s[i] == char1) {
s = s.slice(0, i) + char2 + s.slice(i+1);
}
}
return s;
}
console.log('aaabaaa'.replaceChar('a', 'c'))
The idea is that you need this content of the string in a temp variable, then you need to go char-by-char, and if that char is the one you are looking for - you need to build your string again.

Pure Javascript - Find/Replace a list of words in DOM

I have a list of words and I want to replace them in DOM.
The replace is working but it has an issue. Before and after the replaced text, it puts an undesired slash bar (i.e. reduced to a brigade > reduced/ OTHER WORD 1 /a brigade)
Also, I need to wrap the replaced word with an html tag, like .
(i.e. reduced to a brigade > reduced OTHER WORD 1 a brigade)
You can see the code here:
https://jsfiddle.net/realgrillo/9uL6ofge/
var elements = document.getElementsByTagName('*');
var words = [
[/(^|\s)([Tt]o)(\s|$)/ig, 'OTHER WORD 1'],
[/(^|\s)([Aa]nd)(\s|$)/ig, 'OTHER WORD 2']
];
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
for (var j = 0; j < element.childNodes.length; j++) {
var node = element.childNodes[j];
if (node.nodeType === 3) {
var text = node.data;
for (var k = 0; k < words.length; k++)
{
var from = new RegExp(words[k][0]);
var to = new RegExp('$1' + words[k][1] + '$3');
var replacedText = text.replace(from, to);
//console.log("from: " + from);
//console.log("to: " + to);
//console.log("toDo: " + toDo);
if (replacedText !== text)
{
element.innerHTML = element.textContent.replace(from, to);
console.log("text: " + text);
console.log("replacedText: " + replacedText);
}
}
/*
//
//!!!!THIS CODE FOR 1 WORD WORKS!!!! I need to do this for the list of words.
//
var replacedText = text.replace(/(^|\s)([Tt]o)(\s|$)/ig, '$1OTHER WORD$3');
if (replacedText !== text) {
element.innerHTML = element.textContent.replace(/(^|\s)([Tt]o)(\s|$)/ig, '$1<b class=modified>OTHER WORD</b>$3');
}
*/
}
}
}
Masters of JS, can you help me? Pure javascript, not jQuery please.
Thanks.
I can give you some much more simple function to use. First define a function for replacing words like this:
String.prototype.replaceAll = function (search, replacement) {
try {
var target = this;
return target.split(search).join(replacement);
} catch (e) {
return "";
}
};
That's why the pure javascript replace function only replace one time in a whole case. After that you can use it in your code like here:
var replacedText = text.replaceAll(/(^|\s)([Tt]o)(\s|$)/ig, '$1OTHER WORD$3');
You don't need to parse words[k][0] into a RegExp as it is already one, even though it won't break because the constructor will handle a RegExp well.
Also for the words[k][1], you don't need to parse it into a RegExp either because the second parameter of String.prototype.replace is expected to be a plain string. The slashes are there because you cast your RegExp to a string (and will end up output).
So this is a change that will fix it:
var to = '$1' + words[k][1] + '$3';
And this is optional but probably a good practice:
var from = words[k][0];
For getting an html tag, you can either change it directly in each words[k][1] or in the to var declaration, depending on what you want.
Extra:
In the for loop, the code assumes that there are always at least three groups (because of the '$3' reference), an alternative could be to delegate this replacement directly in words[k][1], but it is up to you to decide.

Valid output in IE9 but undefined output in IE7/IE8

I wrote a simple 'replaceAll' function that extends String.prototype.
String.prototype.replaceAll = function (removalChar, insertionChar) {
var output = "";
for (var i = 0; i < this.length; i++) {
if(this[i] == removalChar) {
output += insertionChar;
}
else {
output += this[i];
}
}
return output;
}
Test code:
var test = "Hello-1-2-3";
alert(test.replaceAll("-"," "));
My test code alerts Hello 1 2 3 in all browsers including IE9.
But in IE7 and 8, the output I get is something like this: undefinedundefinedundefinedundefinedundefinedundefined...
jsFiddle: http://jsfiddle.net/cd4Z2/
(try this in IE7/IE8)
How could I possibly rewrite the function to ensure it works on IE7/8 without breaking its behaviour on other browsers?
You can't access string chars with this[i] in IE7/8. Use .charAt(i) instead, as explained here:
Javascript strings - getting the char at a certain point
Updated fiddle (tested in IE8): http://jsfiddle.net/cd4Z2/2/
I've just replaced this[i] with this.charAt(i).
In this question, some good reasons are stated on why you'd prefer to use charAt as opposed to string[index]. The latter is not part of ECMAScript 3.
IE<9 don't treat a string like an array, i.e. they lack ability to refer individual letter with index. You can use a temporary array (var temp = this.split('');) instead of this[i]
Try this out:-
String.prototype.replaceAll = function (removalChar, insertionChar) {
var output = "";
var res = this.split('');
for (var i = 0; i < this.length; i++) {
if(res[i] == removalChar) {
output += insertionChar;
}
else {
output += res[i];
}
}
return output;
}
var test = "Hello-1-2-3";
//alert(test.replace("-"," "));
alert(test.replaceAll("-"," "));

Multiple specials characters replacement optimization

I need to replace all the specials characters in a string with javascript or jQuery.
I am sure there is a better way to do this.
But I currently have no clue.
Anyone got an idea?
function Unaccent(str) {
var norm = new Array('À','Á','Â','Ã','Ä','Å','Æ','Ç','È','É','Ê','Ë','Ì','Í','Î','Ï', 'Ð','Ñ','Ò','Ó','Ô','Õ','Ö','Ø','Ù','Ú','Û','Ü','Ý','Þ','ß', 'à','á','â','ã','ä','å','æ','ç','è','é','ê','ë','ì','í','î','ï','ð','ñ', 'ò','ó','ô','õ','ö','ø','ù','ú','û','ü','ý','ý','þ','ÿ');
var spec = new Array('A','A','A','A','A','A','A','C','E','E','E','E','I','I','I','I', 'D','N','O','O','O','0','O','O','U','U','U','U','Y','b','s', 'a','a','a','a','a','a','a','c','e','e','e','e','i','i','i','i','d','n', 'o','o','o','o','o','o','u','u','u','u','y','y','b','y');
for (var i = 0; i < spec.length; i++) {
str = replaceAll(str, norm[i], spec[i]);
}
return str;
}
function replaceAll(str, search, repl) {
while (str.indexOf(search) != -1) {
str = str.replace(search, repl);
}
return str;
}
Here's a version using a lookup map that works a little more efficiently than nested loops:
function Unaccent(str) {
var map = Unaccent.map; // shortcut
var result = "", srcChar, replaceChar;
for (var i = 0, len = str.length; i < len; i++) {
srcChar = str.charAt(i);
// use hasOwnProperty so we never conflict with any
// methods/properties added to the Object prototype
if (map.hasOwnProperty(srcChar)) {
replaceChar = map[srcChar]
} else {
replaceChar = srcChar;
}
result += replaceChar;
}
return(result);
}
// assign this here so it is only created once
Unaccent.map = {'À':'A','Á':'A','Â':'A'}; // you fill in the rest of the map
Working demo: http://jsfiddle.net/jfriend00/rRpcy/
FYI, a Google search for "accent folding" returns many other implementations (many similar, but also some using regex).
Here's a bit higher performance version (2.5x faster) that can do a direct indexed lookup of the accented characters rather than having to do an object lookup:
function Unaccent(str) {
var result = "", code, lookup, replaceChar;
for (var i = 0, len = str.length; i < len; i++) {
replaceChar = str.charAt(i);
code = str.charCodeAt(i);
// see if code is in our map
if (code >= 192 && code <= 255) {
lookup = Unaccent.map.charAt(code - 192);
if (lookup !== ' ') {
replaceChar = lookup;
}
}
result += replaceChar;
}
return(result);
}
// covers chars from 192-255
// blank means no mapping for that char
Unaccent.map = "AAAAAAACEEEEIIIIDNOOOOO OUUUUY aaaaaaaceeeeiiiionooooo uuuuy y";
Working demo: http://jsfiddle.net/jfriend00/Jxr9u/
In this jsperf, the string lookup version (the 2nd example) is about 2.5x faster.
Using an object as a map is a good idea, but given the number of characters you're replacing, it's probably a good idea to pre-initialize the object so that it doesn't have to be re-initialized each time the function gets run (assuming you're running the function more than once):
var Unaccent = (function () {
var charMap = {'À':'A','Á':'A','Â':'A','Ã':'A','Ä':'A' /** etc. **/};
return function (str) {
var i, modified = "", cur;
for(i = 0; i < str.length; i++) {
cur = str.charAt(i);
modified += (charMap[cur] || cur);
}
return modified;
};
}());
This will front-load the heavy lifting of the function to page load time (you can do some modifications to delay it until the first call to the function if you like). But it will take some of the processing time out of the actual function call.
It's possible some browsers will actually optimize this part anyway, so you might not see a benefit. But on older browsers (where performance is of greater concern), you'll probably see some benefit to pre-processing your character map.
You can prepare key value pair type of array and via jquery each traverse that array.
Example :
function Unaccent(str) {
var replaceString = {'À':'A','Á':'A','Â':'A'}; // add more
$.each(replaceString, function(k, v) {
var regX = new RegExp(k, 'g');
str = str.replace(regX,v);
});
}
Working Demo
Good Luck !!

How to wrap each symbol inside tag with jQuery?

I have a counter with dinamic number, to style it as I want I need to wrap each number in tag.
My code:
<div class="test">12345</div>
I need:
<div class="test"><span>1</span><span>2</span><span>3</span><span>4</span><span>5</span></div>
Is it any easy solution with jQuery to do that?
Quick and dirty way:
var characters = $("div").text().split("");
$("div").empty();
$.each(characters, function(i, el) {
$("div").append("<span>"+el+"</span");
});
http://jsfiddle.net/h6mJh/1/
var text = $('.text').text();
for(var i = 0; i < text.length; i++)
{
$('.text').append('<span>' + text[i] + '</span>);
}
This will only work for number 0-9. You won't be able to handle double digits or greater if you have them side by side with no delimiters like that. Not sure if there was a potential for such a case.
var s = $(".test").html();
var result = "";
for (var i = 0; i < s.length; i++) {
result += "<" + "span>" + s[i] + "</" + "span>";
}
$(".test").html(result);
I separated the <span> tags because I'm not sure javascript allows them...
James Montagne's answer but a bit nicer.
var chars = $("div").text().split("");
$("div").empty();
$.each(chars, function (i, el) {
var span = document.createElement('span');
span.innerText = chars[i];
$("div").append(span);
});

Categories

Resources