jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements

Posted June 9, 2009 at 9:29 AM by Ben Nadel

Tags: Javascript / DHTML

Yesterday, when I was playing around with using Script tags as data containers in jQuery-powered AJAX requests, I noticed some odd behavior in the way that the Script tags were brought across the wire to the client. In my testing, the Script tags were stripped out of the context in which they were originally written and added to the end of the AJAX request. I wanted to see is there was any rhyme or reason to the way in which this swap was executed, so I set up another test page. This one simply loads content from an external page into a DIV using jQuery AJAX:

  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>jQuery And Script Tags With AJAX Data</title>
  • <script type="text/javascript" src="jquery-1.3.2.js"></script>
  • <script type="text/javascript">
  •  
  • // When the DOM has loaded, gather the html data.
  • $(
  • function(){
  • $.ajax(
  • {
  • method: "get",
  • url: "./script2_data.cfm",
  • dataType: "html",
  • success: function( strHTML ){
  • $( "#loader" ).html( strHTML );
  •  
  • // Log updated HTML.
  • console.log( $( "#loader" ).html() );
  • }
  • }
  • );
  • }
  • );
  •  
  • </script>
  • </head>
  • <body>
  •  
  • <h1>
  • jQuery And Script Tags With AJAX Data
  • </h1>
  •  
  • <div id="loader"></div>
  •  
  • </body>
  • </html>

As you can see, when the DOM is ready to be interacted with, I load external HTML content into the target DIV and then log the resultant HTML to FireBug's console.

In my first test, I tried a number of different element combinations including inline, block, and nested display elements:

  • <!---
  • Here, we are going to load various HTML elements, all with
  • emebedded SCRIPT tags. This is to see how / when script tags
  • are stripped out and added to their parent container.
  • --->
  •  
  •  
  • <!--- Plain div. --->
  • <div>
  • <script type="text/plain">
  • Here is a DIV.
  • </script>
  • Here is a DIV.
  • </div>
  •  
  • <!--- Span inside div. --->
  • <div>
  • <span>
  • <script type="text/plain">
  • Here is a DIV / SPAN.
  • </script>
  • Here is a DIV / SPAN.
  • </span>
  • </div>
  •  
  • <!--- Plain span. --->
  • <span>
  • <script type="text/plain">
  • Here is a SPAN.
  • </script>
  • Here is a SPAN.
  • </span>
  •  
  • <!--- LI inside UL. --->
  • <ul>
  • <li>
  • <script type="text/plain">
  • Here is a UL / LI.
  • </script>
  • Here is a UL / LI.
  • </li>
  • <li>
  • <script type="text/plain">
  • Here is a UL / LI.
  • </script>
  • Here is a UL / LI.
  • </li>
  • </ul>
  •  
  • <!--- LI inside nested UL. --->
  • <ul>
  • <li>
  • <ul>
  • <li>
  • <script type="text/plain">
  • Here is a UL / UL / LI.
  • </script>
  • Here is a UL / UL / LI.
  • </li>
  • </ul>
  • </li>
  • </ul>

After the above HTML is retrieved via AJAX and loaded into the target DOM, the resultant HTML is as follows (spacing has been adjusted for readability):

  • <div>
  • Here is a DIV.
  • </div>
  • <script type="text/plain">
  • Here is a DIV.
  • </script>
  •  
  • <div>
  • <span>
  • Here is a DIV / SPAN.
  • </span>
  • </div>
  • <script type="text/plain">
  • Here is a DIV / SPAN.
  • </script>
  •  
  • <span>
  • Here is a SPAN.
  • </span>
  • <script type="text/plain">
  • Here is a SPAN.
  • </script>
  •  
  • <ul>
  • <li>
  • Here is a UL / LI.
  • </li>
  • <li>
  • Here is a UL / LI.
  • </li>
  • </ul>
  • <script type="text/plain">
  • Here is a UL / LI.
  • </script>
  • <script type="text/plain">
  • Here is a UL / LI.
  • </script>
  •  
  • <ul>
  • <li>
  • <ul>
  • <li>
  • Here is a UL / UL / LI.
  • </li>
  • </ul>
  • </li>
  • </ul>
  • <script type="text/plain">
  • Here is a UL / UL / LI.
  • </script>

