Dynamically Remove JavaScript (using JavaScript!)

XML, Perl, Python, and other languages can be discussed here, even if it isn't PHP (We might forgive you).

Moderator: General Moderators

Post Reply
tomprogers
Forum Commoner
Posts: 50
Joined: Fri Mar 17, 2006 5:17 pm
Location: Minnesota
Contact:

Dynamically Remove JavaScript (using JavaScript!)

Post by tomprogers »

Weirdan | Please use

Code: Select all

,

Code: Select all

and [syntax="..."] tags where appropriate when posting code. Your post has been edited to reflect how we'd like it posted. Please read: :arrow: [url=http://forums.devnetwork.net/viewtopic.php?t=21171]Posting Code in the Forums[/url] to learn how to do it too.[/color]


I'm writing an app that, among other things, will load segments of other pages (served up via AJAX). Because you can't just drop a <script> block into a <div> and have it work, I have PHP parse out <script> blocks (either embedded or included) and include the relevant information in the XML. The JavaScript will then loop through the XML and dynamically load any JS used by the remote page, using something along these lines:
[syntax="javascript"]// pseudocode
foreach(scriptentity as script)
{
 var tag = doc.createElement("SCRIPT");
 tag.type = "text/javascript";
 tag.id = randomString(16); // <script> doesn't support an actual ID (so no doc.getElementById()), but I can still use it to identify this block
 window.extrascripts.push(tag.id); // register this id as a dynamically-loaded script, for unloading later
 if(script.type == "embed") tag.innerHTML = script.contents; // in the case of a non-empty script tag, simply dump the actual JS into the new tag
 else
 if(script.type == "include") tag.src = script.src // in the case of an external file, the contents will be equal to the SRC attribute from the original document
doc.head.appendChild(tag);
 // at this point, any functions defined in the original document, either via embedded code or an external file, will be available to this page, and no one's the wiser
}
This works, but now I'm trying to figure out how to unload these scripts, client-side. Once again, pseudocode:

Code: Select all

foreach(extrascripts as sScriptID)
{
 var h = doc.head;
 var scripts = h.getElementsByTagName("SCRIPT");
 for(i = 0; i < scripts.length; i++)
   if(scripts[i].id && scripts[i].id == sScriptID)
      h.removeChild(scripts[i]);
}
Technically, this works. I've done the debugging, and each line of code in the removal process operates as desired, on the correct objects. However, the functions defined in the "extra" file still appear to be in memory, because references to them continue to function. Example:

Given an A with onclick="includedFunction();", on a page with no definition for includedFunction()
  • click the A; nothing happens (because the function doesn't exist)
  • click a button that triggers my "load extra script" function; I am notified that the script has been loaded
  • click the A again; the function includedFunction pops up an alert dialog (which is what it should do)
  • click a button that triggers my "remove extra script" function; I am notified that the script has been identified and removed
  • click the A a third time; the function includedFunction continues to work, even though the file that defines it is no longer a part of this document
Any ideas how I can purge the contents of the external file from memory without refreshing the page?[/list][/syntax]


Weirdan | Please use

Code: Select all

,

Code: Select all

and [syntax="..."] tags where appropriate when posting code. Your post has been edited to reflect how we'd like it posted. Please read: :arrow: [url=http://forums.devnetwork.net/viewtopic.php?t=21171]Posting Code in the Forums[/url] to learn how to do it too.[/color]
User avatar
volka
DevNet Evangelist
Posts: 8391
Joined: Tue May 07, 2002 9:48 am
Location: Berlin, ger

Post by volka »

Because you can't just drop a <script> block into a <div> and have it work
No?

Code: Select all

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>script block test</title>    
   </head>
   <body>
   	<div id="myContent">
   		<script type="text/javascript">
   			h1 = document.createElement("h1");
   			h1.appendChild(document.createTextNode("test test test"));
   			document.getElementById("myContent").appendChild(h1);
   		</script>
   	</div>
   	<p>
    	<a href="http://validator.w3.org/check?uri=referer"><img
				src="http://www.w3.org/Icons/valid-xhtml10"
				alt="Valid XHTML 1.0 Strict" height="31" width="88" /></a>
  	</p>
	</body>
</html>
http://validator.w3.org/check wrote:This Page Is Valid XHTML 1.0 Strict!
tomprogers
Forum Commoner
Posts: 50
Joined: Fri Mar 17, 2006 5:17 pm
Location: Minnesota
Contact:

Clarification

Post by tomprogers »

Weirdan | Please use

Code: Select all

,

Code: Select all

and [syntax="..."] tags where appropriate when posting code. Your post has been edited to reflect how we'd like it posted. Please read: :arrow: [url=http://forums.devnetwork.net/viewtopic.php?t=21171]Posting Code in the Forums[/url] to learn how to do it too.[/color]


Sorry, I thought I had put that in enough context; you can't [i]update[/i] the contents of a div with a script block and have it work.

For example, assume the following page:
[syntax="html"]<div id="test">
  <p>There is some stuff in here.</p>
</div>
<a href="javascript:void(0);" onclick="updateDiv();">Fire it up!</a>
Also, assume this exists within this page's head:

Code: Select all

function updateDiv() {
  var d = document.getElementById('test');
  if(!d) return false;
  d.innerHTML = '<script type="text/javascript">function test() { alert("It works!"); return true; }</script> <p><a href="javascript:void(0);" onclick="test();">Add</a> some js</p>';
  return true;
}
Using innerHTML to add JS that didn't exist at page load does not trigger the browser's "parse javascript" routines, so the function test() will not exist, even though the updated div will now have a link that calls test().


Weirdan | Please use[/syntax]

Code: Select all

,

Code: Select all

and [syntax="..."] tags where appropriate when posting code. Your post has been edited to reflect how we'd like it posted. Please read: :arrow: [url=http://forums.devnetwork.net/viewtopic.php?t=21171]Posting Code in the Forums[/url] to learn how to do it too.[/color]
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

it's possible to dynamically add script data.
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Post by Weirdan »

hmmm... perhaps something along the lines of:

Code: Select all

for(i in window) {
   if(typeof window[i] == 'function') {
       window[i] = function() {}; // assign empty function
   }
}
should work for you. But there are severe limitations:
  • you can't remove event handlers that were assigned as

    Code: Select all

    document.getElementById('id').addEventListener('click', function(e) {
        // do something
    }, false);
    
  • basically, you can't purge anonymous functions because the only code that has access to them is the code that holds references to them (there would be situations when there's no references to the given function in the global scope but they are still referenced inside some closures)
