A few days ago, Chad mentioned that creating a mobile experience for your users does not have to come at a steep cost if you take advantage of the mobile web. The devices that are on the market today have full-featured browsers—they fully support JavaScript and CSS. If you’ve started researching what it would take to get from mobile web to native app, you’ve likely come across PhoneGap—a framework allowing you to wrap a mobile web app and deploy it as if it were written natively.
We’ve done quite a bit of work in PhoneGap lately and the question inevitably comes up: are apps built in PhoneGap “slow”? A quick Google search returns a page full of reasons that would keep you from wanting to pursue using web technology in your next app. Before you write it off, however, we’ve come up with a few tips that have drastically improved our own PhoneGap apps.
Test on the Device From The Start
The simulators are great at determining whether snippets of code will work or not, but they do nothing to emulate the actual performance of the device. Take it from us: there is a huge difference. It would be worth your while to test on a handful of devices as older devices may see even further impacted performance. These older devices act as great lowest common denominators, don’t cost much used, and have an added benefit of helping to reduce ewaste.
Listen for ‘touchstart’ instead of ‘click’
This is probably the most simple, yet most noticeable change you can make. There is a ≈400ms delay between the user interaction and when the “click” event is fired. While listening for touchstart instead of click does nothing to boost the performance/efficiency of your code, it will make the app respond faster and perception is everything.
Equally important is a responsive UI. When the user taps something, the app should immediately respond in some way. In most cases, simply applying a class (e.g. “active” to highlight the tap) to the selected item is instantaneous, but we found a few cases where some devices would get hung up during a larger JavaScript project. A great way to ensure a responsive UI despite the occasional JavaScript hang up is by using the :active pseudo-class selector in CSS. This style is applied immediately to an element when a user has his or her finger on that element.
1 2 3 4 5 6 7 | a:active, a.active { background: #AAA; } element.addEventListener('touchstart',function(e) { e.currentTarget.className = "active"; }); |
See a Demo
(Check it out on your iOS, Android, or BlackBerry 6 device; BlackBerry OS 5 and older, Palm, Windows Mobile 7, and Nokia do not have the touchstart event as of writing this.)
A word of warning when listening for touchstart: You will need to strictly manage how the user can interact with the UI—this means disabling and enabling items when appropriate. Devices like the iPhone can detect more than one ‘touchstart’ event at the same time (try pressing the bottom two buttons in the demo at the same time, then try pressing the top two at the same time).
Update: In the comments, Ciaran made an excellent point that I totally forgot to address: how touchstart can cause problems when your application requires scrolling. When you touch the screen to scroll, it triggers a touchstart, producing undesired effects if you’re listening for touchstart for user interaction. We’ve worked around this by listening for a touchstart, touchmove, and a touchend. If the touchstart and touchend occurred without a touchmove, then it was a tap. It is reinventing the click event, but without the delay.
Update 2: Here’s a little demo of how we’re using touchstart, touchmove, and touchend to simulate a “tap” instead of relying on the click event. The item turns grey when its been touched, and red when it has been selected. Notice how Item 2 (the only item using the “click” listener) has a significant delay between being touched and selected. I commented the source code in the demo to give some insight behind the logic of the code. This was simply pulled out of one of our projects, so it’s not ready for primetime in your application—it’s meant to give you a push start on implementing it yourself.
See the Demo
(Only works on devices that implement touch events.)
CSS Animations
If you’ve tried to use JavaScript to animate anything in the DOM, you were likely sorely disappointed in the results using mobile devices. It’s not necessarily because the devices are slow to execute JavaScript (although that’s part of it), it’s the reflow and rendering of the DOM elements that causes a slow down.
CSS animations allow you to skip the reflow and render—they can immediately be painted in the window. The key in getting good CSS animations, however, are developing them to take advantage of the hardware acceleration when available (especially in iOS devices). Currently, only -webkit-transform and opacity offer hardware acceleration on iOS devices.
See the Demo
(Check it out on your iOS or Android device.)
The demo shows three blocks that all animate to the right, and then back. The first uses jQuery for animation, the second a CSS transition of the left property, and the third a CSS transition of the translate property. This is a simple example, but already you can notice the performance difference between using JavaScript and the hardware accelerated transform. It may seem like a small difference now, but the difference exponentially increases with every addition to the DOM and each added level of complexity to the animation.
A word of warning: flickering. Test your CSS transitions/animations early and often on the device. They tend to flicker in certain cases. We found the worst offender to be the iPad after using the keyboard or interacting with any input form element.
Optimize your JavaScript
Global Variables
Avoid global variables if at all possible. Besides being good practice, the garbage collector will not automatically clean up any global variables and you will eventually notice an impacted performance.
Memory Management
Help the garbage collector by marking variables for deletion. For example, if you have an initialization function that will only run once, at the end of the function, have it delete itself.
1 2 3 4 | function init() { //application code here delete init; } |
Update: In the comments, Alex and Matt alerted me that I was misusing delete here. I had originally acquired this suggestion from the Apple JavaScript Developer Guide, but using delete is incorrect. delete is for releasing properties of objects and implicitly declared variables. While it seems like using delete actually works within Mobile Safari, it’s not the correct way to do it–and it’s not supposed to work according to the ECMA specification. The goal is to release variables (functions) from the global scope if they’re no longer needed. In this case, this init function likely is landing within the global scope. The best way is to not define this function in the global scope in the first place–JavaScript will automatically garbage collect variables that are no longer in scope. I believe it would also work to assign null to init when you’re done with the function.
JavaScript only has a function scope. Variables defined inside of blocks (e.g.: for loops or if statements) are not scoped to that block—they’re scoped to either the containing function or the global namespace. Try to reuse iterators or temporary variables.
Get into the practice of declaring all your variables with var. If you omit var while declaring variables within a function, the variable is assumed to be part of the global namespace and will not be marked for garbage collection upon return of the function.
Try a Different JavaScript Framework
If you’re using jQuery, it may be contributing significantly to the slowdown. jQuery includes a lot of compatibility for browsers like Internet Explorer 6. This is a lot of extra code being loaded into memory that you do not need. Get to know the devices you’re targeting—what kind of rendering engines are they using? iOS, Android, webOS, and BlackBerry 6 all use some version of WebKit, eliminating the need for (most) compatibility code within frameworks like jQuery. Two great alternatives are XUI or zepto.js. Both have a much smaller footprint (<10kb) and offer much of the same functionality as jQuery.
Concatenate and Minify
Combine all your JavaScript into one file and minify the file prior to distribution. This should improve the loading time of your app. No other resources can be loaded while JavaScript is being loaded and executed. By placing everything into one file, it keeps this pause to a minimum.
JSLint
Running your code through JSLint is a great way to quickly have a “second eye” look over your code and provide some optimization feedback.
Update: Since writing this, I’ve come to appreciate JSHint a lot more.
Update the OS
We’re referring specifically to iOS devices here. There were significant performance boosts in some of our apps when we upgraded our iPads from 3.2 to 4.2. The upcoming iOS 4.3 upgrade this Friday (3/11/2011) boasts up to twice as fast JavaScript execution using the Nitro Engine.
If you’re looking to release to the App Store, you may not have control over the iOS of your users (unless you specify that the app only works on 4.2 or later). But if you’re in an enterprise setting, you definitely have more control of the version of iOS running on the devices.
Split up the Work
PhoneGap provides a wonderful framework for integrating native code through the use of plugins. PhoneGap uses this for interfacing between the different device features such as the accelerometer or camera. However, it is also great for splitting up the work of some of the more processor intensive calculations in your application.
Find the processes which are taking the longest to complete and consider whether it would be worth your while to develop a plugin for this process (e.g. processing/resizing an image). Keep in mind that the plugin will have to be developed for every targeted platform.
Disable Multi-Tasking
Update: This used to make a bigger difference–there’s really no more benefit with the improvements in iOS 4.3 and iOS 5, as well as PhoneGap 1.0. However, we recommend making sure you stop timers when the app moves to the background otherwise they’ll queue up and when your app comes back to the foreground, they’ll all fire at once.
Another tip directed at iOS…and maybe a bit of a controversial one. We’ve found that PhoneGap apps left open in the background for too long don’t always resume as expected. Whether this is due to the fact that it had some memory taken from it while in the background we’ve yet to really explore. However, we’ve found that by preventing the app from resuming where it left off, there was improvement in the performance and reliability of the app. Of course, the huge downside is that the app starts over from the beginning every time (unless you implement your own resume functionality through cookies, for example). Users really expect their apps to pick up right where they left off so implement this tip with care. At the time of writing this, PhoneGap does not really communicate with the web application that it’s switching to the background or that it received a low memory warning in the background. (However, it looks like the upcoming 0.9.5 release of PhoneGap is hoping to include some of these features.)
Update: Use a Client-Side Templating Engine
One of the issues that significantly slows down the performance of an HTML-based app is the complexity of the DOM. (If you want a very technical explanation as to why, check out this post…best to read it after your morning cup of coffee, though). We explored a solution to this problem by using a client-side templating engine and saw significant improvements in our applications. By removing unused elements from the DOM, the time it took update existing elements significantly decreased and we saw an increase in speed and responsiveness in the application.
This is more than just a performance “tweak” however and really covers the overall structure of your application. Thus, it deserved it’s own post: How Well Does PhoneGap Scale?
Conclusion
You’ll notice that most of our tips are related specifically to the efficiency and quality of code in the mobile web app. Non-mobile browsers are usually a lot more lenient of poorly written code because they (often) have the memory and processing power to waste. You don’t have that luxury on a mobile device.
We’ve really enjoyed the flexibility that PhoneGap has allowed us to have while developing a few of our mobile applications. Not to mention the rapid development process from start to finish using web standards. These tips are only some of what we’ve found helpful during our development cycles.
Have you found any other helpful tips? Have any questions about other ways we optimized our PhoneGap apps? We’d love to hear how this has helped you while writing your PhoneGap app!
Are you interested in implementing a PhoneGap app for your organization?






"Developing Better PhoneGap Apps"
Pings
Pingback: links for 2011-03-10 | Visualrinse | Design and Development by Chad Udell
Pingback: links for 2011-03-10 ‹ The Iona Group
Pingback: Mobile Phone Development » Blog Archive » HTML5 Bits and Pieces
Pingback: 对学习移动应用开发的一点建议? | Flash开发者大会
Pingback: Mobile Web开发笔记 | 山维空间