0

I'm using jQuery 2.1.3 on an Asp.net MVC site that renders partial views to certain site areas. A partial view can contain a script block that manipulates the area DOM. Partial views can also contain other partial views, and I can thus end up with several script tags.

This is usually not a problem. For simplicity I have made this example that alerts both 1 and 2:

$("body").html('<div id="target"><script>alert("dynamic dom 1");<\/script></div><div id="source"><script>alert("dynamic dom 2");<\/script></div>'); 

But if the first script manipulates the DOM, and moves the div containing the second script tag, the entire script isn't executed, and I only get alert 1:

$("body").html('<div id="target"><script>alert("dynamic dom 1");$("#target").append($("#source"));<\/script></div><div id="source"><script>alert("dynamic dom 2");<\/script></div>'); 

I'm not getting any JS errors that could stop execution!

I'm using jQuery.append(), that should and in fact does move the div element.

I have tried using ready() handlers to ensure that the DOM is fully loaded, but with the same effect.

This only seems to be a problem with dynamically loaded HTML. It works if I include the code in a plain html file, but with a single page MVC site that's not a usable workaround.

Is this a bug in jQuery.html() or jQuery.append(), or am I using it wrong?

EDIT: I'm not having problems adding the script tag(s). I'm not quite sure how added script tags are executed, and how one added script can affect the execution of the other. This could be the source of the problem. If you know how jQuery handles this, then please elaborate.

EDIT 2: I have recently made a big jQuery update, and this code worked with older jQuery versions. In fact, this works with jQuery-1.8.3, but not with jQuery-1.9.0 (or later). In this update jQuery marks inserted scripts as executed to prevent them from being run several times.

See: http://bugs.jquery.com/ticket/11795

and: http://jquery.com/upgrade-guide/1.9/#loading-and-running-scripts-inside-html-content

I know that it is poor practice to mix JS and HTML, but with MVC partial views it helps maintainability and overview to keep the JS and HTML in the same view file.

Nevertheless, I think that jQuery somehow wrongfully marks the second script to have been executed, when in fact it has not. Can someone please explain how scripts are marked as executed?

Lovbjerg
  • 1
  • 4
  • possible duplicate of [How to dynamically insert a – R3tep Jun 03 '15 at 13:25
  • Do the scripts need to be in the DOM, can't they just be executed? – Luke Jun 03 '15 at 14:05

1 Answers1

0

In general

It seems that from jQuery 1.9.0, when dynamically inserting DOM elements that include script tags, jQuery first scans/parses the input DOM and tags scripts as executed. It then traverses the DOM and evaluates the scripts.

See: http://jquery.com/upgrade-guide/1.9/#loading-and-running-scripts-inside-html-content

When the script tag is moved with jquery.append(), it is already tagged as executed and thus won't be evaluated.

It is not important where in the DOM hierarchy the script tag is moved to/from. The first $.html() tags it as executed, and when moving it, the subsequent $.append() doesn't know that it in fact wasn't executed yet.

To evaluate it explicitely one can do eval($("#source script").text()), i.e.:

$("body").html('<div id="target"><script>alert("dynamic dom 1");$("#target").append($("#source"));eval($("#source script").text());</script></div><div id="source"><script>alert("dynamic dom 2");</script></div>');

For Asp.net MVC

This will help Asp.net MVC partial views where you want to keep the JS close to the DOM elements it manipulates (i.e. in the same file), e.g.:

$("body #someContainer").html('@Html.Partial("_myWidget")');

with "_myWidget.cshtml":

<div id="target">@Html.Partial("_myWidgetContent")</div>
<script>
    alert("dynamic dom 1");
    $("#target").append($("#source")); //or some other setup that manipulates the widget DOM
    eval($("#source script").text();
</script>

and "_myWidgetContent.cshtml":

<div id="source"></div>
<script>
    alert("dynamic dom 2");
    //Something specific to this partial view
    $("#source").click(function() { alert("clicked") });
</script>
Lovbjerg
  • 1
  • 4