Don't do it Twice - Customizing Your Mapping Site for Small Devices

Note: Recently updated with a few fixes. Most notably I switched mobile device detection from CSS width/height to JavaScript navigator.userAgent. It feels like cheating, but it so much easier. Thanks for the feedback!

I just pushed out a new GeoPortal code release, and among the changes is custom styling for small form factors like cell phones.

I should say up front that my phone philosophy is don’t go native (code) unless you just bloody have to. With a HTML5 web app you can use local data storage, access the GPS, cache parts of the app for offline use, put an icon on the home screen, and run the same basic code across desktop browsers and portable devices. The only things you can’t get to are some of the device API’s, like the camera or the accelerometer. Don’t write your app more than once. Just…don’t.

I should also say up front that this won’t help you if you made your app in Flash (won’t run on iOS) or Silverlight (won’t run on anything). If you went that way, I’m sure you had a very good reason. Irregardless, slap yourself.

The first thing you’ll want to do is drop this in your web page header:

Without that your users will be pressing their faces up to their phones to use your site. The device width on phones is often different than the browser canvas width. The device width may be 480, but that iPhone will report that it has 960 to work with and scale down the site when it loads. Bad. That line tells the browser to set the canvas width to the actual device width and scale it 1:1. This won’t break desktop apps, where the device and canvas widths are generally 1:1 already.

Next we need to tell the browser to load a special style sheet for mobile devices. This was trickier than I anticipated. You’re basically looking at canvas or device size, the those portable devices can be pretty darn big. They also change size on you based on whether they’re in portrait or landscape mode. What I didn’t want is to have larger devices, like iPads, getting the small interface when they do just fine with the full interface.

This might not be the best way to do this, but here’s what I came up with:

1
2
3
4
5
6
<!-- Drop in CSS for mobile if Android or iPhone detected -->
<script>
if (navigator.userAgent.indexOf('iPhone') != -1 || navigator.userAgent.indexOf('Android') != -1) {
document.write('<link rel="stylesheet" href="style/mobile.css" type="text/css" />');
}
</script>

Basically I’m using only screen and max-device-width to set an upper limit to the screen size the CSS will load on, but to get around the pesky iPad I’m setting separate parameters based on screen orientation. There’s probably a better way to do this, but darned if I could find it. After getting various odd fails with CSS detection, I went with some simple JavaScript in the head of the document. Basically it’s checking the user agent string for iPhone or Android (and you could add whatever else you want in there). It also gives the advantage of being able to test by changing your browser’s user agent (via User Agent Switcher for Firefox). And for the official record: reloading a map page dozens of times over 3G on a Droid 2 and an iPad in both portrait and landscape modes while swapping out parameters to find something that works really, really sucks.

Now we’ve got a page that will scale correctly in our handhelds, and a CSS file to load just for them. What you’ll need to stick in your CSS file will be entirely dependent upon your site. Basically you want to stack objects vertically and set the widths to 100%. GeoPortal uses the genius jQuery UI Layout, so it was super easy. I basically tweaked a CSS file I was using for printing and was done. Here’s where you’ll find out how badly you suck at web design. If you embedded a lot of styling in the page, or you (gasp!) used tables for page layout, you are in for a bad day.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/* Layout */
html,
body {
overflow: visible !important;
width: auto !important;
height: auto !important;
}

/* Header */
#header-logo { display: none }

/* Search */
#searchdiv {
width: 100%;
padding: 2px;
left: 0;
top: 0;
padding: 2px;
text-align: center;
}
#searchdiv label { display: none }
#searchinput {
width: 98%;
font-size: 16px;
padding: 2px;
}

/* jQuery UI Layout */
.ui-layout-pane,
.ui-layout-resizer,
.ui-layout-toggler {
display: none !important;
position: relative !important;
top: auto !important;
bottom: auto !important;
left: auto !important;
right: auto !important;
width: auto !important;
height: auto !important;
overflow: visible !important;
}
.ui-layout-pane-south,
.ui-layout-pane-north,
.ui-layout-pane-west,
.ui-layout-pane-east,
.ui-layout-pane-center {
display: block !important;
border: 0 !important;
padding: 0 !important;
background: transparent !important;
}
.ui-layout-pane-west {
clear: both;
float: left;
width: 100% !important;
margin-bottom: 1ex !important;
}
.ui-layout-pane-north {
padding: 0 !important;
margin: 0 !important;
padding: 0 8px 0 2px !important;
}
.ui-layout-pane-center {
width: 100% !important;
height: 290px !important;
overflow: hidden !important;
padding-bottom: 5px !important;
padding-top: 2px !important;
}

/* Map */
#toolbar { display: none }

And that’s it. Now small form factor devices will get a more useful layout, desktop users will keep on trucking, and the code base remains the same. For reference, desktop users going to the same site get this:

You can of course download the code from the project site, or you can visit Mecklenburg County’s GeoPortal to see it in action.