In this test, the Script tags were all stripped out and placed after the parent-most element in their respective clustering. Once I saw this parent-association pattern, I wanted to run the above test again, but this time with the entire test wrapped inside of a DIV tag, creating only a single parent-most element:

  • <!---
  • Here, we are going to load various HTML elements, all with
  • emebedded SCRIPT tags. This is to see how / when script tags
  • are stripped out and added to their parent container.
  • --->
  •  
  • <!--- Create only a single parent-most element. --->
  • <div>
  •  
  • <!--- Plain div. --->
  • <div>
  • <script type="text/plain">
  • Here is a DIV.
  • </script>
  • Here is a DIV.
  • </div>
  •  
  • <!--- Span inside div. --->
  • <div>
  • <span>
  • <script type="text/plain">
  • Here is a DIV / SPAN.
  • </script>
  • Here is a DIV / SPAN.
  • </span>
  • </div>
  •  
  • <!--- Plain span. --->
  • <span>
  • <script type="text/plain">
  • Here is a SPAN.
  • </script>
  • Here is a SPAN.
  • </span>
  •  
  • <!--- LI inside UL. --->
  • <ul>
  • <li>
  • <script type="text/plain">
  • Here is a UL / LI.
  • </script>
  • Here is a UL / LI.
  • </li>
  • <li>
  • <script type="text/plain">
  • Here is a UL / LI.
  • </script>
  • Here is a UL / LI.
  • </li>
  • </ul>
  •  
  • <!--- LI inside nested UL. --->
  • <ul>
  • <li>
  • <ul>
  • <li>
  • <script type="text/plain">
  • Here is a UL / UL / LI.
  • </script>
  • Here is a UL / UL / LI.
  • </li>
  • </ul>
  • </li>
  • </ul>
  •  
  • </div>

This time, the resultant HTML after the AJAX data injection looked like this:

  • <div>
  •  
  • <div>
  • Here is a DIV.
  • </div>
  •  
  • <div>
  • <span>
  • Here is a DIV / SPAN.
  • </span>
  • </div>
  •  
  • <span>
  • Here is a SPAN.
  • </span>
  •  
  • <ul>
  • <li>
  • Here is a UL / LI.
  • </li>
  • <li>
  • Here is a UL / LI.
  • </li>
  • </ul>
  •  
  • <ul>
  • <li>
  • <ul>
  • <li>
  • Here is a UL / UL / LI.
  • </li>
  • </ul>
  • </li>
  • </ul>
  •  
  • </div>
  •  
  • <script type="text/plain">
  • Here is a DIV.
  • </script>
  •  
  • <script type="text/plain">
  • Here is a DIV / SPAN.
  • </script>
  •  
  • <script type="text/plain">
  • Here is a SPAN.
  • </script>
  •  
  • <script type="text/plain">
  • Here is a UL / LI.
  • </script>
  •  
  • <script type="text/plain">
  • Here is a UL / LI.
  • </script>
  •  
  • <script type="text/plain">
  • Here is a UL / UL / LI.
  • </script>

Now, we're really starting to see the pattern - the script tags are stripped out of the entire AJAX HTML dataset and added after the parent-most element in the AJAX data DOM. If there is no single parent-most element, then as we saw in the first demo, the Script tags are simply added after the parent-most element of the given sub-tree.

It appears that neither the type of nor the position of the parent element has any impact on the way that the Script tags are inserted into the target DOM. As such, I think that the only viable solution for using Script tags as application data containers is to use the REL attribute to store the unique ID of the target node. Of course, this only applies to HTML that is gotten via AJAX - Script tags that are embedded in the original page rendering keep their original context.




