Using jQuery BBQ to create an instant bookmarkable search with AJAX




jQuery BBQ creates a nice interface for manipulating your url without having to reload the page. This means that you can create bookmarkable content without having to reload the page every time you want to change a url parameter. This is great for creating AJAX based forms that can display results instantly while still being bookmarkable.

A lot of sites use this sort of functionality already. A good example of this is grooveshark, which uses the hash part of a url to store parameters. Drupal also includes jQuery BBQ in its base package. Page reloads take a long time, so in today’s world of responsive web pages and impatient users, putting url parameters in the hash part of a url makes a lot of sense.

Technologies Used:

  • Javscript
  • PHP
  • AJAX + JSON

Demo

Here is a demo built around fetching Gumtree results using the PHP Simple HTML DOM parser.

Libraries used in this demonstration:

Browser support:

This should be supported by all browsers.

Step 1: Set up your PHP page for AJAX returns

Now, for this, you can either use an existing service or roll your own. For this tutorial, I’m going to be using a php file I created to search Gumtree and return a list of ads using the php scraping library I described in my post ‘Scraping a site using PHP’. It’s fairly simple, it uses the main functionality of the PHP simple HTML DOM parser library to get a list of ads then just returns a JSON encoded response.

The way I’m returning the messages is encapsulated in a ‘sendResponse’ function:

function sendResponse( $code = 400, $data = array('message' => 'Invalid input'), $responseFormat = "json" ) {
http_response_code( $code );
switch( strtolower( $responseFormat ) ){
// other response formats here if needed.
case 'json':
case 'application/json':
default:
header( 'Content-type: application/json' );
echo json_encode( $data );
}
}

There are, however, plenty of places that offer APIs you could use if you’re planning on using an existing service, for example Flickr, Amazon, eBay, etc.

Step 2: Set up your HTML page and include the necessary libraries

If your familiar with HTML this should be pretty simple, just include the jQuery and jQuery BBQ libraries:

<script src="js/jquery.min.js"></script>
<script src="js/vendor/jqueryBBQ/jquery.ba-bbq.js"></script>

You can download the jQuery BBQ library from their website.

IMPORTANT NOTE: There is an error when using version 1.9+ of jQuery because they have dropped support for older browsers. In this case, you’ll have to use the non-minified source from a GitHub pull request that includes a bug-fix for the aforementioned problem. Note, if you’ve switched to jQuery 1.9+ then you will have dropped IE 6/7/8 support anyway, this patch just stops javascript errors from popping up.

Once you have the necessary libraries, you can go ahead and create a search input and a results container:

<div id="gumtree-container">
<div id="pagination">
<a href="" data-pagination-limit="1">1</a>
<a href="" data-pagination-limit="5">5</a>
<a href="" data-pagination-limit="10">10</a>
<a href="" data-pagination-limit="50">50</a>
<a href="" data-pagination-limit="0">all</a>
</div>
<div id="gumtree-search-form">
<input type="text" id="gumtree-search" placeholder="Search" />
</div>
<div id="gumtree-listings">
</div>
</div>

Notice I’ve also included some very basic pagination elements. We’ll me modifying these later with BBQ. In my final version I also added a few bits of functionality that allow you to select category, location and price range:

 Search Form

Step 3: Start building using BBQ

BBQ includes a whole bunch of neat functions, including automatic handling of the browser history when changing the hash. This is great as it means we don’t have to deal with it at all!

Some notes on how jQuery BBQ works, to get parameters encoded in your hash value only requires one line of code:

var hashParams = $.deparam.fragment();

hashParams now contains an object of key => value pairs. Now we can create the callback for our search box. I’ve bound it so that pressing the return key will change the hash value. For this exercise, the query parameters will be stored in the variable q. Notice I’ve also set the value of the input box to hashParams.q on the page load so that our query appears there if we come back to the same URL later:

$(function(){
var hashParams = $.deparam.fragment();
$('#gumtree-search').val(hashParams.q).keyup(function( e ){
if(e.which === 13) {
$.bbq.pushState({ q: $(this).val() });
}
}).click(function( e ){ 
$(this).select();
});
});

The function we use here is $.bbq.pushState(). This basically just adds or replaces the variables in the hash part of the url with the ones passed to it when used in the way you see here.

Note I have also set a click handler to automatically select all text when the user clicks on the input box. This makes it easy to type or copy and paste a new query.

Now, as promised, let’s replace our pagination links to something more useful:

