javascript in-page search and replace - javascript

I'm not guru of javascript, so don't blame me much.
My task is to find and replace text inside html page, so every number should be replaced with it's value multiplied by some amount.
e.g. 123 -> 104.55(123*0.85), 245 -> 208,25(245*0.85) etc.
Currently my code is like following:
$('body').html(
$('body').html().replace(/([0-9\.]+)/g, eval("$1*0.85"))
);
eval("$1*0.85") return NaN while trying parseFloat($1)*0.85 also does not return correct results
Can someone advise?
Thanks.

You probably don't want to take the whole page's HTML and replace it with updated HTML, because all of the elements get torn down and recreated, which has implications for event handlers and the like.
It happens that just yesterday I answered this other question here. The answer can be applied quite easily to your situation.
Update: Since you're not moving the result into a new element, the code actually gets simpler:
walk(document.body);
function walk(node) {
var child;
switch (node.nodeType) {
case 1: // Element
for (child = node.firstChild;
child;
child = child.nextSibling) {
walk(child);
}
break;
case 3: // Text node
node.nodeValue = node.nodeValue.replace(
/[0-9]+(?:\.[0-9]+){0,1}/g,
function($0) {
msgs.push($0);
var num = parseFloat($0, 10);
if (isNaN(num)) {
return $0;
}
return num * 0.85;
}
);
break;
}
}
Live example
Note that I've modified the regexp a bit, you might want to massage it until it's how you want it. But I wanted to match decimal points only when followed by digits. (It's worth noting that not all cultures use . as the decimal point, but you seemed to in your example, so...)

it should be sth like this,
var x = $('body').html();
x = x.replace(/([0-9.]+)/g, function ( x ){ return x*0.85;});
$('body').html(x);
if you do not consider events, etc

Related

Javascript Case: "look at" + item

Okay, so this may be a repeat, but I personally haven't seen anything on the internet or in Stackoverflow about this.
I am working on a game project and I have been trying to make a text-based game.
In this game, I have a switch statement, for when the user enters a command.
So far I have things for Inventory and Look (Look around the environment), but how do I work with specific things in a switch statement?
For example:
submit = function(input) {
switch(input) {
case "LOOK":
lookaround();
break;
case "LOOK AT" + item:
look();
}
}
It is the LOOK AT line I am having issues with. I do not know how I can make a string work in that format, unless I had a case for every single item individually, example case "LOOK AT ORANGE" or case "LOOK AT TREE".
I hope I am explaining this thoroughly enough. Can anyone give me some advice?
Thanks
EDIT
I think it is important to note that the user is typing the input into an input box, so the value of the input is going to be a string.
If it will help to see the code I have made, please let me know in the comments below.
EDIT
THANKS FOR YOU HELP GUYS!
I used a regular expression (Thanks #red-devil) and a mixture of slicing. It works perfectly now!
Switch works with constants, not expressions like 'LOOK AT' + anything.
You could define an object for map any of your cases to your own functions. Like that:
var looks = {
'lookat-something' : function() {
alert('something');
},
'lookat-other-thing' : function() {
alert('other thing');
},
};
var x = 'lookat-other-thing';
looks[x]();
It much more flexible than using switch in any way.
If I understood you right, you want the user to be able to input LOOK AT and then any item name. The problem here is that you have this ominous item variable that could stand for anything and this is not going to work.
I would suggest one of these two ways:
Going along the lines of your example:
submit = function (input) {
switch (true) {
case input == "LOOK":
alert("Look")
break;
case input.startsWith("LOOK AT"):
alert(input)
break;
}
}
if (typeof String.prototype.startsWith != 'function') {
String.prototype.startsWith = function (str) {
return this.slice(0, str.length) == str;
};
}
And secondly, and this is the method I would recommend, you implement a way to parse any input into a command and parameters. A way to do this is to split the input at every space character and then the first value is the command and the rest would be the parameters. This would require you to use a one word command like LookAt and not LOOK AT.
So something like this:
function submit(input) {
var parts = input.split(" ");
var cmd = parts[0];
var args = parts.slice(1);
switch (cmd) {
case "Look":
lookAround();
break;
case "LookAt":
lookAt(args[0]);
break;
}
}

better way to write if else statement

First time writing Javascript. I just would like know if there is a shorter way of writing this:
<p id="demo"></p>
<script>
function myFunction() {
var letter = document.getElementById("myInput").value;
var text;
if (letter === "5544") {
text = "Abar, Marlon 1,800";
} else if (letter === "5545") {
text = "Pia, Darla 1,800";
} else if (letter === "5546") {
text = "Salazar, Alex 1,500";
//etc...
} else {
text = "Incorrect Account Number";
}
document.getElementById("demo").innerHTML = text;
}
</script>
Tried map but I couldn't get it to work.
There isn't really a shorter way to write an if statement in that way (which I will assume is what you're asking). However, there could be a few different ways to write this depending on how many things you want to check.
Use a Switch statement
There is a cleaner way when dealing with multiple cases that letter could be.
This would be a switch statement and it would look like this:
var text;
switch (letter) {
case "5544":
text = "Abar, Marlon 1,800";
break;
case "5545":
text = "Pia, Darla 1,800";
break;
// more cases
default:
text = "Incorrect Account Number";
break;
}
This reads a little better than an if else statement in some cases. The default keyword here acts as your else clause in an if else statement. The case acts as your different if statements if you will.
Essentially, the switch statement above will fall through each of the cases it defines until it finds a case that matches letter (such as "5544"). If none matches, it hits the default case. The break keyword at the end of each case stops things from falling through to the next defined case once a match is found.
This method could get cumbersome with more than 6 or 7 cases.
Create an object and look up the value
Now, a shorter way to get the value you want could be to define an object and get the value based on what has been entered like so:
var letter = document.getElementById('selector').value;
var obj = {
'5544': 'Abar, Marlon 1,800'
};
if (letter in obj) {
// do something if found
}
else {
// do something if not found
}
This could be an easy way to get a value if you have many values to check.
Other thoughts
As a side note to all of this, there are short hand if statements called ternary statements which you can find here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator ... However, I would not recommend nesting these as it becomes very complicated and not very readable.
Conclusion
So, to reiterate the answer to your question: No, there isn't really a shorter way to write an if else statement with many values. You can use a switch statement to make it cleaner. Use the object lookup method if you have many values you would like to check.
JavaScript has object (map) literals. Use them for terse code. In your final application you'll get the data for the map from someplace else and not code it directly into your website, but if you did, it would look like this:
document.getElementById( "demo" ).innerHTML = {
"5544" : "Abar, Marlon 1,800",
"5445" : "Pia, Darla 1,800",
...
}[ document.getElementById( "myInput" ).value ];
you can use switch for a long if - else -if ladder:
switch(expression) {
case n:
code block
break;
case n:
code block
break;
default:
default code block
}
This is how it works:
1)The switch expression is evaluated once.
2)The value of the expression is compared with the values of each case.
3)If there is a match, the associated block of code is executed.
if you need basic tutorials in java script then you should try w3 schools.