Reader Comments

Jun 9, 2009 at 1:34 PM // reply »
45 Comments

Well after playing around with it for a couple of days, I've actually changed my mind - I really like your technique. It has immensely simplified a few things that I was jumping through hoops to get done before, like adding pop-up help text. I used to use all kinds of nasty jQuery hackery to pass the text from the db to a jQuery UI dialog. Now I just dump it in a script tag and then load that script tag into the popup (referencing it by a DOM id).

The contextualization really wasn't that important to the way I would use this - I'm much more comfortable with the paradigm of tagging items (using rel tags or ids) anyway. But the overall concept is still a really nice one.


Jun 9, 2009 at 1:41 PM // reply »
10,743 Comments

@Roland,

Awesome my man; glad you are liking this approach. I really like it, although the context issues threw me through a loop. But, as long as you are cool with the IDs, I think it makes good sense.


Jun 9, 2009 at 9:46 PM // reply »
53 Comments

The script tag being used for data seems rather interesting. I haven't had much time to test this myself, so would like to pose the question: Does the script tag movement happen on the AJAX call or on the DOM insertion?

So what would happen if you took out the AJAX call?

And if it is the AJAX call that is re-ordering; is it possible to encrypt or read as binary to by-pass the parsing of the file? (I am not sure what security issues may arise from this though).


Jun 10, 2009 at 10:24 AM // reply »
1 Comments

Sorry, this is off topic...

Ben, how do you pronounce your last name?

NahDAHL?
NayDELL?

etc....


Jun 10, 2009 at 4:55 PM // reply »
10,743 Comments

@eXcalibur.lk,

Good question - the script movement is actually happening on the DOM insertion. The data that comes back over the wire is kept as-is.

@Ivan,

It's prounounced, "Nay-Dell"


Jun 10, 2009 at 6:04 PM // reply »
53 Comments

Ok, so is it jQuery or the browser that is re-ordering it?


Jun 15, 2009 at 9:23 AM // reply »
10,743 Comments

@eXcalibur.lk,

It's probably the browser. I can't see why jQuery would do that on purpose. Furthermore, I wouldn't be surprised if this behavior was NOT cross-browser consistent (although I have not tested it).


Jun 15, 2009 at 6:58 PM // reply »
53 Comments

@Ben,

I have finally had time to sit down and test it out; and it does look like JQuery is moving it around.

The following script:
<script type="application/javascript">
$(document).ready(
function(){
var html = '<div><script type="text/plain">Here is a DIV.<\/script>Here is a DIV.</div>';
document.getElementById('content').innerHTML = html;
console.log(document.getElementById('content').innerHTML);

$('#contentJQuery').html(html);
console.log($('#contentJQuery').html());
}
);
</script>
</head>

<body>
<div id="content"></div>
<div id="contentJQuery"></div>
</body>
</html>

Outputs:
# <div><script type="text/plain">Here is a DIV.</script>Here is a DIV.</div>
# <div>Here is a DIV.</div><script type="text/plain">Here is a DIV.</script>


Jun 16, 2009 at 8:39 AM // reply »
10,743 Comments

@eXcalibur.lk,

Other than losing the context, I'm not finding any functional issues with this, so, at least that's good.


Jun 19, 2009 at 10:26 AM // reply »
10,743 Comments

After some further testing, it looks like this Script tag shifting was happening ONLY when the html was injected into the window's primary DOM tree. Parsing the raw HTML data (gotten via AJAX) into a transient DOM tree (not yet rendered) leaves the Script tags in their original context!!

http://www.bennadel.com/blog/1616-Scripts-Tags-Get-Moved-Only-During-Window-DOM-Node-HTML-Injection.htm

This means that as long as the meta-data is applied pre-rendering, the context feature should be leveraged. Fantastic news.


Feb 20, 2011 at 6:32 PM // reply »
1 Comments

