Different CSS and JS for PC, iPad, and iPhone

October 17, 2010

I’m working on a webapp that will run on PCs, iPad, and iPhone. To get the optimum experience for each, I have some styles and JavaScript that I want to run on each, some that are only for iPad, some only iPhone, and some for a combination of the above. Here’s the patterns I’m using to implement this.

JavaScript is easy: I just use regular user agent detection to determine which platform we’re on.

function iOS() { return null != navigator.userAgent.match(/(iPad|iPhone)/i); }
function iPad() { return null != navigator.userAgent.match(/(iPad)/i); }
function iPhone() { return null != navigator.userAgent.match(/(iPhone)/i); }

CSS is a bit more complicated, but not much. I’ve found recommendations for how to target CSS specifically to iPad and iPhone. But really the iPad one is for all iOS devices, so I end up with an iOS CSS file and an iPhone CSS file:

<link rel="stylesheet" type="text/css" media="only screen and (max-device-width: 1024px)" href="iOS.css" />
<link rel="stylesheet" type="text/css" media="only screen and (max-device-width: 480px)" href="iPhone.css" />

So I put my PC styles in my standard stylesheet. Then, I fill iOS.css with styles that I want on all iOS devices or only on iPad. Then I fill iPhone.css with styles I want only on iPhone; and, if there are any styles in iOS.css that were for iPad only, I overwrite them in iPhone.css. For this to work, I have to list iOS.css before iPhone.css, as I did above.

Dropdown Menu Disappearing in IE7

September 17, 2010

Our site header has two dropdown menus. One of the menus works fine in IE7, 8, and FF. The other one works fine in IE8 and FF, but not in IE7. In IE7, moving the cursor down into the menu results in the menu disappearing. It’s implemented with mouseover and mouseout, so it seems that a mouseout was being sent to it incorrectly.

After brainstorming about the differences between the menu that worked and the one that didn’t, I figured it out. The second menu’s dropdown root element is absolutely positioned, and that element didn’t have a background. Other elements underneath it have backgrounds, but not the root element. And it seems that if the root absolutely positioned element doesn’t have a background, IE7 will treat the whole menu as though it has no background, and send mouseout events to it if you hover over another element that’s beneath it.

Adding a background color to the root element fixed the problem. Unfortunately, we wanted to have translucent PNG rounded corners on the menu, so now the corners have white behind them. Not ideal, but it fixes the IE7 error.

CSS Sprite Best Practices

October 27, 2009

We’re in the process of converting our web site to use CSS sprites to improve frontend performance. To implement CSS sprites, I’d recommend using SmartSprites. It’s a simple build-time tool that allows you to build your CSS using regular separate background images, annotate the CSS files using CSS comments, then run the tool and have it generate the CSS sprite image files and updated CSS files. It’s an excellent tool.

