Ask the CSS Guy

CSS image replacement for... images? It makes sense for print.

Sites with dark backgrounds lend themselves well to white or light-colored logos. The result can be nice on screen, but if the site is printed, there can be undesirable results: either the logo doesn't show up, or if it was saved as a transparent gif, it shows with jagged pixelated edges where the edges are meant to blend in with a dark background color.

tees crossed printed logo

One way people get around this is by including two logos on their site, hiding the one for print in the screens css, and vice versa. It works, but it includes twice as many img tags as what is really necessary.

A method I've had success with is calling a print-optimized image in the html, and using CSS to swap out the image with a screen-friendly one.

Compare what happens when you don't do this to when you do.

HTML

<img id="logo" src="logo_print.gif" alt="my bloggy-blog" />

CSS

#logo {
   background:transparent url(logo_screen.gif) no-repeat top left;
   height:0;
   overflow:hidden;
   padding-top: 100px; /* height of logo for screen */
   width: 100px; /* width of logo for screen */
}

The height:0; and overflow: hidden; combo makes sure that I don't see the logo intended for print on the screen. By adding a width and padding-top to that image, I can set my screen-optimized logo as the background-image. The width and padding-top should match the width and height of the image you want to show up.

This came about by reading David Shea's image replacement tests. I like the Leahy/Langridge method of image replacement because it requires no extra markup, but this image replacement technique has some cons (as they all do) when it is used to replace text.

If it has cons, why do you think it's an ok idea?

One usually reads about image replacement to replace text, and issues that typically come up are what to do when images are disabled, when CSS is disabled, or what extra markup has to be included to make the effect work. Since I'm using image replacement technique to replace an image, these become non-issues. If images are off, it's the same effect as not using the technique at all - alt attribute values are shown instead. If CSS is off, the print-friendly logo is likely the favored image to show anyway. Also, no extra markup is needed - we manipulate the existing img tag and nothing else.

Presenting a src for the print-optimized logo in the HTML, even though it doesn't get shown, feels a little weird, though, doesn't it? You might feel better if you set the src for the screen-optimized logo, and then use a rule in the print style sheet to swap out for the print-optimized one. But that doesn't work - remember that printing of background images is not enabled in browsers (by default, anyway).

So I'm sure you've seen plenty of sites that have a dark background with a white logo on them. You may even have one yourself. Go give them print preview and see how it's handled. Sometimes (as in the case of this site), text is an acceptable fallback, but if the logo is set as an image, hopefully it is represented well in print.

Troubleshooting with <base>

As powerful as Firebug is for inserting HTML and CSS on the fly, sometimes you just want to save a local version of the page so that HTML manipulation can be saved and tested across browsers.

I just wanted to share a tip on how easy this is to do. Right click, view source, and save it as an html file. If you're lucky, the page will load right up, bringing in the CSS/JavaScript files that's being used on its original domain.

For some sites, you have to give a little guidance. Like today, I went to wired.com, viewed and saved the html source. You can view the result here.

Yuck, it's missing styles. That's not what we wanted.

But by inserting a little code in head, we can grab everything the page uses from the site. After the opening head tag, insert the following:

<base href="http://www.wired.com/" />

and then resave the file. View the result, and you should see the page just as it is meant to be seen at its original address.

I can modify my local copy to my heart's content, putting new or modified CSS in style tags until I have it just right, then commit the changes to something more permanent. I use this all the time when troubleshooting defects, especially when needing to see how inserting some new element in the HTML will affect IE. Hopefully, it will help some of you, too.

And note that the wired.com example might work for today, but if they ever redesign, this article's example won't mean jack. I apologize in advance.

Checkbox filters with jQuery

Perhaps I'm using delicious.com wrong, but sometimes I wish I had the ability to narrow down my results from a variety of different tags rather than excluding results that don't share the first tag I marked. That's where this exercise came into play.

The technique can be used anywhere. In my line of work (online hotel reservations), I've experimented with this technique for a room selection for online hotel reservations. For instance, hide all but the non-smoking rooms with a king bed, and only show me rooms that I can pay for in advance to get the cheaper price. That sort of thing.

For this article, I'm going to base my examples on delicious.com's bookmarks.

Disclaimer stuff