Hey Ben, I'm having an issue related to this.

I'm loading external content into my page using jQuery, and the external content includes script tags. However, once the content is loaded, it appears that the script tags are completely lost.

Basically, my external content contains flash video that is embedded with SWFObject. When I retrieve this content with jQuery (using the .load() method) i basically have no flash video in the retrieved content.

Any idea how to keep the script tags so they are executed without having to resort to plain ol' html <object> tags?

Thanks!


Jul 4, 2011 at 12:52 PM // reply »
1 Comments

Other than losing the context, I'm not finding any functional issues with this, so, at least that's good.


Aug 12, 2011 at 10:07 AM // reply »
1 Comments

The docs for the ajax function states that:

"If html is specified, any embedded JavaScript inside the retrieved data is executed before the HTML is returned as a string."

This may explain why JQuery re-arranges the order of the script content.


Feb 10, 2012 at 6:18 PM // reply »
1 Comments

encountered this same, what I consider, jQuery bug last week. I'm building a site in which I load some content via AJAX. This content contains Linkedin share button placeholders which Linkedin API needs as script tags. These script tags are moved to the end of the inserted content and therefor the Linkedin share button is rendered in the incoorect position.

Now I do need to inject them so I'm still on the lookout for a workaround as the non standard <in:tag is not working in IE...

Cheers


Feb 10, 2012 at 7:21 PM // reply »
1 Comments

Update! Instead of

  • $(eval(options.insertAfter)).after(data['insertData']);

I now use:

  • var ajaxNode = document.createElement('span');
  • var parent = $(eval(options.insertAfter))[0].parentNode;
  • ajaxNode.innerHTML = data['insertData'];
  • parent.insertBefore(ajaxNode, $(eval(options.insertAfter))[0].nextSibling);

Works perfectly and proves again this is a jQuery issue!

Cheers



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 16, 2012 at 8:18 PM
Best Of ColdFusion 10 Contest Entry - HTML Email Utility
Just found this, looks good! I'm trying to run it on local, it's the 64bit version and I'm experiencing horrible lag. On average the generate.cfm processes the content change in 60-90 seconds. I've ... read »
May 16, 2012 at 6:40 PM
Maintaining Sessions Across Multiple ColdFusion CFHttp Requests
I am trying to integrate this CFHTTPsession into an application that will log into zeekrewards.com to post ads and I am not having any luck. The code works perfectly for logging into other websites, ... read »
May 16, 2012 at 2:44 PM
Creating A Sometimes-Fixed-Position Element With jQuery
Thank you, very useful technique! Worked like a charm. ... read »
May 16, 2012 at 1:58 PM
Movies As A Religious Experience
Acting can, in a way, ruin the movie-goer's experience. I used to be able to get so caught up in movies and their plots, and totally engaged. But lately, I haven't been able to as much with a lot o ... read »
May 16, 2012 at 1:52 PM
The Science Of Optimal Post-Exercise Nutrition
children of this age eat very less vegetables so u can opt for salads they will like it also carrot ,cucumber,onion and as far as pulses are concerned u can boil them ,give him along with mashed rice ... read »
May 16, 2012 at 1:34 PM
Strange ColdFusion JRUN Stack Overflow Error
Hey, Recently I updated my jrun4 using the latest updater 7 and now i am having memory issues :(:(:( any help is appreciated ... read »
May 16, 2012 at 9:56 AM
ColdFusion 10 Beta, Apache Tomcat, And Symbolic Links On Mac OSX
Hi, Now that ColdFusion 10 is out I have stumbled over this as well and I cannot figure out the proper solution. We're running virtual hosts via Apache2; the ColdFusion-applications store their fil ... read »
May 15, 2012 at 6:03 PM
Movies As A Religious Experience
@Ben, I don't know whether you'd consider this a religious observation, but it seems to me, in a sense, movies multiply how many lives we get to have. Each movie is like a little extra life we get ... read »