Scroll to footer ↓

Serving iOS retina startup images for the new iPad

|

In the past we have touched on how to serve iOS startup images for full screen web apps using both CSS media queries and JavaScript techniques. Now that the latest iPad sports a retina calibre display, we must again look at the best way to serve startup images in our web apps.

Given the potential bandwidth cost incurred by downloading such large images (2048 x 1496 landscape, 1536 x 2006 portrait), the most advisable technique is to use JavaScript to serve only the assets a device requires (rather than downloading every asset, which would happen if we used media queries). This can be done using the following code snippet in the <head> of your page.

<script>(function(){
var p, l, r = window.devicePixelRatio;
if (navigator.platform === "iPad") {
    p = r === 2 ? "img/startup-tablet-portrait-retina.png" : "img/startup-tablet-portrait.png";
    l = r === 2 ? "img/startup-tablet-landscape-retina.png" : "img/startup-tablet-landscape.png";
    document.write('<link rel="apple-touch-startup-image" href="' + l + '" media="screen and (orientation: landscape)"/><link rel="apple-touch-startup-image" href="' + p + '" media="screen and (orientation: portrait)"/>');
} else {
    p = r === 2 ? "img/startup-retina.png": "img/startup.png"; 
    document.write('<link rel="apple-touch-startup-image" href="' + p + '"/>');
}
})()</script>

You can test this code out using the example here.

It would be nice if Apple would provide a better way to do this rather than having to rely on JavaScript. Perhaps in the future we will be able to have the option to use vector formats, such as SVG for startup images?

Like this article? Share on Twitter.

Send to Instapaper.

Scroll to footer ↓

Tap.js - A lightweight 'tap' event JavaScript plugin

|

While the W3C touch events API is quite flexible in its low-level implementation for dealing with multi-touch interaction, it means that the higher level gestures we use daily in our web apps can take quite a lot of work for developers to implement themselves, even down to the basic 'tap' event. Many mobile web frameworks already offer support for such gestures, but if your app does not use or require such a framework, something as simple as a 'tap' plugin can become a necessity on nearly every mobile project.

Tap.js is a simple, lightweight plugin for a common 'tap' gesture. It has no dependencies and can be used just like any regular JavaScript event, along with standard event listener syntax. It also falls back to using regular mouse events when touch is not supported, so you don't need to manually feature detect or provide two different event types in your code.

Setup

First, include the main JavaScript file in the <head> of your HTML document:

<script src="tap.js"></script>

Next create a new Tap instance, passing the element you want to use:

var el = document.getElementById('my-id'),
    myTap = new Tap(el);

You can then start listening for 'tap' events using the regular JavaScript event listener syntax:

el.addEventListener('tap', tapDidOccur, false); 
function tapDidOccur (e) {
    //your code
}

You can stop listening for tap events like so:

el.removeEventListener('tap', tapDidOccur, false);

Tap.js is open source and available on GitHub.

Tested browsers

Like this article? Share on Twitter.

Send to Instapaper.

Scroll to footer ↓

Different ways to trigger touchcancel in mobile browsers

|

The touchcancel event is often neglected when building touch–interfaces using JavaScript. Historically, browsers vendors have never really published documentation detailing the circumstances as to when this event gets fired, and hence it has always been associated with a level of obscurity by many developers. The aim of this post is to try and shed some light on the matter.

Apple originally created touch events for iOS Safari, which have since been adopted as a basis for the W3C Touch Events Specification. The original Safari DOM Additions Reference documentation provides very little information on touchcancel:

Sent when the system cancels tracking for the touch.

Thankfully, the W3C Touch Events specification gives us a bit more detail, including some situations where the event should occur:

