iamnoah / writeCapture

Utility to assist the Ajax loading of HTML containing script tags that use document.write. Mailing List: http://groups.google.com/group/writecapturejs-users
http://iamnoah.github.com/writeCapture
Other
241 stars 38 forks source link

getElementById() failing #8

Closed lukenysen closed 14 years ago

lukenysen commented 14 years ago

Hi, great plugin!

I'm testing this plugin in an environment where small 3rd party JS advertising scripts use document.write()s to embed their advertising. It worked pretty well right off-the-bat for generic ads, but I found an important use-case where its failing for me.

It fails when it receives the following 3rd-party javascript from our ad network:

=============================

1| 2| document.write(''); 3| 4| var strRand = "rand" + parseInt(Math.random() * 100000); 5| document.write("<di_v id=\"" + strRand + "\"><\/d_iv>"); 6| document.getElementById(strRand).parentNode.className = "adServer taEmpty"; 7| 8| document.write('\n<img class=\"emptySlug\" src=\"http://s0.2mdn.net/1878259/1x1_spacer.gif\" width=\"1\" height=\"1\" border=\"0\">'); 9|

(*Ignore the underscores in the div tag, this was for display purposes only)

==============================

When the JS parser makes it to the getElementById call on line 6, it fails to find the 'strRand' element and throws an exception. This is because 'strRand' is defined in line 5, however, it appears that writeCapture() stores all the HTML sent to document.write() until the script is fully parsed and then writes it to the document. This means 'strRand' won't be available for lookup until after execution of this script yet it is needed midst execution.

I noticed writeCapture.js has a getElementById proxy method... do you already have a way that this type of case can be made to work?

NOTE: I'm using the most recent version of writeCapture, version 1.0.0 (looks like you just updated it 5 hours ago)

Cheers, and thanks in advance :D Luke

iamnoah commented 14 years ago

I was actually just thinking about how to design code to break writeCapture and this is it.

You could actually allow it to find the element by setting writeCapture.proxyGetElementById=true, but it would still fail because parentNode would be null...

I have an idea though. writeCapture could output what it already has when getElementById fails to find the element, which might work here. It might also cause terrible problems, so no guarantees.

iamnoah commented 14 years ago

I've pushed a change that may resolve this. I would try something like this in your code:

$.writeCapture.writeOnGetElementById=true;
$('#whatever').writeCapture().html('htmlWithTheProblemScript',function() {
    $.writeCapture.writeOnGetElementById=false;
});

I would turn the hack off when you don't need it (hence the callback). It wont always work. If getElementById is called before all the tags in the HTML are closed, the output will be messed up, although it's possible it wont affect anything.

All caveats aside, I think this will work in your case. Give it a try a let us know if it works.

lukenysen commented 14 years ago

That is brilliant!... yup, it did the trick.

Btw, I'm actually calling writeCapture 4 or 5 times successively where each call is at risk to receive the above troublesome response. Since responses are happening asynchronously, before i change the shared "writeOnGetElementById" value back to false, I actually need to wait for ALL requests to return and set up some sort of check. It would be nice if we could set the "writeOnGetElementById" value per call to writeCapture, perhaps in a passed in options object to writeCapture.

$('#myObject').writeCapture( {writeOnGetElementById:true} ).html('')

The added benefit here is that some writeCaptures can be set to take this precaution while others running in parallel can choose to ignore it.

lukenysen commented 14 years ago

BTW, serious kudos on this plugin. Nice work!

iamnoah commented 14 years ago

All calls to writeCapture actually a placed into a global queue, so they will always run in the same order you make the calls, so just turning it off in the last callback will be sufficient.

I can see though that a passed in option would be a lot simpler. Thanks for the feedback!

lukenysen commented 14 years ago

Okay, I've just completed a thorough test, and every browser I tested checked out. I've tested the following browser OS combos:

Linux:

Windows XP

Mac

iamnoah commented 14 years ago

Very cool, thanks!