Blog

4 Steps to Minimizing Rendering Issues in Cordova Applications

Here at Quick Left, we often use Cordova with Backbone.js to build mobile applications for both Android and iOS. This setup takes advantage of the best of both worlds — we get native device functionality through Cordova and the ease of creating complex, interactive websites with Backbone.

This makes it easy to develop quickly, but also makes it easy to overlook limitations of mobile browsers and processors and develop with the modern desktop browser in mind. This can lead to the app working perfectly fine when developing on your desktop browser, but then exhibit significant rendering problems, (e.g., flickering) when testing on devices. In addition, the problems might differ from device to device. We found that native browsers on different mobile operating systems respond differently to content-rich DOMs.

I recently worked on a Cordova/Backbone application in which we experienced some major flickering and rendering problems during development. The root source of the issues were due to DOM reflow — replacing the whole (or almost whole) screen's worth of content while sliding a menu closed via JavaScript. After a bit of experimentation, we were able to produce smooth performance without sacrificing the functionality we wanted. Here are the strategies we took to alleviate rendering issues:

1. Keep It Clean (the DOM, that is)

Our application loads a variable amount of content depending on the item a user chooses to view. It could be just a few bullet points of text, or multiple paragraphs with images or videos. We found that only the most content-heavy pages caused rendering issues. The first step we took was to look critically at our HTML structure. Do we really need this <div> inside of that <div>?

We explored replacing our <li><a></a></li> with just <li></li>. However, for our list views, this made a negligible difference and also would have required us to maintain extra JavaScript to handle the click events (since there were no longer any links). Because we were pulling content dynamically from an external API, we were ultimately limited in how much we could slim down our DOM. Instead, we turned to solutions that were more content-agnostic.

2. Less is Better

A second option we explored was to reduce the amount of content being loaded at one time. For example, on list-views, we load 10 at a time and stop when the device screen is full. Rather than load potentially hundreds of list items, we're loading 10 or 20, and only loading more when the user scrolls down.

3. Beware the Box-Shadow

While box-shadows and other cool CSS3 effects are fun to implement, some of them are a bit too much for mobile browsers to handle. Box-shadows, it turns out, can really slow down your rendering speeds. For our application, we were able to use box-shadows on newer iOS devices, but had to remove them for Android devices.

4. Different Strokes for Different… Devices

Finally, if you're still experiencing rendering problems, it's time to try some CSS 'hacks'. We found that no single hack seems to do the trick for every OS, and even different versions may respond differently (or not at all!). These are some that worked for us:

iOS and Android:

-webkit-transform: translateZ(0);

The null-transform hack! What does it do, you ask? On the surface, it appears that we are using CSS to move an element by 0x, 0y, and 0z — so no movement at all! And that's true — we're not trying to move the element. We just want it to render more smoothly. The real reason we do this is that it forces rendering to be handled by the device's GPU instead of relying on the browser. This drastically improved our rendering on the content-heavy pages of our application for both iOS and Android. Since you don't want your app to hog your user's device GPU, apply this only to the elements that really need the extra rendering power.

Android

On the Android devices we tested, we found that the null-transform trick helped but didn't completely alleviate the problem. Fortunately, adding this trick fixed things:

-webkit-backface-visibility: hidden;

Essentially, this CSS prevents the backside of the element from being rendered when rotated 3-dimensionally.

The above resolved rendering problems on our Android 4x devices, but caused significant new issues on Android 2.3. Rendering on Android 2.3 did not have the initial problems, so we limited the application of the above fixes to not include Android 2x (using classes placed on the DOM based on device type).

Summary

By using a combination of these strategies we were able to build a solution that worked smoothly for all of our target devices without sacrificing performance or design.