Secrets to Selecting Elements Returned from jQuery Ajax Response Strings

Sam Deering

The jQuery selection engine is fast and flexible and allows you to make selections against DOM elements as well as in-memory memory markup strings. When you couple this functionality with the ability to get the full HTML markup from pages throughout your site, you can come up with some interesting ways to re-use content in your web application.

Concepts

Consider an application which includes content on one page that you want to display on another page. Reasons for an approach like this vary, but common scenarios include working with legacy or “black box” systems where you have no control over the server implementation or where you are working with static content. In the end both “black box” and static content circumstances afford you no opportunity to prepare data on the server into typical Ajax response messages (i.e., JSON or XML).

The example demonstrated in this article works to fetch content fragments from static HTML files and display them on another page in the site. The Figure 1 displays a static HTML page that lists movies from multiple categories in the system.

Figure 1: Full Movies Page

While the Movies page has all the films in the system, the home page will only display a subset of the movies to users. Figure 2 shows how the home page renders only the Action films on the page.

Figure 2: The home page displaying only action films. (index.html)

In order to make this scenario work, an Ajax call against the static HTML page is required. Once the response from the Ajax call is recognized by the browser, then a fragment of the page is extracted from the full response by using  jQuery selectors on the markup returned from the static page. The jQuery selection engine is flexible enough to work on DOM elements as well as selecting against an in-memory string of markup – but there’s a catch.

The response from the Ajax call includes the markup of the full HTML of the page, which includes the DOCTYPE element as well as the root HTML element of the document. A response with two root-level elements is not immediately select-able as the jQuery selection engine requires that query targets must have a single root element. This issue is resolved by manually adding a root element to the response string which is done by wrapping it in a logical container like a DIV element.

Code

The code for the movies page is available in Listing 1 which shows how each category of movie is logically contained by a SECTION element with a corresponding ID value.

Listing 1: Movies page (movies.html)

<h1>Movies</h1>
<section>
<h2>Action</h2>
<ul>
	<li>Die Hard</li>
	<li>The Matrix</li>
	<li>Raiders of the Lost Ark</li>
</ul>
</section><section>
<h2>Drama</h2>
<ul>
	<li>A Few Good Men</li>
	<li>The Shawshank Redemption</li>
	<li>Legends of the Fall</li>
</ul>
</section>

The home page is comprised of the code found in Listing 2 which by default includes only a single structural element found in a DIV tag which acts as a shell for the content rendered on the page.

Listing 2: Home page (index.html)

$(function () {
$.get('movies.html', function (response) {
var source = $('
<div>' + response + '</div>
');
$('#movies').html(source.find('#action-container').html());
});
});

The JavaScript on this page begins by registering the jQuery load handler. Once the page loads, a call to the $.get API fetches the movies.html page. The response from this request is the full HTML markup of the movies.html page (including DOCTYPE). Since the raw markup is not initially select-able the string is wrapped with a root DIV element and is then instantiated as a jQuery object, making it ready for processing by the selection engine.

To locate the desired fragment of HTML in the Movies page, the find API is used to query the descendants of the full markup and return only the elements needed for the home page (i.e., the element with the ID of action-container). Once the fragment is found, it’s then injected into the innerHTML of the content host element by using the jQuery html function against the DIV with the ID of movies.

Finally, Listing 3 includes the common styles used in each page to apply some minimal styling to the pages.

Listing 3: Style Sheet (styles.css)

body, html
{
padding:4px;
margin:0px;
}

body
{
font-family:Arial, Helvetica, sans-serif;
font-size:1em;
}

Conclusion

While the need for this approach may be relatively rare, you may encounter times when you want to make an Ajax call to an existing page on your site and only render a targeted part of the elements on the page. The secrets to making this approach work are to wrap the response string in a single logical root element and then use the jQuery find API to extract out only the markup required for the host page.

BIO:

Craig Shoemaker (Twitter | Google+) is a software developer, podcaster, writer and Technical Evangelist for Infragistics. As host of the Polymorphic Podcast, Craig does what he loves most – making contributions to the community and drawing the best out of industry luminaries.

Craig is a Microsoft ASP.NET MVPASP Insider and guest speaker at various developer user groups and tradeshows. Craig is co-author of the Wrox books “Beginning ASP.NET 2.0 AJAX“, “Beginning ASP.NET Ajax“, and CODE Magazine  and Pluralsight author.

In his spare time Craig enjoys looking for a haystack to hide his prize needle collection.

 

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Devin

    Could come in handy… thanks! For the sake of accuracy in case someone tries to cut and paste the code to play around, shouldn’t the sections have ID tags? In the code above, specifically in the HTML shown for “Listing 1: Movies page (movies.html)” the first should really be and the second one potentially . The rest appears to be on target and I always appreciate the information you gather up for us.

  • Craig Shoemaker

    Devin:

    That is correct. There must have been copy/paste error during publishing. I’ll work to get that fixed.

    Thanks!

    Craig