13th May 2008

Retaining a Dom Object Across Ajax Calls (In Order No Less)

I have been working on a project that displays a list of items that a user can take one of several actions on. Taking an action would change the display state and give the user a message that their action had been successfully completed, or that something went wrong. The user could then close the item and in doing so, update the list of items via an Ajax call.

This worked well enough, however, one of the project managers decided that they did not like the fact that the user had to take an action to update the list by closing one of the items they had acted upon, nor the fact that if they took actions on multiple items, they would have multiple feedback message displayed until they closed one of them triggering the update. So I ended up with the following requests:

  • Taking an action on another item in the list should close any other items that have previously had an action take on it.
  • If the user has less than 10 items in their cue, perform an Ajax call to see if they have any new items that have arrived each time they take an action.
  • The last message they received for the last item they took action on should remain across the Ajax calls.
  • If the item they took action on was in the middle of the list, then it should be in the same spot in the new list.

Now the response I was getting from the Ajax call was being sent into the div that contained the list of items. In other words, the entire list was being replaced. So now I had to figure out how to determine the last item an action was taken on, save it and insert it back into the same location in a new list of items that was replacing the old list.

jQuery to the rescue.

In order to display on the last item an action was taken on, I used a two step process. I used a class to identify items that had actions taken on them called actionTaken. So when an action is taken on an item, I remove all elements with that class and then assign that class to the item the action was just taken on. Since I hide all of the items before assigning the class to the last item I took action on, it essentially hides all of them except for the one I just took action on. This basically looks like:

$('#listItem'+id).addClass('actionTaken'); .

Id in this case is a unique identifier generated in PHP and assigned to the div. So that takes care of only showing the last item a user took action on. This also works in the first case where there is no previous item since the call to remove simply does not find any element and does nothing.

Now I need to figure out how to keep that last item across Ajax calls. Since I now knew that only the last item they had taken action on would be displayed, and that item would have a particular class assigned to it, I could not retrieve that DOM node as follows:

var lastItem = $('.actionTaken').parent().html();

Next I need to determine where this item is located in the list. I do this by retrieving the ID of the item that follows the item of interest in the list.

var insertBefore = $('.actionTaken').parents('tr').next().attr('id');

Now that I have the last item and I know the item that follows it, I take the data I got back from the Ajax call and add it to the DOM.


Finding the item that follows the items we are trying to retain works in most cases except one: if the item in question happens to have been at the end of the list. So I needed to determine if we had found an item following the item being retained. If we did, I insert it back into the DOM structure at the appropriate location (before the item it appeared before in the previous list), otherwise, I just throw it back onto the end of the list.

if (insertBefore ){
$('#itemTable #' +insertBefore ).before('<tr><td>' +lastItem + '</td></tr>');
$('#itemTable').append('<tr><td>' +lastItem + '</td></tr>');

There is more code to this system, but as far as the problems I initially outlined, that’s the relevant code. I now had a system that would only display the last item the user took action on and that item would be retained across Ajax callas and appear in the list at the same location.

Comments are closed.