And you have to take extra-care to not redefine browser's internal functions (as well as those belonging to your own app).
Last edited by Weirdan on Thu Aug 10, 2006 4:34 pm, edited 1 time in total.
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Post by Weirdan »

it's possible to dynamically add script data.
Would you mind to share your knowledge?
tomprogers
Forum Commoner
Posts: 50
Joined: Fri Mar 17, 2006 5:17 pm
Location: Minnesota
Contact:

Hmm

Post by tomprogers »

Conveniently, I don't care if I fail to erase anonymous functions - I'm primarily interested in avoiding namespace collisions between the scripts of multiple pages, none of which will ever actually exist simultaneously. Is there a way to tell if a given property of window is native? I know that if you try to do alert(window.open); (or something similar), you get "[native code]" - theoretically all the native functions should be tagged somehow, or won't appear in a for...in loop, preventing me from erasing them.

I think you've got it, though. I had previously tried adding if(typeof includedFunction == 'function') includedFunction = null;, and it had the intended effect.

I'll let you know what I come up with. Thanks.
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Post by Weirdan »

I know that if you try to do alert(window.open); (or something similar), you get "[native code]" - theoretically all the native functions should be tagged somehow, or won't appear in a for...in loop, preventing me from erasing them.
In IE they do not show up, in Mozilla you can check it as follows:

Code: Select all

for(i in window) {
   if(typeof window[i] == 'function') {
       if(window[i].toString() == window.open.toString()) {
           // native function
       } else {
           // user-defined function
       }
   }
}
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

eval() is the key to dynamically adding script.
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Post by Weirdan »

eval() is the key to dynamically adding script.
Oh, thanks, didn't think about that Image
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

I would also suggest using the DOM functionality instead of innerHTML.
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Post by Weirdan »

I would also suggest using the DOM functionality instead of innerHTML.
Does IE provide that handy DomParser function? Image
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

Weirdan wrote:Does IE provide that handy DomParser function? Image
I'm referring more to the CreateElement/CreateTextNode variety of DOM functionality here. .. which I seem to remember IE supporting.

..of course I could be senile. Image
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Post by Weirdan »

I'm referring more to the CreateElement/CreateTextNode variety of DOM functionality here...
If I understood correctly, original poster wanted to load some pages over which he had no control (except using his own proxy script to load them). What a tedious task it would be to parse (badly formed) html in JS and then reconstruct it using createElement/createTextNode...

On the other hand, it's possible to use innerHTML as poor-man replacement for DomParser:

Code: Select all

var div = document.createElement('div');
div.innerHTML = '<base href="http://original.site.com/" />' + req.responseText;
var images = div.getElementsByTagName('img');
var imgUrls = [];
images.forEach(function(image) {
    imgUrls.push(image.src);
});
Post Reply