This isn't so much of a 'teaching' post as it is an 'asking to be taught' post. Despite the danger I can cause, I'm no JavaScript/jQuery guy, and there are plenty of places here that I'm going about this all wrong. In those instances, I need your help. I'll call out some specific questions where I know I need improvement. And if you see additional places, please let me know. In the meantime, don't look to this post as tried-and-true, ready-to-copy-and-paste way to do things, because it's far from it.

The HTML

Let's recreate a delicious bookmark listing and leave out the filters for now. Here's the html for a single listing:

<ul>
  ...
  <li>
    <h4>The Power of WordPress and jQuery: 25+ Useful Plugins & Tutorials</h4>
    <p class="url">http://www.noupe.com/jquery/the-power-of-wordpress-and-jquery-30-useful-plugins-tutorials.html</p>
    <p class="tags"><strong>Tags:</strong> plugin, wordpress, webdesign, jquery</p>
  </li>
  ...
</ul>

In order for this to work, we need a way to get the list items using class names. So the tags are repeated as class names. Also, I'm going to give this particular list a class name so the javascript has something to target later.

<ul class="filterThis">
  ...
  <li class="plugin wordpress webdesign jquery">
    <h4>The Power of WordPress and jQuery: 25+ Useful Plugins & Tutorials</h4>
    <p class="url">http://www.noupe.com/jquery/the-power-of-wordpress-and-jquery-30-useful-plugins-tutorials.html</p>
    <p class="tags"><strong>Tags:</strong> plugin, wordpress, webdesign, jquery</p>
  </li>
  ...
</ul>

We create several of these records and we have ourselves a nice delicious-like page of results.

Let's say it in English

The JavaScript for this is kind of long and boring, not to mention that I'm not doing it right in several places. So instead of reprinting it, I'd rather just say in English what I wanted the JavaScript to do. Writing out the logic in English is always a good practice anyway. So here goes:

When the page loads, look through all the class names of the list items to be filtered, and build a new list with all the unique values of those class names, along with a corresponding checkbox. Being friendly, we'll also include a 'show all' checkbox at the end of the list. Since all items are shown by default, all checkboxes will be shown by default, too. When someone unchecks a box, hide any item that has the class name of that checkbox's value. When rechecked, show any item that has the class name of that checkbox's value.

Mouthful, right? There's much more to come, as I quickly realized that there is more I needed to do with this list.

Working through one issue at a time

Issues I immediately noticed with example 2:

  1. 'jquery' is a common tag to every item. How should that be handled?
  2. When i uncheck 'webdev', an item that has the class of 'resources' also disappears. It's the only item with the class of 'resources', yet it's checkbox stays checked, misleading me into thinking that because it's checked, there must be a listing showing that has it.
  3. It would be nice to alphabetize the filters.

The latter one was the easiest to knock out. I can sort the array of unique class names by adding this line to the JavaScript:

arrayUniqueClasses = arrayUniqueClasses.sort();

Also, I chose to exclude any class that is common to all elements from being in the filter column. (My code is probably cruddy for doing this - would love feedback on this.) For sake of demonstration, I created a paragraph above the filters that states which classes those are common to all list items.

I noticed an additional challenges:

  1. When a filter is unchecked, before hiding the list item, I need to check other classes on the list item to be hidden.
  2. When a filter is checked again, in addition to showing the list item, I need to check all the other filters for every other class associated with a newly shown list item.
  3. For my personal preference, I wanted to add this in: If all other filters are checked, go ahead and recheck the 'show all' checkbox.

To see how I tackled these challenges, I recommend viewing source on the examples. The script is commented with what I'm doing and questions to myself throughout the process.

Questions for people with more jQuery/JavaScript experience than me:

  • In order to have only unique values in my array, I'm using a function called unique(a) (credit: Johan Känngård, http://johankanngard.net/). I tried to use jQuery.unique, but it wasn't working right. If anyone can do the unique part in jQuery for me and point out how it's done, I'd be much appreciative.
  • I'm using what is probably an unreliable method for determining if a tag is common to every result. I count the number of list items, and I count the number of times any class appears. If a class's total occurrences equals the total of all list items, I assume that class is common to all list items. Is there a better way to do this?
  • I'm removing items from my arrays using a removeItems(array, item) function that I got from who-knows-where. Is there a way I can do this in jQuery?
  • Any other things that could be done better?