Category Archives: jQuery

Improving the speed of Ajax GET requests

In my last article I described how to improve the speed of your Rails 3 Ajax Application by returning correct HTTP states and optimizing browser caching.

One thing that still annoyed me was the fact that the browser had to wait for the 304 “Not Modified” response from the server before displaying cached data. Once the response was received it would trigger the success callback. That means the user needs to wait for a response from the server just so that the browser can display cached data…
At that point I remembered reading about Wycats’ jquery-offline. jQuery-Offline has the same interface as jQuery’s native getJSON, but once it receives the response it’ll store the JSON data in the browser’s localStorage. The next time the same Ajax request is triggered the success callback is executed twice. First when the data is loaded from the localStorage (which is very very fast obviously) and the second time when the browser returns a 200 status response. The great thing is, that the success callback won’t be triggered if the server returns a 304 “Not Modified” status code. This is really really neat and makes the UI unbelievably snappy.

jQuery.retrieveJSON( "/lists/1", { options: "in case you need it" }, function( data, textStatus, jqXhr) {
  // process the data. This will be called up to two times.
} );

Unfortunately getJSON doesn’t have a great way to handle errors. Since retrieveJSON uses the same interface it suffers from the same weakness. Luckily jQuery 1.5 introduced the jqXHR object. With jqXHR error handling becomes real easy:

var jqXhr = jQuery.retrieveJSON( "/lists/1", { options: "in case you need it" }, function( data, textStatus, jqXhr) {
  // process the data. This will be called up to two times.
} );
jqXhr.error( function( xhr, errorType, exception ) {
  // do your error handling here.
} );

If you want to use above functionality you need to use my fork of jQuery-Offline because the official version hasn’t been updated yet to return a jqXhr object.

DeliciousTwitterFacebookLinkedInRedditSlashdotTechnorati FavoritesDiggShare
Posted in jQuery | Leave a comment

jQuery droppable and scrollable DIVs

Today I faced some weirdness around scrollable DIVs and the droppable component of jQuery-UI.

So you have a div with fixed height and width and overflow settings so that it scrolls vertically but not horizontally. Let’s call this div “wrapper”. Within the wrapper you find additional div-elements. Actually so many of them that the wrapper starts scrolling. Each of these inner elements is a droppable which accepts the “Drag Me!” div.

If you drag & drop the “Drag Me!”-div within the wrapper you get an alert “dropped”. That’s nice, because it’s expected. What’s weird is that you’ll get the “dropped” alert as well when you drop the “Drag Me!”-div somewhere below the wrapper. If you scroll down to item “Test28″ and drop again below the wrapper, the drop event won’t be triggered. So it looks like while the items are invisible to the user, they are still accessible through the DOM and thus cause that the drop event is fired.

Check out the example on jsFiddle: http://jsfiddle.net/2p56Y/.

Solution 1

One solution is to wrap the code in the function which is assigned to the drop event into this condition:

if ( $(this).position().top < $('.box').height() && $(this).position().top > 0) {
    // code within this condition will only be executed
    // if the draggable is dropped on an item
    // somewhere within the wrapper
}

See the modified example on jsFiddle: http://jsfiddle.net/5NuLa/5/

This solution seems to work in all scenarios I’ve tested, but it looks a little bit like a hack.

Solution 2

Solution #2 is cleaner because it uses functionality of the droppable component.

$('.item').droppable( {
    activeClass: "ui-state-default",
    hoverClass: "ui-state-hover",
    accept: "#draggable",
    tolerance: 'fit',
    drop: function( event, ui ) {
        alert("dropped");
    }
});

If the tolerance option is set to “fit”, the drop event is only triggered if the draggable is placed exactly within the droppable item. In this example it works, but if the if the draggable is bigger than the droppable it won’t work. It’s also not working if the draggable is a lot smaller than the droppable. Code: http://jsfiddle.net/CcthK/1/

Summary

Looks like there’s currently no easy and elegant solution for this problem, but with above approaches you can work around it. I actually asked the question on Stack Overflow. Check out the comments, there are some really good ones.

DeliciousTwitterFacebookLinkedInRedditSlashdotTechnorati FavoritesDiggShare
Posted in jQuery | Leave a comment

Parsing XML with jQuery

There’s nothing easier than parsing XML with jQuery. Assume you get this XML back from a server:

<response>
  <items>
    <item id="1">item 1</item>
    <item id="2">item 2</item>
  </items>
