Run script after appending it to the HTML - javascript

I have a string with HTML:
var str = '<div><p>Examplee</p></div><script>alert("testing!")</script>';
and then I append it to the HTML:
document.body.innerHTML += str;
and the content is appended but the script does not execute, is there a way to force it?

First, a caveat: Naturally, only do this with scripts you trust. :-)
There are a couple of ways. Basically, you need to:
Get the text of the script, and
Run it
Getting the text
One option is just to go ahead and add it, then find it and grab its text:
document.body.innerHTML += str;
var scripts = document.querySelectorAll("script");
var text = scripts[scripts.length - 1].textContent;
On obsolete browsers, you may need to feature-detect textContent vs. innerText.
You might want to give it an identifying characteristic (id, class, etc.) so you don't have to find it by position like that.
Alternately, you could do what the PrototypeJS lib does and try go get it from the string with regex. You can find their source code for doing that here (look for extractScripts).
Running it
Once you have the text of the script, you have several options:
Use indirect eval (aka "global eval") on it: (0, eval)(text). This is not the same as eval(text); it runs the code in global scope, not local scope.
Create a new script element, set its text to the text, and add it
var newScript = document.createElement("script");
newScript.textContent = text;
document.body.appendChild(newScript);
If doing that, might make sense to remove the original, though it doesn't really matter.
Example that grabs the text of the script element after adding it and uses indirect eval:
var str = '<div><p>Examplee</p></div><script>alert("testing!")<\/script>';
document.body.innerHTML += str;
var scripts = document.querySelectorAll("script");
(0, eval)(scripts[scripts.length - 1].textContent);
Presumably you don't really use += on document.body.innerHTML, which builds an HTML string for the whole page, appends to it, tears the whole page down, and then parses the new HTML to build a new one. I assume that was just an example in your question.

jQuery provides the $.getScript(url [,success]) function. You can then load and execute your code from a separate jquery file which helps to manage and control the flow of execution.
basically put your alert("testing!") script inside a separate file, for instance alert.js in the same directory.
Then you can run the script when adding your employee to the HTML.
var str = '<div><p>Examplee</p></div>';
var url = 'alert.js';
document.body.innerHTML += str;
$.getScript(url);
I know this may seem like more work, but it is better practice to keep your javascript out of your HTML. You can also use a callback to gather user data after the alert or notify another process that the user has been alerted.
$.getScript(url, function(){
//do something after the alert is executed.
});
For instance maybe it would be a better design to add the employee after the alert is executed.
var str = '<div><p>Examplee</p></div>';
var url = 'alert.js';
$.getScript(url, function(){
document.body.innerHTML += str;
});
Edit: I know jQuery is not tagged, but I am also no petitioning to be the accepted answer to this question. I am only offering another alternative for someone who may run into the same issue and may be using jQuery. If that is the case $.getScript is a very useful tool designed for this exact problem.

You should change the HTML after it was loaded.
Try this:
document.addEventListener("DOMContentLoaded", function(event) {
document.body.innerHTML += str;
});

Related

cheerio find a text in a script tag