Unfortunately, due to limitations in the CSS sprite concept, SmartSprites can’t work with just any background image usage you already have. Here are a few pointers to keep in mind when building your CSS, to make sure it’s as spritable as possible.

  1. Don’t use CSS sprites pre-SmartSprites. This may sound obvious, but there are some reasons why you might end up with some CSS sprites before you use SmartSprites. For example, our design firm implemented hover states for UI elements as CSS sprites: the same image has normal and hover states, and a hover event just changes the background alignment. Also, we use YUI widgets, and some of their default styles use CSS sprites.

    The reason SmartSprites doesn’t work on existing CSS sprites is that it ignores your existing background positioning declarations. It assumes you have a single background image, includes that image in the sprite, then positions the sprite to show that one image.

    The workaround I used for this was to split out the smaller CSS sprites into individual images, which are then recombined by SmartSprites into the macro sprites.

  2. Separate out “background properties into “background-image.” SmartSprites requires background-image to be on its own line. It’s not too hard to refactor later, but if you can write the styles this way to begin with, you’ll save some time.
  3. Make sure repeating images are as few pixels as possible in the repeating dimension – preferably 1px. In order to get repeating sprites to tile properly, SmartSprites has to find the lowest common denominator of the image size. For example, if you have a 2px repeating image and a 3px one, it can’t just sprite them together, because then the 2px one would have a 1px empty row. And it can’t just repeat the first pixel of the 2px one, because the pattern might not tile properly. So it needs to create a 6px image, repeat the 2px one 3 times, and the 3px one twice. 6px isn’t too bad, but if you add a few more images of different sizes, it can expand out of control.
  4. Whenever possible, make your background images cover the whole element, not just part of it. Don’t create a vertically-expandable div and style a background image to cover just the top of it – instead, create one div that’s the height of that background image, and style that. Remember, a CSS sprite is a bunch of images concatenated together, so you won’t get blank space below it: you’ll get more images. One way you can get around this is to take advantage of SmartSprites’ ability to organize the images either horizontally or vertically. If you append the images horizontally left-to-right, there won’t be anything below that header image, so you can still style it to cover just the top. One case where this won’t work, though, is in repeating sprites. If you have a horizontally-repeating sprite at the top of an element, then it’s difficult to put it in a sprite, because the only place to put other elements is above or below it, and the below ones will show up in the element.

IE Performance Issues Adding Dom Nodes

October 12, 2009

My app has a search screen that fetches the results via Ajax and displays them in a table. Worked fine in Firefox, but in IE there’s a slight processor jump for a second or so. Strange, but not a problem. The users of this app, however, use a remote desktop app to run IE to access it. And when they did the search, their CPU would hit 100% for 1-5 minutes, making the entire terminal completely unusable.

I agonized over different JavaScript fixes: removed a table JS widget, switched off of tables altogether, switched to innerHTML, used DocumentFragments. Nothing worked.

Then, on a whim, I disabled all the CSS files on the app, and it worked like a charm. As I narrowed it down, I discovered that some CSS I was using to emulate frames (non-scrolling header, footer, and sidebar) was the offending code.

Let’s step back and think about that one again. What caused the processor to hit 100% for 5 minutes was not JavaScript, but CSS.

Sigh. So, if you have an issue with IE hitting 100% processor load for any reason, don’t assume that JS is necessarily the problem. Try disabling your CSS and see if that fixes it.

IE6 :hover Support

October 16, 2008

IE6 does not support :hover pseudo-classes on non-anchor elements. Ugh.

There are a few fixes out there. This one uses prototype. I was able to translate it to use YUI Event and Dom pretty easily. Wish I had an open-source agreement with my employer so I could show it here.

Unnecessary CSS Expressions

July 17, 2008

A full-page-body iframe was being sized correctly in IE but incorrectly in Firefox – it seemed to be just the default arbitrary small iframe size. Turns out the iframe had its width and height set by CSS expressions, so that it could fill up the full size of the document, minus sidebars, headers, etc. But CSS expressions aren’t supported in Firefox 2.

The solution for the width was simple, though, because the page design is fixed-width. There’s no reason to include a CSS expression when you can just set the width to an exact numeric value. It’s not like this code is reused in other locations that might have different sizes.

The height did still need to be dynamic, though, so JavaScript was the solution here. The document.body.clientHeight property seemed to be the most reliable way to get the window height, so I created some JavaScript code to recalculate the iframe height on page load and on resize. Just used a simple method to unobtrusively add the onLoad handler, and an analogous method for onResize.

Override, Don’t Troubleshoot

July 14, 2008

Just had an issue where an image was aligning differently within a table cell on two different pages. All the style attributes and markup seemed to be the same.

Rather than spending too much time troubleshooting it, I just explicitly set the alignment on the table cell. Did a similar fix with a font-sizing issue in CSS recently, as well.

It’s probably not a good idea to do hacks instead of troubleshooting a CSS/display bug. But sometimes it can be a good idea to do a non-hack explicit value, rather than troubleshoot. Comment if you disagree =]