How to change an image on click using CSS alone?

I have an image and when the image is clicked I want to reveal another image below it. I am looking for a simple CSS only solution.

Is that possible?


TL;DR!

input[type="checkbox"] {
    content: url('http://placekitten.com/200/210');
    appearance: none;
    display: block;
    width: 200px;
    height: 200px;
}

input[type="checkbox"]:checked {
    content: url('http://placekitten.com/210/220');
}
<input type="checkbox" />

A Pure CSS Solution

Abstract

A checkbox input is a native element served to implement toggle functionality, we can use that to our benefit.

Utilize the :checked pseudo class - attach it to a pseudo element of a checkbox (since you can't really affect the background of the input itself), and change its background accordingly.

Implementation

input[type="checkbox"]:before {
    content: url('images/icon.png');
    display: block;
    width: 100px;
    height: 100px;
}
input[type="checkbox"]:checked:before {
    content: url('images/another-icon.png');
}

Demo

Here's a full working demo on jsFiddle to illustrate the approach.

Refactoring

This is a bit cumbersome, and we could make some changes to clean up unnecessary stuff; as we're not really applying a background image, but instead setting the element's content, we can omit the pseudo elements and set it directly on the checkbox.

Admittedly, they serve no real purpose here but to mask the native rendering of the checkbox. We could simply remove them, but that would result in a FOUC in best cases, or if we fail to fetch the image, it will simply show a huge checkbox.

Enters the appearance property:

The (-moz-)appearance CSS property is used ... to display an element using a platform-native styling based on the operating system's theme.

we can override the platform-native styling by assigning appearance: none and bypass that glitch altogether (we would have to account for vendor prefixes, naturally, and the prefix-free form is not supported anywhere, at the moment). The selectors are then simplified, and the code is more robust.

Implementation

input[type="checkbox"] {
    content: url('images/black.cat');
    display: block;
    width: 200px;
    height: 200px;
    -webkit-appearance: none;
}
input[type="checkbox"]:checked {
    content: url('images/white.cat');
}

Demo

Again, a live demo of the refactored version is on jsFiddle.

References

  • :checked
  • -moz-appearance/-webkit-appearance

Note: this only works on webkit for now, I'm trying to have it fixed for gecko engines also. Will post the updated version once I do.

Update: the appearance property is now widely adopted, so the use of vendor prefixes is redundant. Horay!


You could use an <a> tag with different styles:

a:link    { }
a:visited { }
a:hover   { }
a:active  { }

I'd recommend using that in conjunction with CSS sprites: https://css-tricks.com/css-sprites/


some people have suggested the "visited", but the visited links remain in the browsers cache, so the next time your user visits the page, the link will have the second image.. i dont know it that's the desired effect you want. Anyway you coul mix JS and CSS:

<style>
.off{
    color:red;
}
.on{
    color:green;
}
</style>
<a href="" class="off" onclick="this.className='on';return false;">Foo</a>

using the onclick event, you can change (or toggle maybe?) the class name of the element. In this example i change the text color but you could also change the background image.

Good Luck


This introduces a new paradigm to HTML/CSS, but using an <input readonly="true"> would allow you to append an input:focus selector to then alter the background-image

This of course would require applying specific CSS to the input itself to override browser defaults but it does go to show that click actions can indeed be triggered without the use of Javascript.