views:

640

answers:

8

What is the best (as in cross-browser) technique to do image replacement in CSS? I am using sprites to do my navigation, but I want the markup to remain SEO friendly. Given the following HTML structure...

 <div id="menu">
   <ul>
     <li><a href="#">Test</a></li>
     <li><a href="#">Tester</a></li>
     <li><a href="#">Testing Testing</a></li>
   </ul>
 </div>

What is the best way to replace the text with a background image using CSS only?

I am currently using this...

text-indent: -9999px;

But, it fails with CSS on, and images off.

+1  A: 

If this is the html:

<div id="menu">
  <ul>
    <li><a href="#" id="home">Home</a></li>
    <li><a href="#" id="about">About</a></li>
    <li><a href="#" id="contact">Contact</a></li>
  </ul>
</div>

And this is the css:

#menu ul li a{
  display: block;
  overflow: hidden;
  text-indent: -9999px;
  background: transparent url(yourpicture.png) no-repeat 0 0;
  width: 100px;
}
#home{
  background-position: 0px 0px
}
#about{
  background-position: -100px 0px
}
#contact{
  background-position: -200px 0px
}

The image would then be 300px wide, and each tab would be 100px wide.

Marius
don't forget `background-position:...` for the sprite offset.
beggs
The background position would be set in another rule, one for each link.
Marius
+1  A: 

The background image is usally applied to the <a> link, giving the entire clickable area an image. To hide the text you can use a very big negative value for text-indent.

Luca Matteis
A: 
#menu ul li a {
    display: block;
    background-image: url(images/someimage.png);
    text-indent: -9000px;
    width: 454px;
    height: 64px;
}

The display:block is important or else your width and height may not look right.

Neil Daniels
I can't think of any way to help avoid your issue if you turn images off but leave css on. That might be a little too cautious and you should probably just assume that images AND css will be available. As a plus though, if you view this on a dumbphone (e.g. a RAZR), your image won't appear, but your text will (and it won't be pushed off the screen).
Neil Daniels
A: 

CSS:

#menu ul li a{
    display: block;
    background-image: url(http://example.com/sprite.png);
    width: 100px;
    height: 50px;
}

#a {
    background-position: <offset for sprite>;
}

#b {
    background-position: <offset for sprite>;
}

#c {
    background-position: <offset for sprite>;
}

HTML:

<div id="menu">
   <ul>
       <li id="a"><a href="#" title="Test">Test</a></li>
       <li id="b"><a href="#" title="Tester">Tester</a></li>
       <li id="c"><a href="#" title="Testing Testing">Testing Testing</a></li>
   </ul>
</div>

Edit: added the link text back in... 'cause it was missed. :-)

beggs
Ummm, what about the text? Do you know anything about SEO?!
Josh Stodola
Check: http://www.mezzoblue.com/tests/revised-image-replacement/and: http://css-tricks.com/css-image-replacement/
beggs
A: 

This is the code I use for replacing logo text with an image while keeping the text in the code but not shown to the user (this is Google approved). View the completed example here:

http://discretiondesigns.com/overflow/imagereplacement/

Here's the full code (images can be found at the above link - images can be varying sizes - the entire image is clickable and changes upon hover):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Image Replacement</title>
<style type="text/css">
<!--
#menu li { list-style: none; }

#menu #a { font: .9em Verdana, Arial, Helvetica, sans-serif; color: #E9E7E0; height: 20px; width: 100px; padding-top: 8px; padding-left: 8px; float: left; }
#menu #a a { background: url(http://discretiondesigns.com/overflow/imagereplacement/a_off.gif) no-repeat left top; height: 20px; width: 100px; display: block; }
#menu #a a:hover { background: url(http://discretiondesigns.com/overflow/imagereplacement/a_on.gif); }
#menu #a span { display: none; }

#menu #b { font: .9em Verdana, Arial, Helvetica, sans-serif; color: #E9E7E0; height: 20px; width: 100px; padding-top: 8px; padding-left: 8px; float: left; }
#menu #b a { background: url(http://discretiondesigns.com/overflow/imagereplacement/b_off.gif) no-repeat left top; height: 20px; width: 100px; display: block; }
#menu #b a:hover { background: url(http://discretiondesigns.com/overflow/imagereplacement/b_on.gif); }
#menu #b span { display: none; }

