How To Generate CSS Click Events And Thoughts About Whether You Should

When you want a click to change something on your page, you usually reach for Javascript. Adhering to principles of modularity and separating structure, presentation, and behavior we’re supposed to use Javascript for behavior layer. However, methods exist for generating click events using only html and css. What are they and should we use them?

I recently had opportunity to use a couple of these css click events in some demos I developed for a series of responsive navigation articles for Tuts+ and found them useful and simple to implement. Then a month ago I found an article with a roundup of most of the different css click event methods on the Codrops site.

I want to briefly walk through the different methods (I’ll provide links to more detailed explanations in and below each section) and also wonder aloud if we should be using them or in the interest of modular code we should stick to using Javascript for click events.

To grow in maturity is to separate more distinctly, to connect more closely

Structure, Presentation, and Behavior

I’m sure you’ve heard someone, maybe me, talk about writing more modular code by separating structure, presentation, and behavior. This separation of concerns generally leads to less code thats’s more maintainable and reusable. When working on the front end of a website we’re told to stick with 3 technologies.

  • html for structure
  • css for presentation
  • javascript for behavior

Over the years, things that are the domain of Javascript have found their way into CSS. Think transforms, transitions, and animations for example. These changes are certainly to an element’s state and we’ve been told they belong to Javascript. Yet we can and do create those state changes without a scripting language.

The real question is whether or not these methods are any less maintainable than Javascript.

Click events are similar. Without Javascript there are methods for simple things like showing and hiding content or changing the presentation of an element. We can trigger an animation or completely restyle the look and layout of a page all without ever turning to a Javascript onclick event.

These non-js click events are easy to use as I’ll show in a moment, but keep in mind the question of whether or not we should avoid using them in the name of modularity as you read through the rest of this post. If the point of separating structure, presentation, and behavior is more maintainable code, are we giving back some of that maintainability in using these html/css click events.

HTML and CSS Click Events

There are a handful of methods for generating a click event, however they all fall under two general categories.

All the methods below make use pseudo selectors. Some also use html form inputs and their associated labels to trigger the event.

Pseudo Selectos

CSS provides a number of ways to style elements based on their state. I’m sure you’ve been changing links based on :hover since not long after you discovered css. Of course :hover is a temporary change of state. Move your cursor off the element and everything reverts back to it’s original state.

Some pseudo selectors allow us to maintain state for a longer duration.

The :target hack — The idea behind this method is that the :target selector will match an element with an id that’s the same as the hash in the url. If you have a url like www.domain.com/my-web-page.html#alert and in your html have

1
2
3
4
5
<a href="#alert">Click Me</a>

<div id="alert">
  <p>some alarming information here</p>
</div>

you could do something like

1
2
3
4
5
6
7
#alert {
  display: none;
}

#alert:target {
  display: block;
}

And with that you have yourself a click event. A visitor clicks the link which points to the same page with the addition of the hashtag. Your #alert div changes from display: none to display: block. All without the use of Javascript.

One downside is that same link can’t be used to toggle things back to the initial state. It would require a different link that doesn’t include the hash in the url. However there are times when that’s what you want. Think of an accordion menu pattern. You could set up each of the top level items with a different hash that opens its particular submenu while all others close.

There are other downsides. It’s a new url which leads to a page refresh and can throw off browser history. You can only affect the one element, since an id is required. Again there are times when none of these will be an issue and this event is now finding its way into various responsive navigation patterns.

The :focus hack — This works similarly to the :target hack. Here when an html element has focus we can use :focus to match it and then use an adjacent sibling selector to target neighbor elements.

There’s a slight catch in that not every html element can be focused by default, though we can change that by adding a tabindex attribute

1
2
<span tabindex="0">Click Me</span>
<p class="alert">Some alarming information here</p>
1
2
3
4
5
6
7
.alert {
  display: none;
}

span:focus ~ .alert {
  display: block;
}

Notice the use of the tilde ( ~ ) to select an adjacent sibling.

Here we aren’t navigating to another url, however we can only target one sibling element and anything inside it. We still can’t use the same element to click back to the initial state, but since clicking anywhere outside the now focused element removes focus, we can get back to the initial state without a new element.

The :active hack — Once again this is a similar method to the two above that uses the :active state of an element.

1
2
3
<div class="btn">
 <p class="alert">Some alarming information</p>
</div>
1
2
3
4
5
6
7
.alert {
  display: none;
}

.btn:active .alert {
  display: block;
}

Unfortunately the :active state only exists after you click an element and before you release the mouse button, so it’s very short lived. However, in combination with either the :hover pseudo selector or a css transition you can maintain the state longer and do some interesting things.

