How to Make your website buttons and links work on the iPad/iPhone without massive customization.
You just completed your latest website for a client. You used jQuery to help eliminate all those pesky browser-specific idiosyncrasies and everything works flawlessly in the dreaded Internet Explorer 7/8/9 as well as all other popular browsers. You have even managed to convinced the client that there really is no need to support versions earlier than IE7.
But when you pull up your new site, the one that tested so well in all other browsers, you are finding out that most of your links and buttons are not working properly. Some work, others don’t and no obvious pattern emerges to explain these weird behaviors. What the..? Hey! a link is a link, right? Well, not necessarily. The one thing that most web designers forget about the iPad is that it has no real mouse, so there is no cursor, which means their is no actual “:hover” state for DOM elements. In fact, there are no mouse events at all. They all get synthesized by the iPad and fed to the apps that are listening for them in order to maintain compatibility across websites. At least that is the theory, but like all theories they rarely work as well in practice.
What is Going On?
You see, your web page is at the mercy of the iPad’s web browser, Safari. It looks at your page and IT decides if a page element is clickable or not before it sends the synthesized mouse event. If it doesn’t think an element is clickable then no touch events are generated which means no mouse events get simulated and passed on to your web page. And if the browser doesn’t talk to your page, then your page will just sit, doing nothing, waiting for some sort of input.
Don’t worry, there is a pretty simple solution. Let me explain what I discovered about the iPad and HTML. I found out through a lot of research, that it appears the iPad is looking at your page elements and seeing if they contain an event property called “onclick”. If this property is not on the element then the iPad simply ignores it – even if the users is furiously tapping the element on their iPad. The iPad has determined that it is not an element that can be interacted with – it doesn’t even try to send the event to the element. It just doesn’t bother. View a demonstration of this here.
It seems that natural links, like the <a> tag have this handler by default when they contain the “href” property. So, these types of links tend to work fine with no further modification. But, other elements like the <div> element don’t have this event property by default. They don’t normally need it simply because a <div> is not a link right? Well, it’s not, but many designers use it as a clickable element that contains an image button, label, or other item that they want the user to be able to interact with. The <a> tag is not the most flexible of elements, and sometimes your design calls for other elements acting like links; not trying to force links to act like other elements.
So, why not do all of your event listeners this way? Well, for one thing pre-version 9 Internet Explorer does not work this way. Only newer versions like IE9 deal with event listeners in this way. So, you would have to have two different methods for each DOM element. One to do it the IE way and the other for most other browsers. And that is the biggest reason to use an API like jQuery – it insulates you from the need to code different ways of doing the same thing for different browsers. But, like all solutions, jQuery can also create some unexpected problems. Because jQuery essentially wraps DOM elements in order to make them more flexible and cross-browser compatible, it also insulates your jQuery listeners from the synthesized iPad mouse events which it needs to handle any mouse event-related actions.
I was not looking forward to being required to handle two sets of events for the same actions. I wondered, instead if it was possible to simply force a bridge between the iPad and the web page to get it to synthesize the appropriate mouse events based on its own translation of its touch events. Could I make the iPad believe that the elements I wanted to be buttons and links, really were buttons and links and therefore, it would communicate with my jQuery event mouse event listeners?
Well, that is exactly what I did. I simply made sure that if the page was being viewed on an iProduct (iPad/iPod/iPhone) that the “onclick” property was appended to each element before the page ran. This way, the event could pass through the jQuery element wrapper and trigger the element’s own onclick event handler, which then triggers the jQuery event handler in the wrapper! Wonderful! View a demo of this effect on here. You will need to visit this link on your iPad to see it properly, otherwise all of the links will work as expected.
Before I show you the actual code I used (which you can also download here), I want to walk you through the logic of the process:
- The page loads into the browser and the function “startUp()” is triggered by the page-ready event.
- I read the user agent string and see if it contains an occurrence of the words “iPad/iPod/iPhone”. Don’t worry about any other identifiers except for these labels, since version numbers will change over time and you do not want your page to break due simply to old age.
- If the page is NOT on an iProduct, then I ignore the next step and simply display the page.
- If the page IS an iProduct, then I use jQuery to get an array of all of the classes of elements that I want to make sure have the “onclick” property.
- After assigning all of the elements an empty “onclick” event, the iPad should now think that it is supposed to pass all simulated mouse events to the page element which can then echo the mouse event to the jQuery event listener for that same element.
- The page should behave as it does in all other browsers that it functions properly in.
It is actually a pretty simple process, but it took me forever to nail down exactly what the problem with my page elements was.
One warning, though. Simply adding the “onclick” property is not a 100% guarantee that things will work perfectly. On occasion, I have noticed that if an element is nested too many times inside of other elements that it does not always behave predictably. In these rare cases, I simply add my event listener to the parent container that my element is nested inside of.
Okay, now the code. Click here to download all the code in a zip file. You will need jQuery (which is included in the zip download) in order to run it properly.