I'm looking to replace text in a webpage (any webpage I want to run it on) using JavaScript. I'm not an expert in JavaScript, so I am sort of lost. If I can help it I would like to avoid jQuery.
Through Google, I've found this stackoverflow question. But when I inject document.body.innerHTML = document.body.innerHTML.replace('hello', 'hi'); into a webpage it sort of messes the page up. It seems to make the page revert to basic text and formatting.
Also, I'm wondering if the regex code from here, could be used. Again, I really am not sure how to use it. What it would do is replace only webpage text - not links or filenames.
I'm using Google Chrome incase that matters.
You could perform your repleacements on all the just the text nodes in the DOM:
function replaceTextOnPage(from, to){
getAllTextNodes().forEach(function(node){
node.nodeValue = node.nodeValue.replace(new RegExp(quote(from), 'g'), to);
});
function getAllTextNodes(){
var result = [];
(function scanSubTree(node){
if(node.childNodes.length)
for(var i = 0; i < node.childNodes.length; i++)
scanSubTree(node.childNodes[i]);
else if(node.nodeType == Node.TEXT_NODE)
result.push(node);
})(document);
return result;
}
function quote(str){
return (str+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
}
}
Quote function borrowed from this answer.
Usage:
replaceTextOnPage('hello', 'hi');
Note that you will need to SHIM forEach in older browsers or replace that code with a loop like so:
var nodes = getAllTextNodes();
for(var i = 0; i < nodes.length; i++){
nodes[i].nodeValue = nodes[i].nodeValue.replace(new RegExp(quote(from), 'g'), to);
}
Recently, I had to exercise a similar problem, and I came up with something similar to this:
<!DOCTYPE html>
<html>
<head>
<title>HTML JS REPLACE</title>
<script type="text/javascript">
function convert(elem) {
var content = document.getElementById(elem).innerHTML; // get HTML content for the given element
var pattern = new RegExp(/hello/gi);
content = content.replace(pattern,'hi');
document.getElementById(elem).innerHTML = content; // put the replace content back
}
</script>
</head>
<body>
<div id="content">
Some text that includes both hello and hi. And a hello.
</div>
<script type="text/javascript">
window.onload = convert('content');
</script>
</body>
</html>
The result will be that you will get a page saying this:
Some text that includes both hi and hi. And a hi.
while the original source still says:
Some text that includes both hello and hi. And a hello.
The tricky bits are really just a few - first, you want the window.onload trigger to be included at the bottom of body, so the DOM loads fully before running any JS on it.
Second, you must have a top-level block element that you assign a unique ID which you can reference from JS.
Third, the convert function uses a regular expression, which executes a global case-insensitive replace of the string "hello" by changing it to "hi".
Your specific application may require to instead capture all of the occurences and then parse them in a loop, which may (or may not) cause some performance issues. Be careful :)
Related
By using JavaScript or jQuery, I am trying to figure out if a word, say "casablanca", is available on the page?
The page I am working on makes an ajax call which changes the content of the page. So I am supposed to search for the word "casablanca" after the ajax call.
The best way I have found so far is:
window.find('casablanca');
But this code highlights the word, which is something I don't want.
Is there a better way?
Thank you!
Here's a quick and programmatic way to determine whether the current HTML page contains a specific keyword:
function pageContains(str) {
return document.body.innerHTML.toString().indexOf(str) > -1;
}
JSFiddle: https://jsfiddle.net/shj4LwqL/
Please be aware that the above code searches for the keyword in the page's body tag, so if you have your JavaScript in the page body, and your keyword is written as a string in your JavaScript, then you will get a false positive. If you want to search within a specific element instead, you can use the following variant:
function elementContains(el, str) {
return el.innerHTML.toString().indexOf(str) > -1;
}
searches for only in content, not in tags and so on:
function searchInPage(val) {
var allElements = document.getElementsByTagName('*');
for(var i = 0; i < allElements.length; i++) {
var node = allElements[i].firstChild;
//Is it a text node?
if(node !== null && node.nodeType === 3) {
if(node.nodeValue == val) return true;
}
}
return false;
}
untested it shows you the way to do it.
Try utilizing .contents() , .is() to return Boolean ; substitute .filter() for .is() to return element containing selected text , if true , false if node not found having selected text
var res = $("body").contents().is(function(i, el) {
return $(":contains(casablanca)").is(el)
}); console.log(res)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="post-text" itemprop="text">
<p>By using JavaScript or jQuery, I am trying to figure out if a word, say "casablanca", is available on the page?</p>
<p>The page I am working on makes an ajax call which changes the content of the page. So I am supposed to search for the word "casablanca" after the ajax call.</p>
<p>The best way I have found so far is:</p>
<pre class="default prettyprint prettyprinted"><code><span class="pln">window</span><span class="pun">.</span><span class="pln">find</span><span class="pun">(</span><span class="str">'casablanca'</span><span class="pun">);</span></code></pre>
<p>But this code highlights the word, which is something I don't want.</p>
<p>Is there a better way?</p>
<p>Thank you!</p>
</div>
<!DOCTYPE HTML>
<html>
<head>
<title> Javascript - stuff </title>
<script type="text/javascript">
<!--
function GetCountsAll( Wordcount, Sentancecount, Clausecount, Charactercount )
{
var TextString = document.getElementById("Text").innerHTML;
var Wordcount = 0;
var Sentancecount = 0;
var Clausecount = 0;
var Charactercount = 0;
// For loop that runs through all characters incrementing the variable(s) value each iteration
for (i=0; i < TextString.length; i++);
if (TextString.charAt(i) == " " = true)
Wordcount++;
return Wordcount;
if (TextString.charAt(i) = "." = true)
Sentancecount++;
Clausecount++;
return Sentancecount;
if (TextString.charAt(i) = ";" = true)
Clausecount++;
return Clausecount;
}
-->
</script>
</head>
<body>
<div id="Text">
It is important to remember that XHTML is a markup language; it is not a programming language. The language only describes the placement and visual appearance of elements arranged on a page; it does not permit users to manipulate these elements to change their placement or appearance, or to perform any "processing" on the text or graphics to change their content in response to user needs. For many Web pages this lack of processing capability is not a great drawback; the pages are simply displays of static, unchanging, information for which no manipulation by the user is required. Still, there are cases where the ability to respond to user actions and the availability of processing methods can be a great asset. This is where JavaScript enters the picture.
</div>
<input type = "button" value = "Get Counts" class = "btnstyle" onclick = "GetCountsAll()"/>
<br/>
<span id= "Charactercount"> </span> Characters <br/>
<span id= "Wordcount"> </span> Words <br/>
<span id= "Sentancecount"> </span> Sentences <br/>
<span id= "ClauseCount"> </span> Clauses <br/>
</body>
</html>
I am a student and still learning JavaScript, so excuse any horrible mistakes. The script is meant to calculate the number of characters, words, sentences, and clauses in the passage. It's, plainly put, just not working. I have tried a multitude of things to get it to work for me and have gotten a plethora of different errors but no matter what I can NOT get this to work. Please help! (btw i know i misspelled sentence)
Remove the semicolon from for (i=0; i < TextString.length; i++);. This breaks out of the loop.
Put brackets {} around
Sentancecount++;
Clausecount++; so they are incremented each time the full stop is seen. Currently only Sentance is incremented each time. Clause is incremented at the end of the text.
I would also use brackets generally after ifs. It makes for readability and you can see what the code is doing if you can read it easily.
Next, you can only have want one return from the method. Have the first method call the secondary ones if that makes sense. Set it so you get some variables being given values, and then print them out.
hth
You could use the split method of the string. Like this:
WordCount = TextString.split(' ').length;
Sentancecount = TextString.split('.').length;
Clausecount = TextString.split(';').length;
Charactercount = TextString.length;
This way, you don't need the for loop anymore.
Well, you have several issues here.
First, you want your for loops to have braces around the code you want to execute on each iteration.
for (i=0; i < TextString.length; i++) {
if (TextString.charAt(i) == " " = true)
Wordcount++;
}
(I'm assuming WordCount is supposed to be outside the loop (but it's incorrect either way))
Next, you're not handling those return values.
The way return works is that you can set a variable to the result of the function.
For example:
function x() {
return 2;
}
var i = 1;
var q = i + x(); // Add the result of the function to i
alert(q); // This will display 3
So by calling return, you're sending that value back to where the function was called, but you're never using it.
Also, calling return immediately exits the function. You do not want this if you want to continue processing. I suggest that you create variables for each of the three values and store the results to that. Then after your processing in the script, you can the innerHTML of a div to the them.
Here's an example that may help you:
<html>
<head>
<title>Example Javascript</title>
<script type="text/javascript">
function myFunction()
{
var text = Array(8).join(parseInt('')) + ' Batman!';
var element = document.getElementById("result");
element.innerHTML = text;
}
</script>
</head>
<body>
The output from our function is: <div id="result"> </div>
<br />
<button type="button" onclick="myFunction()">Run</button>
</body>
</html>
I would like to note that there are some really simple ways to solve this, but this seems like homework or at least a learning experience which is why I'm setting you on the right path rather than just giving you a straight answer. You're close! My advice is to just keep at it!
I recently learned in the chat section that if you use a bookmark you can render LaTeX:
http://meta.math.stackexchange.com/a/3297
The stackexchange sites all render code like this. Anything in between `` gets rendered as code.
I find this feature quite nice and useful. I was wondering if anyone knows how to get the javascript that does this in the stackexchange sites and put it as a bookmark just like the mathjax bookmarks
found here:
http://www.math.ucla.edu/~robjohn/math/mathjax.html
That way if I write something with the backtick escapes say in facebook or any other site I could just click on my bookmark that says renderCode and the javascript will do what the same as it is doing in this site.
Anyone know if this has been asked before and/or how to achieve this? Thanks
Here's a start for you:
var replacer = function(s, match) {return "<code>" + match + "</code>";};
"some `delimited` code `sections` here". replace(/`([^`]*)`/g, replacer);
// ==> "some <code>delimited</code> code <code>sections</code> here"
You can use whatever markup you like in place of "< code >" and "< /code >" to create the effect you like.
You could also use a function like this one:
processTextNodes = function(element, fn) {
var children = element.childNodes;
for (var i = 0; i < children.length; i++) {
var node = children[i];
var type = node.nodeType;
if (type == 1) processTextNodes(node, fn);
if (type == 3) fn.call(this, node);
}
}
Like this:
processTextNodes(someElement, function(node) {
node.value = node.value.replace(/`([^`]*)`/g, replacer);
});
In order to apply this to all the text nodes inside an element.
You would still have to turn this into a bookmark, and figure out how to find the right element. And you'll need the markup and CSS to display the output as you like it. But this might be a good start.
I have some data in a sql table. I send it via JSON to my JavaScript.
From there I need to compose it into HTML for display to the user by 1 of 2 ways.
By composing the html string and inserting into .innerHTML property of the holding element
By using createElment() for each element I need and appending into the DOM directly
Neither of the questions below gives a quantifiable answer.
From first answer in first link, 3rd Reason ( first two reasons stated don't apply to my environment )
Could be faster in some cases
Can someone establish a base case of when createElement() method is faster and why?
That way people could make an educated guess of which to use, given their environment.
In my case I don't have concerns for preserving existing DOM structure or Event Listeners. Just efficiency ( speed ).
I am not using a library regarding the second link I provided. But it is there for those who may.
Research / Links
Advantages of createElement over innerHTML?
JavaScript: Is it better to use innerHTML or (lots of) createElement calls to add a complex div structure?
Adding to the DOM n times takes n times more time than adding to the DOM a single time. (:P)
This is the logic I'm personally following.
In consequence, when it is about to create, for instance, a SELECT element, and add to it several options, I prefer to add up all options at once using innerHTML than using a createElement call n times.
This is a bit the same as to compare BATCH operation to "one to one"... whenever you can factorise, you should!
EDIT: Reading the comments I understand that there's a feature (DOM DocumentFragment) that allow us saving such overhead and at the same time taking advantage of the DOM encapsulation. In this case, if the performances are really comparable, I would definitely not doubt and chose the DOM option.
I thought I read somewhere that the createElement and appendElement is faster. It makes sense, considering document.write() and innerHTML have to parse a string, and create and append the elements too. I wrote a quick test to confirm this:
<html>
<body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
function inner() {
var test = '';
for (var i=0; i<10000; i++) {
test += '<p>bogus link with some other <strong>stuff</strong></p>';
}
console.time('innerHTML');
document.getElementById('test').innerHTML = test;
console.timeEnd('innerHTML');
}
function jq() {
var test = '';
for (var i=0; i<10000; i++) {
test += '<p>bogus link with some other <strong>stuff</strong></p>';
}
console.time('jquery');
jQuery('#test').html(test);
console.timeEnd('jquery');
}
function createEl() {
var dest = document.getElementById('test');
console.time('createel');
//dest.innerHTML = '';//Not IE though?
var repl = document.createElement('div');
repl.setAttribute('id','test');
for (var i=0; i<10000; i++) {
var p = document.createElement('p');
var a = document.createElement('a');
a.setAttribute('href','../'); a.setAttribute('target','_blank');
a.appendChild(document.createTextNode("bogus link"));
p.appendChild(a);
p.appendChild(document.createTextNode(" with some other "));
var bold = document.createElement('strong');
bold.appendChild(document.createTextNode("stuff"));
p.appendChild(bold);
repl.appendChild(p);
}
dest.parentNode.replaceChild(repl,dest);
console.log('create-element:');
console.timeEnd('createel');
}
</script>
<button onclick="inner()">innerhtml</button>
<button onclick="jq()">jquery html</button>
<button onclick="createEl()">Create-elements</button>
<div id="test">To replace</div>
</body>
</html>
In this example, the createElement - appendChild method of writing out HTML works significantly faster than innerHTML/jQuery!
Greetings!
Is it possible to convert an HTML string to an array or JSON using Javascript?
Something like this:
var stringweb = '<html><head>hi</head><body>my body</body></html>';
And as result, I can have this:
var myarray = {[html,
[head,
[hi]
]
[etc...]
]}
Thanks in advance! :)
As you can tell from the comments above, this doesn't seem like the most robust idea... Anyhow, here is a solution that I think gets you what you asked for. It was fun to write, anyhow.
function htmlStringToArray(str) {
var temp = document.createElement('iframe');
temp.style.display = "none";
document.body.appendChild(temp);
var doc = temp.contentWindow.document;
doc.open();
doc.write(str);
doc.close();
var array = htmlNodeToArray(doc.documentElement);
temp.parentNode.removeChild(temp);
return array;
}
function htmlNodeToArray(node) {
if (node.nodeType == 1) {
var array = [node.tagName];
if (node.childNodes.length) {
for (var i=0, child; child = node.childNodes[i]; i++) {
if (child.nodeType == 1 || child.nodeType == 3) {
array.push(htmlNodeToArray(child));
}
}
} else if (node.innerText) {
array.push([node.innerText]);
}
return array;
} else if (node.nodeType == 3) {
return [node.nodeValue];
}
}
I tried it out in the latest chrome, firefox and IE. Here it is running on jsbin: http://jsbin.com/uqize3/7/edit
BTW your HTML string is invalid. Browsers will move "hi" from inside the <head> into the <body>. I assumed you intended to have a <title> in there.
You can do that in JavaScript, because JavaScript is a sufficiently expressive language as to allow just about anything. However, it's not going to be particularly easy: you're going to have to implement (or find) as complete an HTML parser as is necessary to recognize the particular HTML documents that you want to convert. HTML itself is pretty complicated, and that complexity is greatly magnified by the fact that most of the world's stock of existing HTML documents are badly erroneous. Thus, if you've got well-constrained HTML that you know to be valid, or at least consistently invalid, that might make the task a little easier.
edit — #Hemlock points out, quite wisely, that if you're doing this in a browser (that is, if this code is going to run from inside a web page served to browsers), then you've got it a lot easier. You can hand your HTML over to the browser, perhaps as the content document for an <iframe> element you add to the page. If it's not too awful for the browser to parse (and browsers can cope with surprisingly weird HTML), then once the DOM is ready in the <iframe> you can just walk the DOM and generate whatever sort of different representation you want.