A user agent must dispatch this event type to indicate when a touch point has been disrupted in an implementation-specific manner, such as a synchronous event or action originating from the UA canceling the touch, or the touch point leaving the document window into a non-document area which is capable of handling user interactions. (e.g. The UA's native user interface, plug-ins) A user agent may also dispatch this event type when the user places more touch points on the touch surface than the device or implementation is configured to store, in which case the earliest Touch object in the TouchList should be removed.

From this description we can begin to understand touchcancel with regard to browser behaviour a little better. It not only helps the browser keep accurate references to active Touch objects in the TouchList, but it can also be used by developers to carry out UI specific tasks, such as reseting any variables used during touchstart or touchmove. In the event that the system cancels tracking and the touchend event does not fire, touchcancel saves the day.

But when do browsers actually trigger this event? To start investigating, it was necessary to build a simple event logger to track all touch events. This test features a touch-area and logs the last touch event fired, making it easier to see exactly what kind of interactions result in a touchcancel. Try it out in your browser and see how many ways you can manage to trigger the event.

What follows is a list of findings for various mobile web browsers. It is interesting to note that touchcancel is not very predictable, especially on Android (2.3.5). But even on iOS5 you cannot always correctly guess what will trigger a touchcancel.

iOS Safari 5.0.1 (iPhone 4 & iPad 1)

Actions that do trigger touchcancel during a touchstart or touchmove event:

Actions that do not trigger touchcancel during a touchstart or touchmove event:

In some of these circumstances the browser will simply fire a touchend instead of touchcancel. In others situations (such as the old–style popup notifications), touchstart or touchmove will remain active (even while the content is hidden underneath the notification), until the user releases their finger, at which point touchend is fired.

Notable iOS5 system functions that will not activate during a touchstart or touchmove event:

Android 2.3.5 (Samsung Galaxy Y GT-S5363) stock browser

Actions that do trigger touchcancel during a touchstart or touchmove event:

Actions that do not trigger touchcancel during a touchstart or touchmove event:

Notable Android 2.3.5 system functions that will not activate during a touchstart or touchmove event:

Opera Mobile 11.50 for Android was also tested. While the browser appears to have support for touchcancel, it does not actually seem to fire the event. In the case of pressing the home button for example, Opera Mobile appears to immediately fire touchend instead.

BlackBerry Playbook (1.0.8.6067)

Even though the browser reportedly supports the touchcancel event, it does not appear to get fired at all. Even when pressing the power button to put the device on standby, touchcancel is not fired. The same goes for pressing browser tool bar buttons, or focusing on the address bar. It is also worth noting that when performing a touchstart or touchmove, the Playbook does not respond to any bevel swipe gestures.

Conclusion

It is appears from the tests that touchcancel is still a bit unpredictable. While it is easy enough to identify situations where it gets fired, there are often near identical situations where it does not. While the usefulness of this event is by no means in question, it seems like developers cannot yet rely on this event getting fired in all expected circumstances.

What is clear though, is that you cannot rely on touchend getting fired on every device. You need to write code that deals with touchcancel. This is especially true on the iPad for example, say if you're building a multi–touch app or multi–player HTML5 game. Since there is no way of telling if the user has enabled Multitasking Gestures, your app needs to handle touchcancel gracefully. The unpredictable nature of Android browser also demonstrates the importance of dealing with this event, as failure to do so will likely cause bugs in your UI.

So, next time you're writing your own touch events, don't forget about touchcancel. Despite the varying levels of browser support, it still comes in very handy when dealing with certain unexpected user interactions.

Like this article? Share on Twitter.

Send to Instapaper.

Scroll to footer ↓

Enable CSS active pseudo styles in Mobile Safari

|

By default Mobile Safari disables CSS active pseudo styles on web pages, instead opting for a generic tap–highlight colour on clickable elements. A little known trick is that you can easily re-enable your active pseudo styles by declaring a touchstart event on the page. You don't even need to actually use the event, simply declaring an empty function will suffice.

Just add the following single line of JavaScript to your web page and all your active CSS styles will spring to life.

document.addEventListener("touchstart", function() {},false);

Note: if you do this trick it is also worth removing the default tap–highlight colour Mobile Safari applies using the following CSS rule.

html {
	-webkit-tap-highlight-color: rgba(0,0,0,0);
	tap-highlight-color: rgba(0,0,0,0);
}

You can view an online demo here.

QR Code to launch app

On a related note, there are also some other useful mobile Webkit oddities that can activated by using empty event declarations, like making form labels clickable.

Like this article? Share on Twitter.

Send to Instapaper.

Scroll to footer ↓

Event delegation for touch events in JavaScript

|

Anyone who has experience developing web applications on mobile devices will have no doubt encountered the 300ms delay when firing click events in web browsers such as Mobile Safari. There are some useful standalone libraries and informative articles to help combat this delay, but this post aims to show a super–quick example of how to do simulated 'tap' events on dynamic content using JavaScript event delegation.

A common technique when dealing with click events in dynamic content is to use event delegation to capture events bubbling up through the DOM. This same technique can be applied to touch events, although it is a little more difficult since there are at least four different events to capture; touchstart, touchmove, touchend and touchcancel.

Here's a Gist containing a very basic example of how to capture a simulated 'tap' event:

var tapArea, moved, startX, startY;

tapArea = document.querySelector('#list'); //element to delegate
moved = false; //flags if the finger has moved
startX = 0; //starting x coordinate
startY = 0; //starting y coordinate

//touchstart			
tapArea.ontouchstart = function(e) {

	moved = false;
	startX = e.touches[0].clientX;
  	startY = e.touches[0].clientY;
};

//touchmove	
tapArea.ontouchmove = function(e) {

        //if finger moves more than 10px flag to cancel
        //code.google.com/mobile/articles/fast_buttons.html
	if (Math.abs(e.touches[0].clientX - startX) > 10 ||
      	Math.abs(e.touches[0].clientY - startY) > 10) {
    		moved = true;
  	}
};

//touchend
tapArea.ontouchend = function(e) {

	e.preventDefault();

        //get element from touch point
	var element = e.changedTouches[0].target;

        //if the element is a text node, get its parent.
	if (element.nodeType === 3) {	
		element = element.parentNode;
	}

	if (!moved) {
                //check for the element type you want to capture
		if (element.tagName.toLowerCase() === 'label') {
                        alert('tap');
                }
	}
};

//don't forget about touchcancel!
tapArea.ontouchcancel = function(e) {

        //reset variables
	moved = false;
	startX = 0;
  	startY = 0;
};

Notice here it is useful to make use of touchmove to detect any finger dragging gestures that might occur after a touchstart has fired. This way it is easy to cancel the 'tap' on touchend if desired.

Update: Originally this article was using the function document.elementFromPoint(x,y) to get the element target in ontouchend. A few people have kindly pointed out that you can actually just use e.changedTouches[0].target or even e.target to get the same result for a simple 'tap'. It should be noted however, that the target attribute in this case always refers to the originating element, so if you do need to reference the element a finger might have moved on/off during touchmove or touchend, you would still need to use:

document.elementFromPoint(e.changedTouches[0].clientX, e.changedTouches[0].clientY).

Any questions, comments or suggestions feel free to leave one on the Gist. Life would be so much easier if we had a native 'tap' event in mobile browsers!

Like this article? Share on Twitter.

Send to Instapaper.

Scroll to footer ↓

WKSlider - A touch slider UI JavaScript plugin for Mobile Webkit

|

I've just released another JavaScript plugin for Mobile Webkit, this time a simple to use touch–based slider UI plugin. So far I've tested it successfully on iOS, Android and Opera Mobile 10.1.

Check out the online demo, or alternatively grab the source direct from GitHub.

Like this article? Share on Twitter.

Send to Instapaper.

Scroll to footer ↓

Using HTML5 localStorage as a fallback for offline form submission

|

I recently put together a working demo for a client showing how you might use HTML5 localStorage as a fallback for form data submission when a mobile device is offline without signal. I've uploaded a working demo for anyone interested and the full source code is over on GitHub.

The demo uses window.navigator.onLine to listen for a boolean value of true or false, determining whether the device is online or offline. When online, the app makes a regular XmlHttpRequest and sends data to a server. If the device loses signal and goes offline, the app queues up and saves submitted data to localStorage as a JSON string. When online connection is next restored, the app then re-sends the queued data to the server automatically. Once the data has been sent successfully, the item is then removed from localStorage.

I won't go through all the code line by line as it's reasonably straight forward, but feel free to take a closer look on GitHub. Many Webkit based smart phones now support window.navigator.onLine, as does desktop Safari or Firefox. To test, try enabling flight mode on your phone, or disable wifi on your laptop once the app is open.

Like this article? Share on Twitter.

Send to Instapaper.

Scroll to footer ↓

WKShake - 'Shake to Undo' JavaScript plugin for iOS Mobile Safari/Webkit

|

I've created a little JavaScript plugin that emulates the familiar 'shake to undo' gesture found in many iOS apps, so you can now use it in your web applications on iOS 4.2.1 onward.

Check out the online demo, or alternatively grab the source direct from GitHub.

Note: WKShake has since been renamed to shake.js.

Like this article? Share on Twitter.

Send to Instapaper.

Scroll to footer ↓

WKTouch - Multi-touch JavaScript plugin for Apple iOS

|

I've just released a little JavaScript plugin called WKTouch up on github. It's a tiny plugin for Apple's iOS Safari browser, enabling multi–touch drag, scale and rotate on HTML elements.

Using the plugin is pretty simple. Firstly, include the main JavaScript file in the header of your HTML document:

<script type="text/javascript" src="WKTouch.js"></script>

Next, add the following CSS to your stylesheet. Please note, your HTML element must use absolute positioning:

.touch {
    -webkit-user-select: none;
    -webkit-tap-highlight-color: rgba(0,0,0,0);
    -webkit-text-size-adjust: none;
    -webkit-touch-callout: none; 
}

#element1 {
    position: absolute;
    left: 10px;
    top: 10px;
    height:120px; 
    width:120px;
    background-color: blue;
}

Now add the class name and id to your HTML element:

<div class="touch" id="element1"></div>

Finally, in the head of your HTML file, include the following JavaScript to create a new instance of the plugin, making sure to pass it the id of your HTML element:

<script type="text/javascript"> 
window.onload = function() {                     
    var element1 = new WKTouch('element1').init();       
};
</script>

That's it! You can view a fully working online demo here using multiple instances of the plugin on one page. You can also download or checkout the source over on github.

Like this article? Share on Twitter.

Send to Instapaper.