Show/Hide Script-Free (Which Means CSS Only)

There are many ways to hide and show content with a click (or tap or poke or key-press or …). Many of them have JavaScript under the hood and nearly all of them have dependencies on third-party libraries and/or CDNs. This may be fine when you already have to load a pile of script on a page. This may also lead to poor development practices.

You can mitigate that JavaScript failure with a little CSS-only progressive enhancement. If you lean on the :target selector you only need two lines of CSS for the most basic show/hide feature:

.expando {
  display: none;
.expando:target {
  display: block;

I made a sample to show it in action:

See the Pen Show/Hide Script-Free by Adrian Roselli (@aardrian) on CodePen.

Supporting older browsers that don’t know what to do with :target can be as simple as wrapping the CSS in media queries or using conditional comments (for old Internet Explorer).

From here you can layer all your nifty JavaScript animated effects, key bindings, and so on.

What Prompted This Post?

I was at the W3C looking for all the accessibility-related specs. I saw the list of groups of specs, saw the blue text that implied I could click (underlines are inconsistently applied, so they aren’t a consistent clue) and something would happen, but was unable to get anywhere.

A quick view-source showed me that those weren’t links nor buttons (no a href and no button to be seen). Checking the console I saw the following error: Uncaught TypeError: Cannot read property 'substring' of undefined

The script was failing and since the a href is added after the page is served, the error prevented any controls from being added, leaving the content I wanted unavailable. The raw HTML looks like this:

<div class="trviewcat expand_block closed">
  <h3 id="tr_Accessibility__All_">
    <span class="expand_section">Accessibility (All)</span>

I grabbed a screen shot that shows (what I think) are all the relevant bits:

Screen shot of W3C site with the developer tools showing a script error.
Screen shot of the W3C with the developer tools showing the DOM and the script error.

The page is set up to show all the content expanded if there is no JavaScript support. That’s a good practice to support users who cannot get the script (bad connections, firewalls, etc.).

The script then comes around and collapses all the content and adds a hrefs to make it all work. Unfortunately, if the script breaks after it collapses the content but before the links are added then users are left in a bad spot. Adding the a hrefs server-side could mitigate that, reducing the scenarios where the page breaks.

I want to be fair to the folks at the W3C who built this. Nobody can reproduce this issue. I can’t reproduce it on any other browser (so far) besides Chrome, and it works properly if I visit the page in incognito mode. If I turn off all my Chrome extensions, however, the page still breaks. I just haven’t figured out what part of my configuration breaks it yet.

More on Progressive Enhancement



Nice! but there an option to add a function to remove the last option chosen like a combo select?

Carlos; . Permalink
In response to Carlos. Reply

Carlos, no there is not — at least not without another anchor link. When the URL no longer has an anchor referenced (via #foo), then nothing will be the target and the CSS selector won’t apply. This is really just a very basic example to show the capability.


and it is possible to join this example with yours?
Thanks for your help Adrian

Carlos; . Permalink
In response to Carlos. Reply

Sort of. Those are select menus, so they aren’t related to my example at all. You could use the select menu to load the page at a specific anchor, but then you are using a form for navigation and that’s generally frowned upon. You could also re-style my example to look like those menus, but that’s also suggesting to users that functionally they will behave like the form element (such as support for arrow keys).


This is pretty nifty and simple. Targeting an ID scrolls the page to that element, is there anyway to avoid the scroll? I have a lengthy Q&A where you answer each question with yes or no (some nested questions too), and once clicked your answer shows, then you manually scroll to the next question.

I would like to avoid the page scrolling to the answer (your choice) when it is chosen. Any thoughts on this? I see another solution using input checkboxes that I might try too, but it’s not as elegantly simple as this.


Craig W; . Permalink
In response to Craig W. Reply

I cannot think of an option off the top of my head that does not involve some scripting. However, since I consider the scrolling effect to be a feature and one which I would not want to disable, then this may not be best option for you.

You may want to just use a <button> element with a simple function that toggles a class. Then use an adjacent sibling selector for any buttons with that class and display the content.

FWIW, I am still not a fan of hiding content until a user interacts with it when it is already on the page, but I do not know your application.


Is there a way to re-hide? I’m using this to open up more text (descriptive) once read I would like the user to be able to shut it.

In response to Ang. Reply

Nope. Not without navigating to a different anchor or clearing the anchor from the URL. This approach is not nearly as robust as <details>/<summary>, which will require a polyfill for IE and Edge.

In response to Adrian Roselli. Reply

This seems to work for re-hide:

<a href="#Expando1" rel="nofollow ugc">Show</a>
My content<a href="" rel="nofollow ugc">Hide</a>

Am I missing anything?

In response to JJ. Reply

Well, the validator does not balk. So maybe yeah? It is seemingly no worse than what I propose in this post!

In response to Adrian Roselli. Reply

Here’s what I was trying to show:

CodePen gets a little fussy with the empty href attribute, but the empty href is valid in HTML5 and this seems to work otherwise.


Hi JJ, add a hash to your Codepen <a href="#" rel="nofollow ugc">Hide my content</a> and it’ll work

Leave a Comment or Response

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>