1

I'm loading a HTML partial through ajax. The partial is attached to the DOM by using innerHTML on an existing node.

The partial contains a few script tags at the bottom, something like:

<script src="/Scripts/Griffin.Editor.js" type="text/javascript"></script>
<script type="text/javascript">
            marked.setOptions({
                renderer: new marked.Renderer(),
                gfm: true,
                tables: true,
                breaks: false,
                pedantic: false,
                sanitize: true,
                smartLists: true,
                smartypants: false
            });
            var textParser = {
                parse: function (text) {
                    return marked(text);
                }
            }
            var prismHighlighter = {
                highlight: function (blockElements, inlineElements) {
                    blockElements.forEach(function(item) {
                        Prism.highlightElement(item);
                    });

                }
            };
            var editor = new Griffin.Editor('editor', textParser);
            editor.syntaxHighlighter = prismHighlighter;
            editor.preview();
</script>

However, as the script tags are not executed, I traverse the loaded partial to identify all script tags. I then create new script nodes in the DOM and attach them to the HEAD.

Something like:

var scripts = viewElem.getElementsByTagName('script');
for (let i = 0; i < len; i++) {
    var scriptTag = scripts[0];
    let node = document.createElement('script');
    if (scriptTag.src && scriptTag.src.length > 0) {
        node.src = scriptTag.src;
        node.type = scriptTag.type;
    } else {
        node.text = scriptTag.text;
        node.type = scriptTag.type;
        //had eval here before (instead of attaching the embedded script to the HEAD).
    }
    document.head.appendChild(node);
    scriptTag.parentNode.remove(scriptTag);
}

From what I understand the browser should load the referenced scripts before invoking the embedded script. That is however not the case for me, because the JS console complains about not finding the object defined in the dependency script.

If I use a timer and eval the embedded script in it everything works. But that seems as a ugly workaround and I really want to understand the mechanics behind the load behavior (i.e. why the scripts are not executed when the partial is attached to the DOM and why the referenced scripts are not loaded directly when I add the nodes to the HEAD tag).

jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • Does the script contain document.write or other inline only code? Also you remove the script immediately, perhaps there is something still needed – mplungjan Jul 19 '15 at 19:32
  • could be the problem that some script files are dependent on other script file loading. You should check this answers. [link](http://stackoverflow.com/a/29250976/1577396) – Mr_Green Jul 19 '15 at 19:33
  • @mplungjan: I added the real script – jgauffin Jul 19 '15 at 19:34
  • So the settings and invocation for the editor need the editor js to be present it would seem – mplungjan Jul 19 '15 at 19:35

2 Answers2

0

From what I've encountered, you can't have immediately executing JavaScript inside an Ajax response. The reason being you are trying to execute JavaScript inside another JavaScript function. So the browser has no idea which executing context to use with this scenario.

I would recommend using deferred execution like you mentioned. Except, you need to let the browser interpret the Ajax response first. For example:

$.get('url', function (html) {
    // html = "<script>function myTest () { console.log('here'); }</script>"
    $('#result').html(html);
    // Now that the DOM has had time to parse the response we can do:
    myTest();
});

Notice it is the Ajax callback invoking the response function not the response immediately executing itself. Hope this helps.

beautifulcoder
  • 10,832
  • 3
  • 19
  • 29
0

I found a really great article explaining in depth how scripts are loaded into the browser.

In essence you can't be sure of execution order per default when you include scripts dynamically. To be sure of the order you need to do one of the following

a. Use async=false if supported
b. Use readyState (for ie<10)
c. Use defer attribute.

Try to use the mentioned features in that order to be sure.

However, even if you do all that you will still get screwed if you mix embedded scripts (code in in the script tag) with referenced scripts (using src attribute).

The problem is that the embedded scripts will run directly, even if the references script tags are added before. To solve that you need to push the embedded scripts into a queue and hook the load event for all referenced scripts.

Once all referenced scripts have toggled the load even you are free to invoke the embedded scripts (either by added the script tags to an element or by eval their text property).

Source: http://blog.gauffin.org/2015/07/embedded-script-tags-in-content-loaded-through-ajax-and-execute-the-script-tags-dynamically/

jgauffin
  • 99,844
  • 45
  • 235
  • 372