0

in a previous SO post a solution was found to the issue of dynamically loading pages with embedded <script> tags. however, it was not a comprehensive solution, as I've come to find. The scenario that breaks the code (please review previous post and its solution), is something that looks like this:

- svc.html -

<script type="text/javascript" src="/plugin.js" css="/plugin.css"></script>
<script type="text/javascript">
plugin_method();
</script>

<div id='inner'>
dynamically loaded content
</div>

where plugin.js looks the same as before but contains a function definition for plugin_method.

The problem seems to be that when the script node is added to the document:

target[0].appendChild(scriptNode);

the code is not executed immediately. If (as in the previous post) there is only one script tag in the svc.html page, everything is fine, but once there is a second one, the $('script:last') in the plugin no longer points to the correct script, and therefore fails to load the stylesheet.

furthermore, the body of the second script tag seems to get executed before sourcing-in the code for the first, such that the method call fails (since the function hasn't been defined yet).

I've found a couple of links, which I'm mulling over:

http://requirejs.org/docs/why.html http://unixpapa.com/js/dyna.html

ideas anyone?

Community
  • 1
  • 1
ekkis
  • 9,804
  • 13
  • 55
  • 105

1 Answers1

0

ok, so the answer is we can't just iterate through the list of scripts - we need to recurse within the load event, like this:

// inspired by Kevin B's suggestions
// at: http://stackoverflow.com/questions/8234215/where-are-scripts-loaded-after-an-ajax-call

(function ($) {
    $.fn.loadWithJs = function (o) {
        console.log(".loadWithJs - started");
        var target = this;

        target.addScript = function (scripts, n, success) {
            var script = scripts[n];

            var scriptNode = document.createElement('script');
            for (var i = 0; i < script.attributes.length; i++) {
                var attr = script.attributes[i];
                $(scriptNode).attr(attr.name, attr.value);
            }
            scriptNode.appendChild(
                document.createTextNode($(script).html())
            );
            $(scriptNode).load(function () {
                if (++n < scripts.length)
                    target.addScript(scripts, n, success);
                else if (success)
                    success();
            });
            target[0].appendChild(scriptNode);
        };

        var success = o.success;
        o.success = function (html) {
            // convert script tags
            var h = $('<div />').append(
                html.replace(/<(\/)?script/ig, "<$1codex")
            );
            // detach script divs from html
            var scripts = h.find("codex").detach();
            // populate target
            target.html(h.children());
            // start recursive loading of scripts
            target.addScript(scripts, 0, success);
        };

        $.ajax(o);
    };
})(jQuery);

* edit *

ok, that doesn't work either. If we add a script tag at the end of main.html, it gets added to the $('script') array before the first load event fires (and before the scripts get run) and therefore $('script:last') is still wrong. grr...

ekkis
  • 9,804
  • 13
  • 55
  • 105