I want to extract js script in script tag.
this the script tag :
<script>
$(document).ready(function(){
$("#div1").click(function(){
$("#divcontent").load("ajax.content.php?p=0&cat=1");
});
$("#div2").click(function(){
$("#divcontent").load("ajax.content.php?p=1&cat=1");
});
});
</script>
I have an array of ids like ['div1', 'div2'], and I need to extract url link inside it :
so if i call a function :
getUrlOf('div1');
it will return ajax.content.php?p=0&cat=1
If you're using a newer version of cheerio (1.0.0-rc.2), you'll need to use .html() instead of .text()
const cheerio = require('cheerio');
const $ = cheerio.load('<script>script one</script> <script> script two</script>');
// For the first script tag
console.log($('script').html());
// For all script tags
console.log($('script').map((idx, el) => $(el).html()).toArray());
https://github.com/cheeriojs/cheerio/issues/1050
With Cheerio, it is very easy to get the text of the script tag:
const cheerio = require('cheerio');
const $ = cheerio.load("the HTML the webpage you are scraping");
// If there's only one <script>
console.log($('script').text());
// If there's multiple scripts
$('script').each((idx, elem) => console.log(elem.text()));
From here, you're really just asking "how do I parse a generic block of javascript and extract a list of links". I agree with Patrick above in the comments, you probably shouldn't. Can you craft a regex that will let you find each link in the script and deduce the page it links to? Yes. But very likely, if anything about this page changes, your script will immediately break - the author of the page might switch to inline <a> tags, refactor the code, use live events, etc.
Just be aware that relying on the exact contents of this script tag will make your application very brittle -- even more brittle than page scraping generally is.
EDIT: Sure, here's an example of a loose but effective regex:
let html = "incoming html";
let regex = /\$\("(#.+?)"\)\.click(?:.|\n)+?\.load\("(.+?)"/;
let match;
while (match = regex.exec(html)) {
console.log(match[1] + ': ' + match[2]);
}
In case you are new to regex: this expression contains two capture groups, in parens (the first is the div id, the second is the link text), as well as a non-capturing group in the middle, which exists only to make sure the regex will continue through a line break. I say it's "loose" because the match it is looking for looks like this:
$("***").click***ignored chars***.load("***"
So, depending on how much javascript there is and how similar it is, you might have to tighten it up to avoid false positives.

How to use req.responseText, in Ajax to return only the content inside a div of a page

I am using XMLHttp request in Ajax, but I have a problem. I want that to use this document.getElementById('partid').innerHTML=request.responseText
in a way that return only the content inside the divcalled'cars' of the page export.php. But when I use it like above it return all the content of the page export.php. Is there any way to make this line of code to return only the content inside a div?
I am trying to use it like:
document.getElementById('partid').innerHTML=request.responseText.getElementById('cars').
, but it does not function.
Please,help me. I have spend hours searching but i have not find anything about this..Please help.Thanks in advance
When responseText comes back, it is -- as the name suggests -- text.
Before you can access it as HTML, you have to turn it INTO HTML.
This is typed off the cuff, but it would be something like:
var tempdiv = document.createElement("div");
tmpdiv.innerHTML = request.responseText;
var wantedDiv = tmpDiv.querySelector('[id="cars"]');
// Might want to empty out 'partid' first
document.getElementById('partid').appendChild(wantedDiv);
request.responseText is just text perform any DOM operations on it. You can parse it with jQuery though and find the required content.
$('#partid').html($(request.responseText).find('#cars').html());
http://api.jquery.com/find/
http://api.jquery.com/html/

Use JavaScript to update the contents of a script tag dynamically

This click event works:
document.getElementById('control').addEventListener('click', function(){
alert('test');
});
Why can't I make it the content of a script tag that already exists on the page?
var s = document.getElementsByTagName("script")[0];
s.innerHTML = "document.getElementById('control').addEventListener('click', function(){alert('test');});";
I've tried enclosing the attempt to do something in an IIFE:
s.innerHTML = "(function(){alert('test')}());";
so that it will call itself.
Also:
s.innerHTML = "var f = function(){alert('f')}; f();"
I'm only interested theoretically and in no way am saying this is a good or bad idea, but how could I make it work?
Script tags are evaluated once and code is not replaced if you change their contents (they work differently than other types of tags in this regard). If you want to add new code to the page, you can simply add new script tags to the page that contains the new code. If you want to redefine existing publicly accessible functions, you can simply redefine them with new code.
So, if you want to replace a previous definition of function f(), you can simply assign new code to that symbol:
f = function() {alert("new f");}
You can insert new scripts from a remote source at any time with a code snippet like this:
var s = document.createElement("script");
s.type = "text/javascript";
s.src = url;
document.getElementsByTagName("head")[0].appendChild(s);
It is also possible to use eval() to pass text to the javascript engine that you want it to parse and then run, but there are rarely any good reasons to do it this way as there are generally better ways to solve a problem like this.
In addition to what jfriend00 said. If you want to dynamically execute scripts. Use should probably use JavaScript's eval function.
Note: But use it carefully!!!
you can't do this with an existing script tag, you have to create a new script tag and push your script before adding it to the DOM:
var sc =document.createElement("script");
sc.type="text/javascript";
var scriptText=document.createTextNode("document.getElementById('control').addEventListener('click', function(){"+
"alert('test');" +
"});");
sc.appendChild(scriptText);
document.querySelector("head").appendChild(sc);
and it will do what you want.
The important point here, once the first script in a script tag is executed, you no longer can inject any script in it, actually you can, but it won't work.

firefox add-on innerHTML not allowed, DOM help needed

I'm writing my first firefox add-on.
It was completed, but mozilla rejected it with this answer:
1) Your add-on creates DOM nodes from HTML strings containing potentially unsanitized data, by assigning to innerHTML or through similar means. Aside from being inefficient, this is a major security risk. For more information, see https://developer.mozilla.org/en/XUL_School/DOM_Building_and_HTML_Insertion. Here are some examples where you do this: (cut...)
I wrote:
var myDiv = content.document.getElementById("myContent");
myDiv.innerHTML = "some html code";
now I'm not a JS programmer and I don't understand how to go on.
I tested some code like this:
var NewNode = content.document.createElement("span");
NewNode.appendChild(content.document.createTextNode("Hello World!"));
//content.document.body.appendChild(NewNode);//ok, works
content.document.getElementById("myContent").appendChild(NewNode);//doesn't work
but it doesn't work until I append it to .body
Samples working on other pages seems not working here. Moreover I don't understand if it fixes the problem that mozilla indicated.
Could you please help me with the code that should replace the two lines I wrote?
If you need the full code, here it is: http://www.maipiusenza.com/LDV/down/ldvgenerator.xpi
Thanks!
Nadia
Just did a quick js fiddle, I was wondering why you have used content.document so I amended it to document and it worked.
http://jsfiddle.net/eDW82/
var NewNode = document.createElement("span");
NewNode.appendChild(document.createTextNode("Hello World"));
document.getElementById("myContent").appendChild(NewNode);
I had a similar problem with unsanitized HTML and as I used it extensively I opted to use jQuery which will pass mozillas rules. It makes life a lot easier to be able to create your nodes that way.
$("<div>", {id:"example"}).text("Hello World")
It just reads so much nicer.
OK then, I did some digging and I think I managed to find your problem:
Whenever you want to inject any kind of html to your extension, The browser considers it as a security hole, that's why you have this problem. you have 2 different solution;
first: you can create an iframe and use it to show your html (in javascript whenever we want to show a file we have 2 option, first pass a file path on the server, or use data: to show your data directly):
var htmlStr = "<span>Hello World!</span>";
var frm = content.document.createElement("iframe");
content.document.getElementById("myContent").appendChild(frm);
frm.src = "data:text/html;charset=utf-8," + encodeURIComponent(htmlStr);
second: this solution would help you out, if you don't want to use an iframe to show your html.
var htmlStr = "<span>Hello World!</span>";
var frm = document.createElement("iframe");
frm.style.display="none";
document.body.appendChild(frm);
var win = frm.contentWindow;
var frmrange = win.document.createRange();
// make the parent of the first div in the document becomes the context node
frmrange.selectNode(win.document.firstChild);
var frg = frmrange.createContextualFragment(htmlStr);
content.document.getElementById("myContent").appendChild(frg);
Old Guess: the problem in your code is different document objects, try this:
var NewNode = content.document.createElement("span");
NewNode.appendChild(content.document.createTextNode("Hello World!"));
content.document.getElementById("myContent").appendChild(NewNode);
this was my first clue to point out.

Search on page with javascript

I have a html page, and I want to find on it some data, but main trouble is that page is generated on server, and I want to write javascript code on my local machine and run it. So how can I write and run on local machine javascript code, so that it will find text, or get element by id/class?
Note, this is important: only pure javascript, no libraries like jQuerys and etc!
Thank you.
Updated answer:
I didn't understand at first that you want to call up a web page you're not in control of, and then use JavaScript in your browser to interact with it.
The information in the original answer below is still relevant, but the question is: How do you make the code run in the right context? And the answer is: There are at least two ways:
Any decent browser these days has built-in debugging tools. Look on the menus for them, but in many browsers they're accessible via the F12 key or Ctrl+Shift+I. In those tools, you'll find a "console" where you can type JavaScript and have it run in the context of the page you're looking at.
This is great for doing things interactively, but it's a bit of a pain to retype it every time. You can also put the code in a local file (say, /home/tjc/foo.js) and then when you go to the page, use the console to append that script to the page (which will cause it to execute, within the context of the page), like this:
document.documentElement.appendChild(document.createElement('script')).src = "file:///home/tjc/foo.js";
Once you have your script doing what you want, you might want to turn it into a bookmarklet. This is a browser bookmark using the javascript: scheme rather than the usual http: and such. See the link for details. You'll want a tool that takes your JavaScript code and does the necessary URL-encoding for you, like the Bookmarklet Crunchinator or similar.
Original answer:
... so that it will find text, or get element by id/class...
Those are three very different questions:
To find text on the page, you have a couple of options:
If you only want to find the text but don't much care exactly what element contains it, you can just look through innerHTML on document.body. innerHTML is a string; when you access it, the browser creates an HTML string for all of the DOM elements in the element you call it on (and its descendants). Note that this is not the original content from the server; this is created on-the-fly when you access the element. For a lot of use-cases, getting this string and then looking through it could be useful. Note that the text you're searching through is markup, so for instance, if you searched for the word "table" you might find it in a sentence ("We sat down at the table.") or in markup (<table>...).
Here's an example of counting the word I'm on the page using innerHTML: live copy | source - See note about examples at the end.
(function() {
var pageText = document.body.innerHTML;
display('Count of "I\'m" on the page: ' +
pageText.match(/I'm/g).length);
function display(msg) {
var p = document.createElement('p');
p.innerHTML = String(msg);
document.body.appendChild(p);
}
})();
If you need to find out exactly what element it's in, you'll need to write a recursive function that walks through the nodes of the page and, for Text nodes, looks at the text within. Here's a basic example (the function is the walk function): Live copy | source - See note about examples at the end.
(function() {
var matches = [], index;
walk(matches, document.body, "");
function walk(matches, node, path) {
var child;
switch (node.nodeType) {
case 1: // Element
for (child = node.firstChild; child; child = child.nextSibling) {
walk(matches, child, path + "/" + node.tagName);
}
break;
case 3: // Text
if (node.nodeValue.indexOf("I'm") !== -1 ) {
matches.push("Found it at " + path);
}
break;
}
}
display("Matches found (" + matches.length + "):");
for (index = 0; index < matches.length; ++index) {
display(matches[index]);
}
function display(msg) {
var p = document.createElement('p');
p.innerHTML = String(msg);
document.body.appendChild(p);
}
})();
To find an element on the page by id, use document.getElementById.
To find an element on the page by class, on most modern browsers you can use either getElementsByClassName or querySelectorAll.
Note about the examples: I'm using JSBin, which puts the JavaScript you see on the left-hand side in the "source" view at the end of the HTML you see on the right (just before the closing </body> tag), by default. This is in keeping with best practices.
Reading:
DOM2 Core
DOM2 HTML
DOM3 Core
HTML5 Web Application APIs
If you are looking for imacros solution, then it'some like this:
var reportDataTable = window.content.document.getElementById("yoursid");
if (reportDataTable == null)
{
iimPlay("mac1.iim");
}
else
{
iimDisplay("stop!");
}
Where mac1.iim is macros, which would repeated, until
window.content.document.getElementById("yoursid");
will not be founded

Categories

Resources