Last week I posted on the PhoneGap blog “Questions And Myths About PhoneGap“. It has a bunch of content, and if you’ve been wondering/ questioning the use of PhoneGap, you should definitely check it out.
What is the difference between PhoneGap and Apache Cordova?
I’ve been working on lots of different projects lately. On several of them, I’ve had the need for a reusable list component. In some cases, it needed to handle a large data set, in others it just needed to be self-contained and easy to use. Out of these projects came MegaList: a reusable list component for jQuery, which I’ve released as open source on Github.
MegaList is a jQuery plugin that creates a touch-enabled list component, capable of very large datasets, complete with data virtualization. It was originally intended for touch-enabled devices, however it also works in many desktop browsers.
For performance optimizations, the list component uses data virtualization techniques, so there are never more list elements in the HTML DOM than what is currently visible on the screen. As the user scrolls through content, the list updates the DOM elements accordingly. This makes scrolling lists of thousands of items extremely fluid. This works in a very similar way to ItemRenderer classes in Flex list and grid components.
You can employ the list component using one of two approaches. One option is to declare the list structure in HTML markup, another option is to specify a dataProvider array, from which the list will create DOM elements.
Sometimes you need a pre-built list that you can reuse. Sometimes you need to scroll through big data sets, and other times you just need component logic kept away from your app logic. It doesn’t fit every scenario, but it certainly fits a few.
Data virtualization techniques allow you to quickly scroll through massive lists, without performance degradation. However, if your app really has 100K list items to scroll through, you should fire your UX designer.
View the “samples” directory to see the scrollable list component in action. All samples are interactive, and scrollable via touch or mouse events, with function event handlers.
Each of these examples can be scrolled using either the mouse or finger, and just tap/click on a row to select it, invoking the selection handler (alert message). On the desktop, you can also scroll with the scrollbar. Note: I originally intended this for mobile – on the desktop, I’ve only tested in Chrome and Safari.
Simple List Created With Inline LI Elements
This is a basic example with a list of 50 LI elements.
Note: These inline/embedded examples are contained inside of iframes – if you mouse-up outside of the iframe, the iframe contents won’t receive the event. If MegaList is used in a page, without a wrapping iframe, you don’t run into this issue. Follow the “View Sample” links above to see them without the iframe issue.
Contrary to my expectations, using CSS3 translate3d is actually slower than using CSS top/left when placing the virtualized content. If you enable CSS3 translate3d and set backface visibility, there is an extremely noticeable performance degradation on both desktop and mobile devices.
I’ve experimented with lots of permutations to get the best performance possible. I’m not finished yet, but I’ve found that you can achieve significantly faster performance of DOM manipulation by removing elements from the DOM, manipulating them, then re-adding them. This is what is done within the updateLayout() method. The <ul> is removed from the DOM, <li> elements are added or removed, and then the <ul> is added back to the DOM. You may see a flicker on rare occasions, but I didn’t find this overly intrusive.
For small data sets, this may not be much advantage – you can get better performance by just using something like iScroll in a <div> containing a <ul>. With large data sets, this is definitely faster.
The more complex the HTML inside of your label function, the slower the animation will be.
The full source code for this component is available on Github. Check out the landing page for API documentation and samples.
I’ve decided to release the source code of this application to demonstrate how you can create interactive drawing experiences for PhoneGap applications. You can access the full source of this application at:
This app was designed specifically for the iPad. The UI is specifically styled for the iPad’s portrait form factor. The code is also specifically setup for touch events. If you try to run this code in the desktop browser, you will get layout and runtime JS errors.
The sketching for this application does work on other platforms (Android, BlackBerry, etc…), however at the time of release, only the HTML5 Canvas on iOS performed well enough to release. This is because the HTML5 Canvas element on iOS is hardware accelerated. The HTML5 Canvas on BlackBerry Playbook OS 2.0 is now hardware accelerated, however, I have not repackaged and tested the app on that platform. The HTML5 Canvas inside of webview elements in Android is not yet hardware accelerated.
Lil Doodle supports multiple touch points, where the “brush images” example, only supports a single touch point.
Lil’ Doodle DOES NOT update the canvas on every touch event – if you did so, you would have runtime performance issues. Instead, it caches the touch locations, and updates the visual canvas inside of a render loop (target 60FPS). Using this approach, I was able to get very good performance, even with multiple touch points. The loop currently uses JS timeouts b/c iOS 5 and earlier do not support requestAnimationFrame.
The Fresh Food Finder is an open source mobile application built using PhoneGap (Apache Cordova) that helps users locate local farmers markets that are registered with the FDA. You can search for specific markets, or find the closest markets to your current location. Check out the video below to see it in action:
It was originally intended to just be a sample application for app-UI with full source available here, but happens to be quite popular and useful in the “real world” as well.
In fact, the Fresh Food Finder made it all the way up to #18 in the iPad “Lifestyle” category on iTunes in the first week of its release, and even made one of the featured apps in the Lifestyle category:
All of the information displayed within the Fresh Food Finder is freely available from the US Department of Agriculture through data.gov. This data set was last updated on April 25, 2012.
The top request that I’ve received for the Fresh Food Finder is improved data, with market schedules. I’ve heard everyone loud and clear, and am happy to say that I have some improved data on the way (including schedules and times), so keep an eye out for it in the not-to-distant future.
The Fresh Food Finder can be downloaded today in the following markets:
The Fresh Food Finder uses the following technologies:
PhoneGap: http://www.phonegap.com – PhoneGap is an HTML5 app platform that allows you to author native applications with web technologies and get access to APIs and app stores.
Mustache: https://github.com/janl/mustache.js – Mustache is a logic-less template syntax. It can be used for HTML, config files, source code – anything. It works by expanding tags in a template using values provided in a hash or object.
The code is organized into the following structure:
assets – This folder contains fonts, images, and CSS styles used within the application.
views – This folder contains UI/Mustache templates. Each template is within a separate HTML file.
When the application loads, all templates are loaded into memory as part of the bootstrapping/startup process. Once all the data and templates are loaded into memory, the UI is presented to the user. The majority of the application logic is inside application.js, all views are rendered from the Mustache templates inside of viewAssembler.js, and all UI styling is applied via CSS within styles.css.
Mustache is a templating framework that enables you to easily separate presentation layer (HTML structure) from application logic and the data model. Basically, you create templates that Mustache will parse and convert into HTML strings based upon the data that gets passed in. I’ll write another post later about Mustache, but it can be extremely useful for larger applications.
If you’ve ever used a Windows Phone device’s web browser, then you have probably noticed the gray highlight that the Windows Phone browser puts on top of the HTML content any time that you click on a link. For static web pages, this isn’t a big deal. It indicates that you have clicked on an item. However, if you are building apps with dynamic content, this can become much more frustrating and can have a negative impact in overall user experience. Fret not, TouchClick.js is here to help!
In PhoneGap applications, you can build experiences that respond to mouse/touch events, which can be more dynamic than what is supported in the device browser. Note: The device browser does not support mouse events. All touch input gets translated into mousedown, mousemove, or mouseup events inside of the PhoneGap container. This gray highlight can become a major pain point in highly-dynamic applications. Luckily, I’ve found a way to get rid of it. Check out the video below to see an example of the gray highlight, as well as the workaround in action:
Through lots of trial and error, I’ve found that the gray highlight is applied to all <a> anchor elements, and any html element that has a “click” event handler or lingering “mousedown” and “mouseup” event handlers. Luckily, you can manage event listeners and add/remove event handlers as necessary so that you minimize the gray box. This approach does not get rid of the gray box 100% of the time, but I think it’s safe to say that it gets rid of it 90% of the time. After applying these techniques, I only see the gray highlight if you tap on multiple clickable items in rapid succession.
Here’s how I was able to get rid of the gray highlight box:
Do not use <a> anchor tags ever.
Assign a “mousedown” event handler to the <span>, <div> or other element that you want to be clickable.
When the “mousedown” event handler is invoked, remove the “mousedown” event handler, save the mousedown input coordinates, and add a “mouseup” event handler to the window object.
When the “mouseup” event handler is invoked, remove the “mouseup” event handler, and then compare the mouse coordinates. If the mouse coordinates have not changed, and its within a reasonable amount of time, you can infer that this should be a “click” event, and invoke an action as desired.
Set a timeout to restore the “mousedown” event handler in an asynchronus operation. If you re-add the “mousedown” event handler inside of the “mouseup” event handler, you will still get the gray box.
To make things even more confusing, if you have dynamic content (changing DOM elements) underneath of your touch input, then you get multiple mousedown and mouseup events invoked on different DOM elements underneath where you touched. This happens even if you only touched the screen once. I was able to intercept mouse events and prevent their default actions to mitigate this behavior.
Once you include TouchClick.js, you add event listeners as you normally would, and it even works with jQuery’s event wrappers: