I'm doing some experiments, and I found that when you execute that code on any web pages :
document.documentElement.innerHTML = document.documentElement.innerHTML;
Javascript scripts just get 'disabled'. So here is a little script I wrote that re-execute those scripts using jQuery ( so you'll need to inject jQuery on some websites ( I'm using Firebug + FireQuery )) :
// Disabling all javascript scripts
document.documentElement.innerHTML = document.documentElement.innerHTML;
// Saving scripts tags and his parent
var scripts = [];
jQuery('html').find('script').each(function(){
scripts.push([jQuery(this),jQuery(this).parent()]);
}).remove(); // REMOVING ALL SCRIPT TAGS
// Re-appending those deleted script tags in the right place
for (var i in scripts) {
scripts[i][1].append(scripts[i][0]);
}
So this script works on most of the website I tried, except one : Google ( in Firefox )
The thing I'm actually trying to do is storing Google HTML, and pasting it into an iframe ( via Firefox Extension ). So everything is working well except that I can't get the Javascript scripts working ( no autocomplete, buttons are not working... ). Here are the errors i get :
gapi.loaded_0 is not a function
window.google.pmc is undefined
I was thinking that, this may be due to an execution order issue or something. But how can I fix that. Is there any other way to re-run Javascript scripts ? Or any better way doing what I'm doing ?
Thank you !
Yes! There is a better way to do what you are doing.
First, what is happening:
When you use innerHTML the existing elements removed from the DOM. Not quite in a 'burn and salt the earth' because other references might survive, but they are off the DOM.
Fortunately, there is an easy way to deal with this:
This is a wonderful discussion of Event Delegation that describes what you need to do. It's worth reading to understand behind the scenes.
Once you have that, look at the jQuery's Event Delgation which does a bunch of work for you:
Since you didn't show any HTML, I'll invent some:
<div id="contents-will-be-replaced">
<button>Click me</button>
</div>
// Attach to the DIV
// But listen for buttons
$( "#contents-will-be-replaced" ).on( "click", "button", function() {
alert("Button pushed");
});
U can simply use Jquery's .html() to load a script dynamically in the dom. Basically, jquery will inspect your html when it added to the DOM and then create a script tag in the head automatically. It's pretty slick. See if it works for you.
From the docs:
By design, any jQuery constructor or method that accepts an HTML string — jQuery(), .append(), .after(), etc. — can potentially execute code. This can occur by injection of script tags or use of HTML attributes that execute code (for example, <img onload="">). Do not use these methods to insert strings obtained from untrusted sources such as URL query parameters, cookies, or form inputs. Doing so can introduce cross-site-scripting (XSS) vulnerabilities. Remove or escape any user input before adding content to the document.
Related
As the title says, if I remove a script tag from the DOM using:
$('#scriptid').remove();
Does the javascript itself remain in memory or is it cleaned?
Or... am I completely misunderstanding the way in which browsers treat javascript? Which is quite possible.
For those interested in my reason for asking see below:
I am moving some common javascript interactions from static script files into dynamically generated ones in PHP. Which are loaded on demand when a user requires them.
The reason for doing this is in order to move the logic serverside and and run a small script, returned from the server, clientside. Rather than have a large script which contains a huge amount of logic, clientside.
This is a similar approach to what facebook does...
Facebook talks frontend javascript
If we take a simple dialog for instance. Rather than generating the html in javascript, appending it to the dom, then using jqueryUI's dialog widget to load it, I am now doing the following.
Ajax request is made to dialog.php
Server generates html and javascript that is specific to this dialog then encodes them as JSON
JSON is returned to client.
HTML is appended to the <body> then once this is rendered, the javascript is also appended into the DOM.
The javascript is executed automatically upon insertion and the dynamic dialog opens up.
Doing this has reduced the amount of javasript on my page dramatically however I am concerned about clean up of the inserted javascript.
Obviously once the dialog has been closed it is removed from the DOM using jQuery:
$('#dialog').remove();
The javascript is appended with an ID and I also remove this from the DOM via the same method.
However, as stated above, does using jQuery's .remove() actually clean out the javascript from memory or does it simple remove the <script> element from the DOM?
If so, is there any way to clean this up?
No. Once a script is loaded, the objects and functions it defines are kept in memory. Removing a script element does not remove the objects it defines. This is in contrast to CSS files, where removing the element does remove the styles it defines. That's because the new styles can easily be reflowed. Can you imagine how hard it would be to work out what a script tag created and how to remove it?
EDIT: However, if you have a file that defines myFunction, then you add another script that redefines myFunction to something else, the new value will be kept. You can remove the old script tag if you want to keep the DOM clean, but that's all removing it does.
EDIT2: The only real way to "clean up" functions that I can think of is to have a JS file that basically calls delete window.myFunction for every possible object and function your other script files may define. For obvious reasons, this is a really bad idea.
If your scripts have already executed removing the DOM elements are not going to get rid of them. Go to any page with JavaScript, open up your preferred javascript console and type $("script").remove(). Everything keeps running.
And this demonstrates #Kolink answer:
http://jsfiddle.net/X2mk8/2/
HTML:
<div id="output"></div>
<script id="yourDynamicGeneratedScript">
function test(n) {
$output = $("#output")
$output.append("test " + n + "<br/>")
}
test(1);
</script>
Javascript:
$("script").remove();
// or $("#yourDynamicGeneratedScript").remove();
test(2);
test(3);
test(4);
function test(n) {
$output = $("#output")
$output.append("REDEFINED! " + n + "<br/>")
}
test(5);
test(6);
test(7);
I have a problem.
We are doing a Captive Portal.
Go to any site, for example www.php.net
Then in Chrome's console, use this:
$("html").load( "https://www.ccc.co.il/Suspend.aspx" );
You will notice, the DOM is replaced, but not quite the way it should be:
The wrapper elements of the loaded webpage (title, body for example) are missing!
This causes problems of course on the injected page.
How do I replace the entire initial DOM?
And please dont suggest to me using a link, or normal redirect.
Those are the restrictions, I need to replace the entire DOM tree please.
Thanks!
This is fundamentally a feature of browsers.
Here's a snip from the jQuery docs for .load():
jQuery uses the browser's .innerHTML property to parse the retrieved document and insert it into the current document. During this process, browsers often filter elements from the document such as <html>, <title>, or <head> elements. As a result, the elements retrieved by .load() may not be exactly the same as if the document were retrieved directly by the browser.
While I don't recommend what you're suggesting at all, I will attempt to answer your question:
Using a server-side language (like PHP, for example), return documents as parsed json:
{
"head": [head string],
"body": [body string]
}
Then your JavaScript can individually replace each element.
You'll need to switch from .load() to something more configurable, like .ajax()
I think you would have to use an iframe in this case as I don't think that you can replace an entire DOM with another.
$('body').html("<iframe height=100% width=100% frameBorder=0 src='https://www.ccc.co.il/Suspend.aspx'></iframe>");
http://jsfiddle.net/c7EbY/
$.get("https://www.ccc.co.il/Suspend.aspx", function(html){$("html").html(html)});
I'm using a regular AJAX function here because it shouldn't strip anything.
Sorry about those 4 htmls in a row. :P
I want to add a script that applies to a DOM object of a certain type right after it is loaded/rendered. This type of object always comes together with the javascript script, and I want to put them together within some tag. Is it right to do it as below? If so, I suspect span is not the best tag to be used here because it may interact with the way the element inside will be displayed. What tag should I use?
<span>
<div>the dom object to be modified by the script</div>
<script>theJavascriptFunctionThatModifiesTheDomObject()</script>
</span>
I doubt this is the best way to load your script just after a particular element has been loaded by DOM due to these reasons:-
It makes your page load slower.
User will see your complete page in a discrete way.
Instead you should do this:-
Specify a selector to your element.
Include your single javascript code at the end of body.
Update DOM elements using that script.
EDIT:
Solution1: Append your JS at the end of body so that it has access to all the DOM elements.
Since you are injecting the element in DOM using ajax, you can define a success handler for XHR object which will modify your element in DOM.
Solution2: You can define a separate method in your JS and bind this method on some event. In your HTML markup define a data-event attribute and in your success handler append the element to DOM, extract the data-event using jquery data method and trigger that event.
Atleast it will keep you markup far away from scripting logic.
Some useful Links:
Best practices for speeding up your website - yahoo
Why we should load scripts at end - SO Link
The problem here is the script tag does not know where it is located in the DOM. It would be better to do something like add a class to the element[s] you want to alter. On DOM ready, you look up the element[s] and do your magic.
I would avoid this approach; scripts block the page loading
– so if you did this after several dom elements the page would run slow (or not at all if errors were found)
Try using jquery onready - example here : http://api.jquery.com/ready/
And scripts [usually] need to go on the bottom of the page to allow the page to load first
…there are exceptions to this rule such as the well known modernizer script library that needs to go first so it can evaluate the dom as it loads
jQuery does not seem to document how it behaves when using $.html() or $.load() to insert an HTML document fragment into the current DOM:
What does it do with < script > tags?
They are executed, though. But are nowhere to be found in the DOM. It seems jQuery inserts the script blocks just for a moment into the DOM or evals it.
There is no documentation this regard. Do you know any way to tune it, i.e. disable it?
This has implications of security in nature!
The <script> tags are stripped out of the code and explicitly executed after the DOM has been updated.
If you use "$.load()" with a URL that's got a selector in it (it's a hack-y feature of the API), like this:
$.load("http://something #stuff", ... )
then it will not run the scripts.
I'm trying to dynamically insert the Tweetmeme button using Javascript. I'm currently using jQuery throughout the site. Here's the script that I'm using. I basically want to cycle through all the blog entries with a class of journal-entry and append the following JavaScript to the end. This Javascript comes straight from tweetmeme.com. This doesn't work for me though and it has something to do with the code between append(). It doesn't like the second set of script tags.
<script type="text/javascript">
$(document).ready(function() {
$('.journal-entry').each(function(index) {
$(this).append('<script type="text/javascript" src="http://tweetmeme.com/i/scripts/button.js"></script>');
});
});
</script>
Any help is appreciated.
Thanks.
Don't do this.
Inserting <script> HTML content into the DOM is unreliable: it works subtly differently in different browsers, and jQuery won't protect you from the differences.
In particularly, writing innerHTML never causes a <script> element's content to execute, but subsequently moving the <script> element from where it is to a new parent (which is part of jQuery's append process) may cause the script to execute, but not always (it depends on the browser).
In any case, it'll never work, because looking at button.js, it is calling document.write(). That function only makes sense to call at initial document parsing time; if you call it from an event afterwards, the call will simply replace the entire page content, destroying your existing page. A script that calls document.write() can only be run at document load time, from inside the execution path of a <script> element. You can't include the script in dynamically-created content at all, because it's not designed for it.
(If it makes you feel any better, it's barely designed at all; the button.js script is a badly broken load of old crap, including improper URL-escaping—using escape instead of the correct encodeURIComponent—and missing HTML-escaping. You may be better off well away from these total idiots.)
The closing </script> in the string in your append(...) call is closing the overall <script>
Try splitting it up into two strings. E.g:
$(this).append('<script type="text/javascript" src="http://tweetmeme.com/i/scripts/button.js"></'+'script>');