jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
Posted June 9, 2009 at 9:29 AM
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
Newer Post
Ask Ben: Dynamic Form Field Population With jQuery
Older Post
Using Script Tags As Data Contains In AJAX-Powered jQuery
Reader 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.
@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.
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).
Sorry, this is off topic...
Ben, how do you pronounce your last name?
NahDAHL?
NayDELL?
etc....
@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"
Ok, so is it jQuery or the browser that is re-ordering it?
@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).
@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>
@eXcalibur.lk,
Other than losing the context, I'm not finding any functional issues with this, so, at least that's good.
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!!
This means that as long as the meta-data is applied pre-rendering, the context feature should be leveraged. Fantastic news.




