Recently I have been deeply involved in the 1.1 development of jqMobi (core) and jqMobi UI, and here I will try to point out some technical details of what was done, how, and why.
For those of you unaware, jqMobi is a jQuery-like framework - a DOM element(s) wrapper with common functionalities which jQuery’s success as made a standard in the industry.
The main difference is jqMobi focuses exclusively on webkit-based mobile touch devices - iOS, Android 2+ and Blackberry OS6+; So, although not nearly as featured as jQuery itself, it’s performance is highly optimised for these devices.
But we soon realised we would need more. Much more.
We needed perfect smooth scrolling for heavy media content and a better and broader compatibility across devices, specially on native input elements.
We needed improved animations and responsiveness, events, usability.
We needed to put jqMobi on steroids.
I joined the team in March and I was tasked with designing and implementing these improvements.
I immediately turned my attention to the core UI scrolling engine - it needed an urgent upgrade.
I had been reading about the latest native touch scrolling shipped with iOS5 and how some developers at the time had deemed impossible to implement into a UI due to event handling issues. I decided to give it a try.
In a few days we built a proof of concept it could work and a couple of days later we started implementing it into jqMobi’s core.
This is how we started working with the jqMobi guys.
Here is what we’ve done:
Native scroll support
Native scroll involved changing quite of the core UI and scroller plugin.
The scroller plugin was split into two child classes, one managing JS scroll for older devices, and another managing the new native scroll features.
While Native scroll can be achieved only with HTML and CSS, maintaining consistency with the UI, touch events, pull to refresh and “no click delay” was a big challenge, but not impossible.
Scrollstart and scrollend events were added cross-device, pull to refresh feature implemented and panning the page prevented.
Note: For those of you unaware, “no click delay” is a block layer on touch events, allowing the page to stay in place and click event to be delivered immediately thus working around the default 300ms delay on iOS click events - it does, however, also block some native input controls and native scrolling altogether.
Eventually “no click delay” was redesigned from the ground up to accomodate these and other changes, and became the touchLayer plugin, managing native events flow and translating them to the rest of the application as necessary - but not necessarily blocking.
This way we were able to keep the immediate click feature on iOS but still allow native scroll and native events on input controls.
Now, the touchLayer deals with a great number of compatibility issues, including delivering proper resize/orientation events in a clear way to the application and managing to keep the address bar hidden across different devices.
In a way, it became the UI police officer, keeping everything consistent and abstracting most of the cross-browser / cross-device stuff.
Native input Controls
Along the way, a great deal of work was done in keeping as much native interaction with input controls as possible. This allowed for native text input features to regain most native features of the OS such as copy/paste and autocomplete.
The next step was to improve the non-native scrolling plugin to its possible best on older iOS, Android and Blackberry devices.
The current implementation relied on changing the CSS transform on every touchmove and a CCS transition for the momentum scroll. This lead to considerable performance problems when having a large amount of content and images on the scrolled panel.
The problem was the browser was being forced to render every couple of milliseconds (every touchmove) and it “choked” on so many redraws in such a short amount of time leading the whole UI to feel sluggish and uncomfortable.
The solution was achieved by separating the read stack from the write stack and having a kind of “frame rate” on the write process.
Movement information is now cached and recalculated on every touchmove but a separate interval takes care of updating the CSS, still fast enough for the human eye to render it as smooth, but spaced enough so the browser wouldn’t choke.
CSS 3 animations
The issue we encountered with CSS3 animations set on having multiple elements animating simultaneously, sometimes with different timeframes and not necessarily starting in the same exact JS stack (imagine a transition of element X starts on click, and element Y only starts on setTimeout(c,0)).
Thus the CSS3 animation plugin was improved to allow binding element animations. In practice this means the transition callbacks only fire when the last of the (aggregated) transitions finishes, enabling the developer to chain multiple sets of transitions without worrying of time lapses on browser render delays.
We have also added support for setting the transition instance by CSS classes and added the ability to cancel an animation chain - resulting in a failure callback.
Behind the scenes there were also a number of small improvements to core functionalities and APIs.
Note: The way it works internally it is probably not a best standard and can certainly be improved with the use of internal caching or ECMA5 properties in the future, but, for our use cases, it solved a great number of issues.
$.asap(callback [, scope, arguments]) is a setTimeout(callback,0) replacement relying on window.postMessage to deliver a near-instant response. The classic setTimeout has an internal default delay (around 40ms in most cases), while postMessage events act in 1 to 5ms delay, allowing for a much quicker reaction than setTimeout, while still retaining all the same advantages.
Bugfixes and other stuff
The UI plugin was refactored to be compatible with the new features and a number of small improvements were added to the core, selectBox and carousel plugins.
Bugfixes were also mostly focused on device compatibility and fixing memory leaks and stability in some core functionality and plugin objects.
And this was just our part, other developers built a lot of new stuff for the 1.1.
You can check out the awesome result at jqMobi’s github.
On a personal note its been quite an experience and I hope to be able to keep improving and creating stuff like this in the future.
There is still so much more to be done.
Special thanks to Ian Maffet, Diego Perez, Kenneth Lam, Anton Laurens, William Lewis and Jessica Reid who have all, in one way or another, also contributed their work for the features mentioned above.