Ryan Collins offers a demo using :active and :hover to create a clickable drop down menu. Codrops offers a demo that uses a css transition to essentially maintain the state indefinitely.

The pros and cons differ a little depending on what you combine with :active, but as with the methods above it’s an easy, albeit imperfect, way to get a click event under certain circumstances.

Form Elements

Form elements also give us a way to alter states. Forms even come with their own click event, the submit button, but we’re actually more interested in some form inputs here.

The checkbox hack — The idea here is to use a connected form label and checkbox input to toggle the checkbox on and off and once again use an adjacent sibling selector to change the state of another element. We can use :checked to trigger different styles depending on the state of the checkbox .

1
2
3
<label for="toggle">Click Me</label>
<input type="checkbox" id="toggle">
<p class="alert">Some alarming information</p>

Note that the “for” of the label must match the “id” of the checbox input for them to be connected. The css below should now look somewhat familiar.

1
2
3
4
5
6
7
.alert {
  display: none;
}

:checked ~ .alert {
  display: block;
}

Once again we’re targeting an element based on it being an adjacent sibling selector, this time of a checked input.

You probably don’t want the checkbox itself visible so it’s typically positioned off the page, though you can hide the checkbox in other ways should you want.

1
2
3
4
input[type=checkbox] {
  position: absolute;
  left: -999em;
}

The checkbox hack needs a couple of extra fixes for iOS below version 6 and Android version 4.1.2. The iOS fix is to add an empty onclick event to the label and the Android fix is to add a fake animation on the body element.

One advantage of the checkbox hack over the pseudo element selector hacks is we can use the same element to toggle something on and off. Click the label once to check the box. Click it again to uncheck it. This hack seems to be replacing the pseudo selector hacks for this very reason.

The radio input hack — This works much the same way as the checkbox hack, except we can now control more than a single on/off. We can use as many radio buttons as we want and change their state based on which is selected.

A common use is a tab switcher. Louis Lazaris has created a nice example of a tab switcher on Adobe Developer Connection. I’ll send you there instead of reproducing his code here.

With a checkbox a single element is in an on/off state. With radio buttons many elements can be connected with one being on and the others being off. In Louis’ example the “on” tab takes on a different look and displays its content, while the content of the “off” tabs remain hidden.

His post also provides more in-depth examples of the checkbox hack and the focus hack so it’s worth having a look.

Should We Use These Hacks?

I asked above to keep in mind the question of whether or not these hacks make sense given our goal of modularity and separating structure, presentation, and behavior. What do you think? Should we use the non javascript hacks to change the state of elements? Isn’t that behavior? Doesn’t that mean we should use Javascript even if these hacks can be easier to use at times?

I’m not sure I have a good answer at the moment, though I do expect to use the hacks above more just because they do make for simple click events. Then again adding and removing a class on an element triggered by a Javascript click event is also pretty simple and allows us to change state just as all the methods above.

One con of all these hacks, which I haven’t yet mentioned is they don’t work in every version of every browser. For the most part that means older versions of IE as you probably expect. Fallbacks typically employ Javascript so….

About a year ago Kevin Dees argued in an article on the Treehouse blog that this whole separation of structure, presentation, and behavior is dead or dying. CSS is getting structure in the form of pseudo elements and it’s getting behavior in the form of pseudo selectors and animation. Let’s face it. If these things are in css, we’re going to use them.

The real question is whether or not these methods are any less maintainable than using Javascript. I can’t claim to have thought about it exhaustively, but they don’t seem to be any less maintainable on the surface. Perhaps it’s something worth a deeper look another time.

How about you? Have you been using any of these css click events and if so what do you think? Would you use them again?

Download a free sample from my book, Design Fundamentals.