</response>

You can loop through all Item-nodes like this:

var xml = $( xmlString );
 
var itemNodes = xml.find( "item" );
 
$.each( itemNodes, function( index, item ) {
    document.writeln("ID: " + $( item ).attr( "id" ) );
    document.writeln("Content: " + $( item ).text() );
});

Checkout a working example on jsFiddle: http://www.jsfiddle.net/YVRbx/1/

DeliciousTwitterFacebookLinkedInRedditSlashdotTechnorati FavoritesDiggShare
Posted in jQuery | Leave a comment

Rails 3, jQuery UJS and dynamically added forms in Internet Explorer

I guess much more specific a title for a post couldn’t be. Anyways… I’m using jQuery in my Rails projects using the jQuery UJS adapter. Moreover some of the forms are added dynamically to the page.
This should not really be a problem because jQuery UJS relies on jQuery’s live method. In fact, it really isn’t a problem in all browsers… except.. Internet Explorer.

In Internet Explorer for some reason (and maybe just on my specific page), the live method didn’t work with the submit event of the form. The form would just post straight to the Rails application, which is not quite what I wanted. I know there were problems with .live() and a couple of Javascript events in previous versions of jQuery, but according to the doc, that should be all good now.

What I did in the end is this. In jQuery UJS I changed

$('form[data-remote]').live('submit', function (e) {
  $(this).callRemote();
  e.preventDefault();
});

to

$('form[data-remote]').find("input[type=submit]").live('click', function (e) {
  $(this).parentsUntil('form[data-remote]').last().parent().callRemote();
  e.preventDefault();
});

So instead of binding to the submit event, I’m binding a function to the click event of the submit button. That’s not as flexible as the original code, because you need to have a submit button and it wouldn’t work if the submit is triggered from Javascript directly. For my purpose however, the trick does it.

This two line change took me a while to figure out, so I’m really wondering if my current approach of writing Javascript applications with a Rails backend is the best approach. The approach is a mixture of retrieving JSON on the one hand vs. Rails returning Javascript code which gets executed once the Ajax call is completed on the other hand.
Alternatives would be to use Javascript MVC, Sproutcore, Pure MVC or Cappuccino. All have their advantages and disadvantages and I haven’t decided yet which one I like the best…

DeliciousTwitterFacebookLinkedInRedditSlashdotTechnorati FavoritesDiggShare
Posted in jQuery, RubyOnRails | Leave a comment

How to get Rails Flash Messages when using Ajax

Rails provides a very convenient way of transporting short messages from the server to the user: The Flash.

So you can do for example stuff like this:

class LoginsController < ApplicationController 
  def destroy 
    session[:current_user_id] = nil 
    flash[:notice] = "You have successfully logged out"  
    redirect_to root_url 
  end 
end

If you use Ajax however the user will never see this message. You could do another Ajax request in order to update the area in your layout which displays the flash messages, but that’s pretty heavy. Better would be if the Flash messages were returned directly with the Ajax request.

After thinking about that for a while, I thought the cleanest solution for transporting flash messages is, to add them to the response header. So I added this method to my application controller:

class ApplicationController < ActionController::Base
  after_filter :add_flash_to_header
 
  def add_flash_to_header
    # only run this in case it's an Ajax request.
    return unless request.xhr?
 
    # add different flashes to header
    response.headers['X-Flash-Error'] = flash[:error] unless flash[:error].blank?
    response.headers['X-Flash-Warning'] = flash[:warning] unless flash[:warning].blank?
    response.headers['X-Flash-Notice'] = flash[:notice] unless flash[:notice].blank?
    response.headers['X-Flash-Message'] = flash[:message] unless flash[:message].blank?
 
    # make sure flash does not appear on the next page
    flash.discard
  end
end

Now, when doing an Ajax call like this:

var test = $.ajax({
	url: "http://localhost:3000/lists/5/edit",
	dataType: "json",
	type: "GET",
	processData: false,
	contentType: "application/json"
});

you can get the JSON data by calling

var json = $.parseJSON( test.responseText );

And you get get your flash message (assuming you created one in the edit action) by calling:

var notice = test.getResponseHeader("X-Flash-Notice");
DeliciousTwitterFacebookLinkedInRedditSlashdotTechnorati FavoritesDiggShare
Posted in jQuery, RubyOnRails | Leave a comment