var qsObj = $.deparam.fragment();
$('#pagination a').each(function( i ) {
qsObj.limit = $(this).data('pagination-limit');
$(this).fragment(qsObj);
});

This uses jQuery’s $.data() function to read the stored data and just adds it as a hashed url parameter using jQuery BBQ’s fragment function. So simple!

Step 4: Add a ‘hashchange’ event listener

jQuery BBQ implements added support for the ‘hashchange’ event; this event is fired every time the hash part of the URL changes. We can use this to detect any time a user enters a query. This is built into some browsers, but jQuery BBQ makes sure it works the same way across all browsers.

I’ve wrapped the functionality for building my actual layout in a function called doGumtreeListings(). This keeps the meaning of the code nice and clear, but for the moment here is my hashchange callback:

$(function(){
doGumtreeListings();
$(window).bind( 'hashchange', function(e) {
doGumtreeListings();
});
});

NOTE: jQuery BBQ documentation recommends that you do this after the DOM is ready, so I’ve used jQuery’s shorthand domready function.

And it’s really that simple. Your page should update every time you enter a new query now without having to reload and the page is bookmarkable!

Step 5: Add your AJAX functionality

Now we can add our AJAX functionality:

var doGumtreeListings = function() {
$('#gumtree-listings').html('');
var data = {};
// get hash parameters
var hashParams = $.deparam.fragment();
// update with hashed query value if it exists
data.q = hashParams.q || "";
// get our limit
var limit = hashParams.limit || 0;
// our AJAX url
var url = "getGumtreeListings.php";
// get JSON from url
$.getJSON(url, data, function(data){
// loop over returned ads
$.each(data.ads, function(i, ad) {
// our naive limiting functionality, it works for a simple implementation
// but ideally you should limit the amount of results another way.
if( limit == 0 || i < limit ) $('#gumtree-listings').append(makeGumtreeAd(ad));
});
}).fail(function(jqxhr, textStatus, error){
// error handling
data = $.parseJSON(jqxhr.responseText);
$('#gumtree-listings').append(data.message);
});
// We'll update the pagination links here, as they only change
// if our query changes
var qsObj = $.deparam.fragment();
$('#pagination a').each(function( i ) {
qsObj.limit = $(this).data('pagination-limit');
$(this).fragment(qsObj);
});
}
var makeGumtreeAd = function(ad) {
// Build an ad and return it
var $ad = $('<div class="ad" />');
$ad.attr('id', ad.id);
var $adLink = $('<a class="ad-link" />').attr('href', ad.link).appendTo($ad);
$('<img />').attr('src',ad.img).appendTo($adLink);
$('<h3 class="ad-title" />').append(ad.title).appendTo($adLink);
var $adDetails = $('<div class="ad-details">').appendTo($ad);
if(ad.relativetime) $('<span class="ad-relative-date" />').append(ad.relativetime).appendTo($adDetails);
if(ad.type) $('<span class="ad-type" />').append(ad.type).appendTo($adDetails);
$('<div class="ad-description" />').append(ad.description).appendTo($ad);
var $adLocationAndDate = $('<div class="ad-location-and-date" />').appendTo($ad);
if(ad.location) $('<span class="ad-location" />').append(ad.location).appendTo($adLocationAndDate);
if(ad.dateavailable) $('<span class="ad-date-available" />').append(ad.dateavailable).appendTo($adLocationAndDate);
return $ad
}

The code is nicely commented and I shouldn’t have to explain much, but do note I’ve moved the functionality where we change our pagination links into this function. After a bit of styling, we can end up with something like this:

Search Screen
Search Screen

Step 6: Extend, extend, extend

You can use this functionality to create all sorts of great applications. It’s especially good for mobile use as it eliminates the need to send a request for each different page. In reality, this amounts to much more than just one request and will reduce load times by quite a few seconds. Not only this, but we can display loading data or images seamlessly.

Conclusion

Using jQuery BBQ allows us to create responsive applications with bookmarkable pages. The only downside is that using these techniques means that your websites are no longer crawlable by sites such as Google. There are ways around this, but the easiest way and best way is to build a version that doesn’t rely on AJAX first and then gracefully add the AJAX functionality for javascript enabled browsers.

You can find a lot about implementing these solutions on this GitHub page, detailing the best ways to deal with the problem of hashbangs.

One Reply to “Using jQuery BBQ to create an instant bookmarkable search with AJAX”

Leave a Reply