22 comments

  1. Had no idea these hacks existed! I came here because I´m currently studying your Responsive Navigation Off Canvas tut which by the way is awesome. Thanks to you I´m a 1 percent better front-end dev today (trying to reach 365 percent by the end of 2013)

    • Thanks Juanfe. These hacks are nice, aren’t they. I only recently discovered them myself and learned about them mainly by using them on the responsive navigation series. I’m glad you liked the off canvas article.

      1% every day adds up. :)

  2. That’s incredible to do,

    I never thought of generating click events using pure CSS, In Fact I started learning Java Script for that, though it is a good thing to learn,

    Thanks for the hacks

    • CSS click events were relatively new to me as well when I first wrote this. They aren’t a perfect solution, but they are pretty cool under the right circumstances.

  3. Thanks for the nice roundup. I figured I can use the target hack to switch a div on *and* off — sort off. I put the very same <a href="#thingOn" into the div switched on, just with <a href=”#thingOff” rel=”nofollow”> and place the switched on div exactly over the parent such that it looks like the link to switch is there all the same.

  4. Cool article thanks! Have bookmarked for future reference.

    I do use the :target method quite a bit – especially for simple mobile menus but I’m not totally sure about the other methods mentioned in terms of semantic mark up and accessibility. Think you would need to think carefully about how overuse of tabindex might affect a keyboard only user or what a screen reader might make of random checkboxes. Any thoughts?

    • Thanks John. Good points about accessibility and semantics. I can accept a loss of semantics with these methods, because they are something of a hack and while semantics are important, I don’t think we should be slaves to it.

      Accessibility on the other hand is something we do need to be concerned about. Too many checkboxes isn’t a good idea. One probably isn’t even the best idea, but I’m not sure how bad it is. I guess the tradeoff is how much Javascript can you eliminate by using one of these click events and is the tradeoff worthwhile.

      It’s not an easy question with an absolute answer.

  5. The focus method: By placing a similar but invisible p (span or whatever works with tabindex) just after the p to click and then with focus make this p visible and the click p not visible (css sequence important!) you can get a text to click at the same location. As clicking at this point everywhere restores the initial state no focus and css is necessary for the “hide” p.
    I think this would work for the target method as well, but haven’t tested this as I don’t like this method changes the url.
    Example:

    CSS onclick focus test

    #lightbox {
    display: none;
    margin-top: 20px;
    width: 200px;
    text-align: center;
    border: 1px solid red;
    }
    #spS {
    font-weight: bold;
    }
    #spH {
    font-weight: bold;
    display: none;
    }

    #spS:focus ~ #lightbox {
    display: block;
    }
    #spS:focus ~ #spH {
    display: inline !important;
    }
    #spS:focus {
    display: none;
    }
    /* #spH:focus similar to #spS:focus isn’t needed because click anywhere recreates initial state */

    Show the lightbox
    Hide the lightbox

    Hi!
    I am the lightbox

    • Thanks Bo. This is a new one to me. I’ll have to give it a try. Of all the methods listed, the one I’ve used the most is the checkbox hack. I’m with you on the :target hack. I don’t like that it changes the url even though it does work as advertised.

  6. Thanks Stephen. For those who didn’t get the point because my example was garbled up by this entry field understanding html here is a version where “” are replaced with “<” and “>” which by copy and reverse replace should make html again:
    <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>
    <html xmlns=”http://www.w3.org/1999/xhtml”>
    <head>
    <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″ />
    <title>CSS onclick focus test</title>
    <style type=”text/css”>
    <!–
    #lightbox {
    display: none;
    margin-top: 20px;
    width: 200px;
    text-align: center;
    border: 1px solid red;
    }
    #spS {
    font-weight: bold;
    }
    #spH {
    font-weight: bold;
    display: none;
    }
    #spS:focus ~ #lightbox {
    display: block;
    }
    #spS:focus ~ #spH {
    display: inline !important;
    }
    #spS:focus {
    display: none;
    }
    /* #spH:focus similar to #spS:focus isn’t needed because click anywhere recreates initial state */
    –>
    </style>
    </head>
    <body style=”margin: 20px;”>
    <div>
    <p id=”spS” tabindex=”0″>Show the lightbox</p>
    <p id=”spH”>Hide the lightbox</p>
    <div id=”lightbox”>
    <p>Hi! <br />
    I am the lightbox</p>
    </div>
    </div>
    </body>
    </html>

  7. Thanks for making it right Steven. I tried with html entities like this line:
    <tag>
    But because they don’t show up as less than and greater than characters while entering I changed my mind as I thought I had done something wrong.

    • Glad to help. I used to do the same thing when trying to type code here. You have to use & lt ; and & gt ; without the spaces. If you do WordPress should convert them to the right characters.

      It wasn’t you. It was just WordPress. It strips out code by default as a security measure I think.

  8. You may have already heard of it, but thought it was prudent to this article (I found it yesterday):
    http://labs.bigroomstudios.com/libraries/CuttySSark
    Event Driven CSS! Still not 100% on what I think about it (and would like to do some performance tests) and definitely not sure about some of the more advanced examples (bringing too much behaviour into presentation) but still – for clicks, toggles onloads it’s pretty cool stuff.

    • Thanks John. Actually I hand’t seen it yet. Looks pretty cool. It’s still javascript behind the scenes, but it looks like an easy way to trigger events using css.

      I hear you about mixing behavior and presentation. I’m ok with certain things like toggles though.

  9. I Like this article. I think its yet a smarter approach to simple events. So if the user disabled javascript, can this can be a fallback, which can be a better approach?

Leave a Reply

Your email address will not be published.

css.php