After a little run in with a Gawker site this weekend that pushed me into a mobile version of their site and wouldn't let me out, I thought I'd share two quick tips, and examples, of how to both auto detect a mobile device as well as allowing mobile devices to run your normal web site.
First, for detection, myself and others have been making use of a free script from Detect Mobile Browser. This little one page site provides scripts in multiple languages including ColdFusion. It's user agent based which means it won't be perfect. If some new device uses a unique user agent in the future then you will need to update, but for quick and dirty nothing beats it. Here's an example of the output from the site.
<cfif reFindNoCase("android|avantgo|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino",CGI.HTTP_USER_AGENT) GT 0 OR reFindNoCase("1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-",Left(CGI.HTTP_USER_AGENT,4)) GT 0><cflocation url="http://detectmobilebrowser.com/mobile"></cfif>
(Note - I may add a space or two in the block above if it renders poorly on the site.) I've used this in a few places and it's been fine for me. That's only part of the issue though. This will work fine if someone comes to your site's home page, but what if they come to a deeper page? You have a few different options here. One is to simply look at the requested page, and the query string, and pass them along to the mobile version. So a request for entry.cfm?id=X means you want to redirect to mobile/entry.cfm?id=x. A site that makes use of cgi.path_info, like BlogCFC, required slightly different code. Here's how Dave Ferguson handled it with BlogCFC:
<!--- this is all wrapped in a CFIF like shown above ---> <cfset urlVars=reReplaceNoCase(trim(cgi.path_info), '.+\.cfm/? *', '')> <cfif listlen(urlVars, '/') LTE 1> <!---NOT AN SES URL---> <cfset urlVars = ''> </cfif> <cfset path = cgi.http_host & ListDeleteAt(cgi.script_name, listLen(cgi.script_name, "/"), "/")> <cfif NOT right(path, 6) EQ "mobile"> <cflocation url="http://#path#/mobile/index.cfm#urlVars#" addToken="false"> </cfif>
Obviously you have a few options here, but I think the critical point is this: Do not bother auto pushing someone to a mobile site if you're going to lose the original context of their request. Supporting this should be considering a required, not optional feature.
And speaking of what's required, I also believe, very strongly, that you should provide a way for folks to leave your mobile site. I recently acquired a Motorola Xoom tablet (long story short - I love it), and it renders web pages really well. On more then one site I get automatically pushed to a mobile version. Most support allowing me to click a link and return to the normal version. Unfortunately, all of Gawker's web sites do not. (But let's be honest, their entire network has been pretty badly redesigned lately so this comes as no surprise. I swear I'm not bitter. Honest.) Here is a simple way to support allowing people to leave your mobile site, again, based on BlogCFC.
First, provide a link with some kind of flag:
<p><a href="http://www.foo.com/?nomobile=1" id="leaveMobileLink">Click Here</a> to exit mobile version.</p>
Then simply look for it in your core Application file:
<cfif structKeyExists(url, "nomobile")> <cfset session.nomobile = true> </cfif>
You can then look for this session variable when detecting mobile...
<cfif not structKeyExists(session, "nomobile") and ....>
In my case, I've used a session variable, but you could use a cookie to have more control over how long the "no mobile" flag persists.
Anyway, I hope this helps out. One thing I'm curious about - and I think it will take some time to really flesh out - is to see what "real" users think about this. If a typical user is pushed to a mobile site, will they get confused seeing a link out? Will they get confused in general if the site doesn't match what they see on the desktop (even if it's much better for their device).