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

Posted June 9, 2009 at 9:29 AM

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:

 Launch code in new window » Download code as text file »

  • <!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:

 Launch code in new window » Download code as text file »

  • <!---
  • 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):

 Launch code in new window » Download code as text file »

  • <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:

 Launch code in new window » Download code as text file »

  • <!---
  • 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:

 Launch code in new window » Download code as text file »

  • <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.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page




Learning ColdFusion 9 - ColdFusion 9 tutorials, samples, examples, demos

Reader Comments

Jun 9, 2009 at 1:34 PM // reply »
31 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 »
6,516 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 »
45 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 »
6,516 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 »
45 Comments

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


Jun 15, 2009 at 9:23 AM // reply »
6,516 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 »
45 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 »
6,516 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 »
6,516 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.


Post Comment  |  Ask Ben

Recent Blog Comments
Nov 21, 2009 at 5:15 PM
Using ColdFusion Structures To Remove Duplicate List Values
@Jose Galdamez, Oh heh yeah I didn't paste the whole code. I should have defined the vars -- my bad. It's fixed thou. Thanks. ... read »
Nov 21, 2009 at 4:49 PM
Styling The ColdFusion 8 WriteToBrowser CFImage Output
Great work yet again Ben! Whilst I didn't use this whole code, I copied some of your regex code for a similar problem with the lack of an alt attribute and unescaped ampersands in CFIMAGE for Railo 3 ... read »
Nov 21, 2009 at 1:13 PM
My First ColdFusion Builder Extension - Encrypting And Decrypting CFM / CFC Files
@Ben, Because I am pedantic, I just want to make sure that everyone knows there is absolutely no encryption going on. There is only encoding and obfuscation. The cfencode tool only obfuscates your C ... read »
Nov 21, 2009 at 12:28 PM
Using ColdFusion Structures To Remove Duplicate List Values
@Jody I can't seem to get your code sample to work. If you are still having problems, try this code out and see if it gets you what you wanted. <!--- Comma delimited list with various duplicates ... read »
Nov 21, 2009 at 11:03 AM
Groovy Operator Overloading Does Not Work In The ColdFusion Context
Hi Ben, Thanks for this informative post. Now I am reading ur old posts too ... read »
Nov 21, 2009 at 10:56 AM
HostMySite.com Has The Best ColdFusion Hosting
@Mehul, Yes very nice people, however several downtimes per day which was not acceptable. Hence we had to move out. I am glad you are having good luck with them so far. ... read »
Nov 20, 2009 at 11:32 PM
Five Months Without Hungarian Notation And I'm Loving It
I've used headless camel case for years for not only ColdFusion variables, but also SQL tables and fields... pretty much everything involving code. I also subscribe to the "don't abbreviate and clea ... read »