jQuery Mobile: In-Page Hash Links

A client of mine had an existing HTML document with a bunch of anchor tags linking to hashes within the page.  Since jQuery mobile uses the hash for it’s ajax loading the #top, #whatWeCollect and so on links would of course trigger a new (non-existent) page to load.

To get around this limitation in jQuery Mobile I wrote a little script.  It will essentially look for a class on the anchor tag and then do a little JavaScript magic to link to the in-document hash location.  Essentially recreating normal browser activity.

$('.ui-page').live('pagebeforeshow',function()
{
	$('.retroHashes').find('a[href^="#"]').unbind('click').click(function(e)
	{
		e.stopPropagation();
		e.preventDefault();

		var $target = $(this).parents('.ui-page').find($(this).attr('href')).eq(0);
		if($target.length == 1)
		{
			$.mobile.silentScroll($target.offset().top);
		}
		return;
	});
});

Essentially, what is going on here is that when a “page” is loaded by jQuery Mobile, I am looking for a wrapper element with the class “retroHashes”. Inside this element is the old [ugly] code I got from my client.

Then I look for all the anchor tags where the href begins with the hash.  If it does I unbind anything that might have been attached to click by other methods.  Not very nice and you might not want to do that step but in my case I was fragging all other behaviors on these links.

I bind new functionality to the click.  stopPropagation() prevents jQuery Mobile from doing it’s ajax load.

The rest is finding the target.  In my case the client was setting the the id of the target element and not the name, so I can actually just use the href attribute of the link as the selector.

I check to make sure that I found a target, and scroll to the offset within the document.

There you have it.  A quick hash link hack for jQuery Mobile.  If people like it I may expand this example to more use cases.

jQuery UI Draggable Revert Callback

While this functionality is built into jQuery UI as of this writing, it is not well documented that you can set a callback function for the revert option of a draggable object.  This allows us to add logic to the revert functionality, e.g. is this droppable object in a position to receive this draggable object in terms of the state of our application?

The simple Answer:

For those familiar with how this should work; you can use a callback in place of the documented boolean/integer value stated in the docs.  Treat your function as if it were being asked “Do you want to revert?”  Return true or false.  You may also be able to return a integer value which would define the length of the revert animation in milliseconds.  I’ve tested the first method but not the second, however I’m pretty sure that it will work either way.

If you’d like a stronger example of this in action, please continue.

Advanced Draggable Revert functionality with a Callback Function:

jQuery UI provides us with a number of useful widgets and behaviors to allow us to build rich user interfaces. In this document I will show you how to better use draggable and droppable by adding a callback function to the default revert behavior.

For the sake of simplicity I like to think of draggable objects as pegs and the droppable objects as sockets.

The revert option of the draggable class can be modified to execute a callback function that allows for greater flexibility in it’s operation. By default the revert option has the following possible values:

revert: ‘invalid’,’valid’,’true’,’false’

Just to briefly co over what each does, the invalid option will cause the draggable object to revert back to it’s previous position (the location it was in before the drag), if the draggable does not get dropped in a valid droppable.

There are a number of cases where we need to provide additional checks in order to determine if an object can be dropped in a specific location after the fact.

Consider the following code for a set of draggable divs given the class “peg”.

$(".peg").draggable(
{
  revert: function(socketObj)
  {
     //if false then no socket object drop occurred.
     if(socketObj === false)
     {
        //revert the peg by returning true
        return true;
     }
     else
     {
        //socket object was returned,
        //we can perform additional checks here if we like
        //alert(socketObj.attr('id')); would work fine

        //return false so that the peg does not revert
        return false;
     }
  },
  snap: '.socketInner',
  snapMode: 'inner',
  snapTolerance: 35,
  distance: 8,
  stack: { group: 'pegs', min:50 },
  stop: function()
  {
     draggedOutOfSocket = false;
     alert('stop');
  }
} );

As you can see that we’ve replaced the usual string or boolean values given to the revert option with a callback function. The functionality is in this form, the same as using ‘invalid’, but it illustrates the concept well, because this is by far the most common usage case for revert.

The anonymous function takes one parameter, I’ve decided on calling it socketObj but the importand bit is that you know that one parameter is sent too the callback. This lone parameter will either be false if the draggable was not dropped into a droppable socket, or it will contain the droppable object if it was successful.

The action taken by revert is determined by what we return from our callback. In the example if the socketObj is false we return true, because we do want to revert position. Conversely if an object was passed to the function then we will return false so that our dragged object will stay where we dragged it.

Note that you can see there is a commented out javascript alert. I placed it there to express that this is a standard jQuery object that has all the usual methods you are used to dealing with such as attr() to read the attributes.

Some common things you might end up doing in this portion of the function would be making additional tests to see if you really want that element where the user dropped it, potentially reverting it (by returning true) even though the drop was valid in the UI sense it might not make sense in your application.

If you have any questions or comments please feel free to make them. I’ll try to provide a more practical real world example of this in action soon.