Apparent trouble with behavior of substring in JavaScript

I am writing a recursive algorithm to build a finite state automaton by parsing a regular expression. The automaton iterates through the expression, pushing characters to a stack and operators to an "operator stack." When I encounter "(" (indicating a grouping operation), I push a "sub automaton" to the stack and pass the rest of the pattern to the sub automaton to parse. When that automaton encounters ")", it passes the rest of the string up to the parent automaton to finish parsing. Here is the code:
var NFA = function(par) {
this.stack = [];
this.op_stack = [];
this.parent = par;
};
NFA.prototype.parse = function(pattern) {
var done = false;
for(var i in pattern) {
if (done === true) {
break;
}
switch(pattern.charAt(i)) {
case "(":
var sub_nfa = new NFA(this);
this.stack.push(sub_nfa);
sub_nfa.parse(pattern.substring(i+1, pattern.length));
done = true;
break;
case ")":
if (this.parent !== null) {
var len = pattern.length;
/*TROUBLE SPOT*/
this.parent.parse(pattern.substring(i, pattern.length));
done = true;
break;
}
case "*":
this.op_stack.push(operator.KLEENE);
break;
case "|":
this.op_stack.push(operator.UNION);
break;
default:
if(this.stack.length > 0) {
//only push concat after we see at least one symbol
this.op_stack.push(operator.CONCAT);
}
this.stack.push(pattern.charAt(i));
}
}
};
Note the area marked "TROUBLE SPOT". Given the regular expression "(a|b)a", the call this.parent.parse, is called exactly once: when the sub-automaton encounters ")". At this point, pattern.substring(i, pattern.length) = ")a". This "works", but it isn't correct because I need to consume the ")" input before I pass the string to the parent automaton. However, if I change the call to this.parent.parse(pattern.substring(i+1, pattern.length), parse gets handed the empty string! I have tried stepping through the code and I cannot explain this behavior. What am I missing?
At Juan's suggestion, I made a quick jsfiddle to show the problem when trying to parse "(a|b)a" with this algorithm. In the ")" case, it populates an empty div with the substring at the i index and the substring at the i+1 index. It shows that while there are 2 characters in the substring at i, the substring at i+1 is the empty string! Here's the link: http://jsfiddle.net/XC6QM/1/
EDIT: I edited this question to reflect the fact that using charAt(i) doesn't change the behavior of the algorithm.
I think the previous answer was on the right track. But there also looks to me to be an off-by-one error. Shouldn't you be increasing the index for your substring? You don't want to include the ")" in the parent parse, right?
this.parent.parse(pattern.substring(i + 1, pattern.length));
But this will still fail because of the error Juan mentioned. A quick temporary fix to test this would be to convert the i to a number:
this.parent.parse(pattern.substring(+i + 1, pattern.length));
This might do it for you. But you should probably go back and switch away from the for-in loop on the string. I think that's causing your issue. Turn it into an array with str.split('') and then use an integer to loop. That will prevent further such issues.
The real problem is the fact that you were using a for in to iterate through the characters of the string. With the for in loop, your i is going to be a string, therefore, when you try to do i+1, it does string concatenation.
If you change your iteration to be
for(var i=0; i < pattern.length; i++) {
Then it all works fine http://jsfiddle.net/XC6QM/2/
Scott's answer correctly identified the problem but I think his solution (converting the indexes to numbers) is not ideal. You're better off looping with a numeric index to begin with
Also, you should not use brackets to access characters within a string, that does not work in IE 7
switch(pattern[i]) {
should be
switch(pattern.charAt(i)) {

How to search DOM to count the number of $ symbol found on a product page?

I am looking to find the best possible way to find how many $ symbols are on a page. Is there a better method than reading document.body.innerHTML and calc how many $-as are on that?
Your question can be split into two parts:
How can we get the the webpage text content without HTML tags?
We can generalize the second question a bit.
How can we find the number of string occurrences in another string?
And the 'best possible way to do this':
Amaan got the idea right of finding the text, but lets take it further.
var text = document.body.innerText || document.body.textContent;
Adding textContent to the code helps us cover more browsers, since innerText is not supported by all of them.
The second part is a bit trickier. It all depends on the number of '$' symbol occurrences on the page.
For example, if we know for sure, that there is at least one occurrence of the symbol on the page we would use this code:
text.match(/\$/g).length;
Which performs a global regular expression match on the given string and counts the length of the returned array. It's pretty fast and concise.
On the other hand, if we're not sure if the symbol appears on the page at least once, we should modify the code to look like this:
if (match = text.match(/\$/g)) {
match.length;
}
This just checks the value returned by the match function and if it's null, does nothing.
I would recommend using the third option only when there is a large occurrence of the symbols in the page or you're going to perform the search many many times. This is a custom function (taken from here) to count the occurrence of the specified string in another string. It performs better than the other two, but is longer and harder to understand.
var occurrences = function(string, subString, allowOverlapping) {
string += "";
subString += "";
if (subString.length <= 0) return string.length + 1;
var n = 0,
pos = 0;
var step = (allowOverlapping) ? (1) : (subString.length);
while (true) {
pos = string.indexOf(subString, pos);
if (pos >= 0) {
n++;
pos += step;
} else break;
}
return (n);
};
occurrences(text, '$');
I'm also including a little jsfiddle 'benchmark' so you can compare these three different approaches yourself.
Also: No, there isn't a better way of doing this than just getting the body text and counting how many '$' symbols there are.
You should probably use document.body.innerText or document.body.textContent to avoid getting your HTML give you false positives.
Something like this should work:
document.body.innerText.match(/\$/g).length;
An alternate way I can think of, would be to use window.find like this:
var len = 0;
while(window.find('$') === true){
len++;
}
(This may be unreliable because it depends on where the user clicked last. It will work fine if you do it onload, before any user interaction.)

Select numbers on a page with jQuery or Javascript

I'm just wondering if there's a way to locate numbers on a page with jQuery or plain Javascript.
Here's what I want to do:
Say "June 23" is on the page. What I want to do is be able to prepend and append some <span> selectors to the number.
Using :contains() with jQuery selects the whole thing, not just the number.
These strings are being generated without any wrapping elements by a Wordpress theme I'm working on, and I only want to select the number.
Any help would be appreciated! Thanks for even thinking about it.
-George
You can walk through all the elements, looking at text nodes, and replacing them with updated content that has the number wrapped.
var regex = /(\d+)/,
replacement = '<span>$1</span>';
function replaceText(el) {
if (el.nodeType === 3) {
if (regex.test(el.data)) {
var temp_div = document.createElement('div');
temp_div.innerHTML = el.data.replace(regex, replacement);
var nodes = temp_div.childNodes;
while (nodes[0]) {
el.parentNode.insertBefore(nodes[0],el);
}
el.parentNode.removeChild(el);
}
} else if (el.nodeType === 1) {
for (var i = 0; i < el.childNodes.length; i++) {
replaceText(el.childNodes[i]);
}
}
}
replaceText(document.body);
Example: http://jsfiddle.net/JVsM4/
This doesn't do any damage to existing elements, and their associated jQuery data.
EDIT: You could shorten it a bit with a little jQuery:
var regex = /(\d+)/g,
replacement = '<span>$1</span>';
function replaceText(i,el) {
if (el.nodeType === 3) {
if (regex.test(el.data)) {
$(el).replaceWith(el.data.replace(regex, replacement));
}
} else {
$(el).contents().each( replaceText );
}
}
$('body').each( replaceText );
Example: http://jsfiddle.net/JVsM4/1/
Note that the regex requires the g global modifier.
Probably a little slower this way, so if the DOM is quite large, I'd use the non-jQuery version.
Just thinking out loud, but do you reckon this would work?
document.body.innerHTML = document.body.innerHTML.replace(/(\d+)/g, "<span class='number'>$1</span>")
It is fully dependent on what format your date is.
I found this website with a lot of different regular expressions (because you are just searching a normal piece of text for a date).
This seems a good option if this is your format for your date (dd MMM yyyy): http://regexlib.com/REDetails.aspx?regexp_id=405
I assume, because it is a template, that your dates will be the same for all pages. So the format will be the same as well. You can use the regular expression on every piece of text on your template if you define it well.
You can also select decimal numbers that contain comma as thousands separators:
let regex = /([,\d]*\.?\d+)/g;
This will match 1234 and 1,234 and 1234.5678 and 1,234.5678 and 0.5678 and .5678.
Refer to the above answer for full solution.

Categories