Tag Archives: Mobile

Handling clicks for mobile platforms [UPDATED]

I’ve been doing a lot of work in Phonegap lately and I really like it.  However, one of the things I don’t like is that click events are really, really slow.  There’s a reason for this.  How does the browser know if it’s a click or the beginning of a move?  The answer to this is to bind your click events to the “touchend” event.

So, in JQuery you would previously do something like this.

1
target.bind('click', callback);

But that introduces the lag.  So I wrote a little function where instead of calling bind() I would call this function instead.  It would determine if it’s a desktop or mobile device and then either register the click event or the touchend event.

However, there is a problem with this.  The touchend call might come at the end of a move, in which case you don’t want to fire the click event’s callback.  So I wrote a little bit of code that checks for a move event and then unbinds the touchend event, storing the callback in the new function called at touchend.  If there is no move event the callback is called at touchend.  If the move event is not thrown the callback is re-bound at touchend so the next time a “click’ event is triggered the event will be there to handle it.

1
2
3
4
5
6
7
8
9
target.bind('touchmove', function() {
  var cb = callback;
  $(this).unbind('touchend', cb);
  $(this).bind('touchend', function() {
    var cbb = cb;
    $(this).bind('touchend', cbb);
    });
  });
target.bind('touchend', callback);

[EDIT]

It turns out that this is wrong.  One of the things that I neglected to see offhand was that this code was fine… IF only one touchmove event was fired.  However, if many were fired the callback would be added for each time touchmove was called.  If your finger was moving a lot, then a lot of calls to the callback would be executed on touch end.

Here is code that actually works properly.

1
2
3
4
5
6
7
8
9
10
11
12
target.bind('touchmove', function() {
  $(this).data('moved', true);
});
target.bind('touchend', function() {
  if ($(this).data('moved')) {
    $(this).data('moved', false);
    return;
  }
  $(this).data('moved', false);
  $(this).trigger('touchendnomove');
});
target.bind('touchendnomove', callback);

What this does differently is simply set a flag if a touchmove event is fired.  Then on touchend it checks to see if the flag has been set.  If not a CUSTOM event is fired.  This is important because we want $(this) to refer to the target when an event is fired.  This way we don’t have to do any magic to get the proper “this” association done and we don’t have the problem of those pesky touchmove events.

Creating a separate mobile layout for a Zend Framework application

I've got a Pre… and Sprint.  Together they make for a pretty poor browsing experience.  More due to the Sprint part I presume since browsing the web is a painfully slow experience.  The Pre has other problems.  That poor browsing experience has extended to the e_eschrade website for me even though there aren't a lot of elements that need to be loaded.  So to get around that I decided that it was time to make my site (mostly) mobile friendly.

Doing that was actually relatively easy.  It isn't perfect since it doesn't automatically know ANY type of mobile phone but I think I have it covered so that many mobile devices can be found.

The way I did it was simply by adding yet another Zend Controller plugin called Esc_Application_Plugin_Mobile and enabled it in my application config by adding the following line.

resources.frontController.plugins[] = "Esc_Application_Plugin_Mobile"

From there I coded it.  Like I said, it's not perfect, and it might not even be pretty, but it kind of works.

class Esc_Application_Plugin_Mobile extends Zend_Controller_Plugin_Abstract
{
    private $_agents = array(
        'mobile'    => array('ipad'),
        'webos'        => false
    );
    
    public function dispatchLoopStartup(Zend_Controller_Request_Abstract $req)
    {
        $uAgent = $req->HTTP_USER_AGENT;
        foreach ($this->_agents as $agent => $negation) {
            if (stripos($uAgent, $agent) !== false) {
                if ($negation) {
                    foreach ($negation as $neg) {
                        if (stripos($uAgent, $agent) !== false) {
                            return;
                        }
                    }
                }
                Zend_Layout::getMvcInstance()->setLayout('mobile');
                return;
            }
        }
    } 
}

What this does is prior to actually dispatching the request it gets the browser user agent and iterates over all the user agents it is designed to catch.  I found based off of a Wikipedia entry on mobile user agents that a great many of them have the word "mobile" in their string.  And since I have a Pre I needed to include the Webos as a string to search for as well.  But there's a bit of a problem and that is that the iPad also has the word "mobile" in it, but it probably has a big enough screen to render the full web site.  So what I did was have it iterate over the agents, but then have an option to negate the finding.  So, in this example, if it finds the word "mobile" it will then look for the word "ipad".  If it can't find it then it sets the layout as mobile.

This, then, is the beauty of the 2-stage view pattern.  All I need to do is create a new layout called "mobile.phtml" and place it in my views/layouts directory and if someone visits the site with a mobile phone it will still print out the content as if it had been requested normally, but will skin it with a much more lightweight skin with less HTML and many fewer images.

As I said, it's not perfect, but it seems to work OK.