Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically inserting javascript into HTML that uses document.write

I am currently loading a lightbox style popup that loads it's HTML from an XHR call. This content is then displayed in a 'modal' popup using element.innerHTML = content This works like a charm.

In another section of this website I use a Flickr 'badge' (http://www.elliotswan.com/2006/08/06/custom-flickr-badge-api-documentation/) to load flickr images dynamically. This is done including a script tag that loads a flickr javascript, which in turn does some document.write statments.

Both of them work perfectly when included in the HTML. Only when loading the flickr badge code inside the lightbox, no content is rendered at all. It seems that using innerHTML to write document.write statements is taking it a step too far, but I cannot find any clue in the javascript implementations (FF2&3, IE6&7) of this behavior.

Can anyone clarify if this should or shouldn't work? Thanks.

like image 959
Kamiel Wanrooij Avatar asked Sep 10 '08 12:09

Kamiel Wanrooij


4 Answers

In general, script tags aren't executed when using innerHTML. In your case, this is good, because the document.write call would wipe out everything that's already in the page. However, that leaves you without whatever HTML document.write was supposed to add.

jQuery's HTML manipulation methods will execute scripts in HTML for you, the trick is then capturing the calls to document.write and getting the HTML in the proper place. If it's simple enough, then something like this will do:

var content = '';
document.write = function(s) {
    content += s;
};
// execute the script
$('#foo').html(markupWithScriptInIt);
$('#foo .whereverTheDocumentWriteContentGoes').html(content);

It gets complicated though. If the script is on another domain, it will be loaded asynchronously, so you'll have to wait until it's done to get the content. Also, what if it just writes the HTML into the middle of the fragment without a wrapper element that you can easily select? writeCapture.js (full disclosure: I wrote it) handles all of these problems. I'd recommend just using it, but at the very least you can look at the code to see how it handles everything.

EDIT: Here is a page demonstrating what sounds like the effect you want.

like image 57
noah Avatar answered Sep 28 '22 17:09

noah


I created a simple test page that illustrates the problem:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
    <head>
        <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
        <title>Document Write Testcase</title>
    </head>
    <body>
        <div id="container">
        </div>
        <div id="container2">
        </div>

        <script>
            // This doesn't work!
            var container = document.getElementById('container');
            container.innerHTML = "<script type='text/javascript'>alert('foo');document.write('bar');<\/script>";

            // This does!
            var container2 = document.getElementById('container2');
            var script = document.createElement("script");
            script.type = 'text/javascript';
            script.innerHTML = "alert('bar');document.write('foo');";
            container.appendChild(script);
        </script>
    </body>
</html>

This page alerts 'bar' and prints 'foo', while I expected it to also alert 'foo' and print 'bar'. But, unfortunately, since the script tag is part of a larger HTML page, I cannot single out that tag and append it like the example above. Well, I can, but that would require scanning innerHTML content for script tags, and replacing them in the string by placeholders, and then inserting them using the DOM. Sounds not that trivial.

like image 26
Kamiel Wanrooij Avatar answered Sep 28 '22 17:09

Kamiel Wanrooij


Use document.writeln(content); instead of document.write(content).

However, the better method is using the concatenation of innerHTML, like this:

element.innerHTML += content;

The element.innerHTML = content; method will replace the old content with the new one, which will overwrite your element's innerHTML!

Whereas using the the += operator in element.innerHTML += content will append your text after the old content. (similar to what document.write does.)

like image 42
David Refoua Avatar answered Sep 28 '22 16:09

David Refoua


document.write is about as deprecated as they come. Thanks to the wonders of JavaScript, though, you can just assign your own function to the write method of the document object which uses innerHTML on an element of your choosing to append the supplied content.

like image 32
Mo. Avatar answered Sep 28 '22 16:09

Mo.