#menu #c { font: .9em Verdana, Arial, Helvetica, sans-serif; color: #E9E7E0; height: 20px; width: 100px; padding-top: 8px; padding-left: 8px; float: left; }
#menu #c a { background: url(http://discretiondesigns.com/overflow/imagereplacement/c_off.gif) no-repeat left top; height: 20px; width: 100px; display: block; }
#menu #c a:hover { background: url(http://discretiondesigns.com/overflow/imagereplacement/c_on.gif); }
#menu #c span { display: none; }
-->
</style>
</head>

<body>
 <div id="menu">
   <ul>
     <li id="a"><a href="#" title="This is A!"><span>Nav A</span></a></li>
     <li id="b"><a href="#" title="This is B!"><span>Nav B</span></a></li>
     <li id="c"><a href="#" title="This is C!"><span>Nav C</span></a></li>
   </ul>
 </div>
</body>
</html>
Elle
This still fails with CSS on and images off, correct?
Josh Stodola
+1  A: 

In 2008, Google's presentation at An Event Apart made it clear that valid image replacement will not be penalized by Google. See Mezzoblue's post about it

Basically, as long as the image you replace the text with has the same text in it, it will be considered valid and not trying to cheat search engines. How do they determine whether the image is valid or not? I have no idea... OCR? Manual review?

As far as CSS on/images off, there is no perfect solution, all of them require extra non-semantic markup. See the css-tricks link that beggs posted on the different techniques. I personally do not bother with the very small percentage of users who browse with CSS but no images.

Your choice is simple. Extra markup, or don't care about css on/images off.

kmiyashiro
+1  A: 

I just came up with this, it seems to work in all modern browsers, I just tested it then on (IE8/compatibility, Chrome, Safari, Moz)

HTML

<img id="my_image" alt="my text" src="images/small_transparent.gif" />

CSS

#my_image{
  background-image:url('images/my_image.png');
  width:100px;
  height:100px;}

Pro's:

  • image alt text is best-practice for accessibility/seo
  • no extra HTML markup, and the css is pretty minimal too
  • gets around the css on/images off issue where "text-indent" techniques still hide text for low bandwidth users

The biggest disadvantage that I can think of is the css off/images on situation, because you'll only send a transparent gif.

It might be possible to write a little javascript to help out with this, replacing all the image sources with their background-image css properties. But this would only work if the browsers still attaches css properties to elements and then ignores them. I don't know if this is the case or not, I'll have to test it out. You'd also want to develop a javascript-based test to see if css is being applied to the page (maybe check the position of some test element).

btw, I'd like to know, who uses images without stylesheets? some kind of mobile phone or something?

edit:

Based on comment below... inline styles hrm... maybe I should just make a php helper function like <?php echo css_image('image_id','my text','image_url');?> to generate some code like this:

HTML

<div id="image_id" style="background-image:url('image_url')" class="image">
<img src="image_url" class="alt_text" alt="my text" />
<p>my text</p>
</div><!--/#my_image-->

then just attach some CSS in the stylesheet

#image_id{width:*image width*;height:*image height*}

.alt_text{position:absolute;top:0px;left:0px}
.image{display:block;background-position:left top}
.image p{position:absolute;left:-9999em}

it's an older technique that I'm using, not sure where I found it though. It works with CSS on/images off, CSS off/images on, CSS on/images on.

If a user with CSS off/images off visits, they'll see doubled up text. If a search engine spider visits, they'll see alt text and regular text, an intelligent spider could easily identify this for what it is, an innocent image replacement technique.

So, this technique is worst for screen readers, since alt text is read, but these users should be able to skip to the next paragraph, which is why I stuck <p></p> around "my text".

Everyone else with both CSS and images turned off is some kind of bot, right?

David Meister
Thanks! Regarding the CSS off/images on scenario, no that is highly unlikely. But the thing I am concerned about is screen scraping/feeding data. For example, if an external tool parses my markup and wants to re-format it (like an RSS feed), the transparent image will come through. This, of course, can be addressed by using inline styles, but I was hoping that there would be one perfect solution out there. It appears there is not (yet).
Josh Stodola