More Showing, More Hiding
read 230 commentsWe've received a number of comments recently from people looking for variations on the showing and hiding theme. For the basics, you can take a look at two earlier entries, Basic Show and Hide and Slicker Show and Hide.
For a full-blown plugin solution with lots of options, look no further than Jörn Zaefferer's Accordion Menu. But if you want to try some showing and hiding on your own, read on.
Caveat
In this tutorial, we'll explore a couple ways to show and hide details by clicking on headings. I recognize that there are many ways to mark up the HTML for this sort of thing, and many more ways to write the JavaScript/jQuery. Even the functionality can vary greatly, depending on your needs. Some common variations include:
- each detail shows and hides independently of others
- one and only one detail visible at a time
- either no detail or one detail visible at a time
We'll take a look at the first two variations this time, and the third in a separate entry (so that I can get something posted more quickly).
The Setup
As with any jQuery script, we need to include the jquery.js file and our custom script file in the <head>
, like so:
- <script src="/js/jquery.js" type="text/javascript"></script>
- <script src="/js/more-show-hide.js" type="text/javascript"></script>
It's important to include jquery.js before any other files that use it. Also, you may need to change the path to match your site's structure.
For the elements to show and hide, we'll use a simple <h3>
/ <div>
structure:
- <div class="demo-show">
- <h3>Title 1</h3>
- <div>Lorem...</div>
- <h3>Title 2</h3>
- <div>Ipsum...</div>
- <h3>Title 3</h3>
- <div>Dolor...</div>
- </div>
Now let's add to that a little CSS to shape things up a bit:
- .demo-show {
- width: 350px;
- margin: 1em .5em;
- }
- .demo-show h3 {
- margin: 0;
- padding: .25em;
- background: #bfcd93;
- border-top: 1px solid #386785;
- border-bottom: 1px solid #386785;
- }
- .demo-show div {
- padding: .5em .25em;
- }
Finally, we can get to the scripting.
Option 1: Independent
Remember, this is the option that allows each detail section to be shown or hidden independently of the others. It's also the easiest of the three to accomplish.
The key method we'll be using here is .slidetoggle()
, an excellent little effect that slides the matching elements down when they are hidden and slides them up when they are visible. But before we do that, let's make all of the detail sections hide when the DOM is ready:
- $(document).ready(function() {
- $('div.demo-show:eq(0)> div').hide();
- });
Line 2 gets every <div>
that is a child of the first <div>
and hides them. I'm using :eq(0)
here because I'll be showing two show-hide examples that use the same class, but we're taking the examples one at a time.
Now, we can bind a click
handler to each <h3>
that is a child of <div>
. You can see this in line 3 below:
- $(document).ready(function() {
- $('div.demo-show:eq(0)> div').hide();
- $('div.demo-show:eq(0)> h3').click(function() { });
- });
All that's left is to drop the .slidetoggle()
method inside the click
method. Since we know that each <div>
that we want to toggle appears next to each <h3>
that might be clicked, we can use the handy .next()
method (line 4):
- $(document).ready(function() {
- $('div.demo-show:eq(0)> div').hide();
- $('div.demo-show:eq(0)> h3').click(function() {
- $(this).next().slideToggle('fast');
- });
- });
I used the "fast" option for the duration of the slide, but we also could haved used "slow" or "normal" or a numeric value for the number of milliseconds.
Try out the demo. See how a click on each <h3>
will show or hide the next <div>
, independent of the others:
Title 1
Title 2
Title 3
Option 2: One and Only
Let's move on now to the scenario in which we want one detail <div>
at all times, but no more than one. The first thing we'll need to change for this one is the line that hides all of the child <div>
s of <div>
. We'll make it hide all but the first <div>
:
- $(document).ready(function() {
- $('div.demo-show:eq(1)> div:gt(0)').hide();
- });
Again, we add an :eq()
to the containing <div>
selector, this time with 1
as the index, because we want our second demo (and because JavaScript numbering starts at zero).
Next, we add the same click handler for the <h3>
elements, but this time we need to change the effects that take place within it:
- $(document).ready(function() {
- $('div.demo-show:eq(1)> div:gt(0)').hide();
- $('div.demo-show:eq(1)> h3').click(function() {
- $(this).next('div:hidden').slideDown('fast')
- .siblings('div:visible').slideUp('fast');
- });
- });
Line 4 above slides down the <div>
that follows the clicked <h3>
by using $(this).next()
, but only if that <div>
is hidden (:hidden
). Also, now that we've chained .next()
onto $(this)
, we've changed the context to that following <div>
. So then in line 5 when we refer to .siblings()
, we're actually getting the siblings of the <div>
. Since out of all the siblings we only want to slide up the slides up the ones that are visible <div>
s, we use .siblings('div:visible')
.
Give demo #2 a whirl:
Title 1
Title 2
Title 3
There you have it—two fairly straightforward examples of showing and hiding elements on a page. Next time we'll take a look at two ways we can implement the third variation on the showing-and-hiding theme: having either no detail or only one shown at all times.
Scripts included in this post:
- www.learningjquery.com/js/more-show.js
- Other JavaScript Files …
230 comments
Hi.
Thanks for your tutorials. They are great.
I have a question. I'm trying to use the same technique to create a mouse over (or click) effect where the text appers the same place but differ according to what button you click (over spot you mouseover). My problem is that if I click/mouseover before the first effect (eg. SlideDown) is finish I get the new text AND the old text (that should be hidden .hide()) right after each other and hence breaking the layout...:(
Can I somehow tell the slideDown-function to hide everything else even if another slideDown effect is still taking place...?
Hope it was somewhat clear...;-)
Hi CH,
I'm glad you like the tutorials! I think I understand what you're trying to do, but I'm not certain. You might want to try to put the
.slideDown()
in the callback of the.slideUp()
. If you can wait a few days, I'll be posting a follow-up entry that describes how to do that.Cool. Ill give it a shot tomorrow at work.
Keep up the great work!
What do you think about image button's, instead of using text link title 1, title 2, it should be possible right? This would essentially create a drop down, or flyout menu, with several links for each of the three buttons. Sounds workable no... It would be great if the flyout slightly overlapped the button to more appear that it was the flyout from the button.
I'm not sure I've seen an accordion menu done with image buttons, I guess that would work too, but left to right fly-in was the requested effect. That I've no idea how to accomplish.
Keep the great stuff coming then jQuery'ers!
Hey Ty,
There's no reason it couldn't be done with image buttons. I think I understand what you're getting at with the fly-out menu idea. You would want the fly-out stuff to overlay everything, right?
Karl, nice tutorial. Good way to demonstrate some more advanced selectors.
Also see the Xpander plugin for another take on this:
labs.activespotlight.net/jQuery/Xpander.html
_______
SEAN O
That's a really cool one. Thanks, Sean!
their is something wrong... Their is a small but sudden flash before the anim. start.
Ah, yes, you're probably using FF2.0 Mac, right? That flicker is due to a bug in an early 1.1.1 build of jquery.js. I just updated to a more recent SVN build and tested it; I don't see the flicker now.
Thank you for bringing this to my attention! Would you mind testing it again and seeing if the flicker still occurs?
thanks Karl
one question about this demo - what does the "gt" in div:gt(0) mean or do? or is that "greater than"?
Also, and this may be outside the scope of this demo, if you had 3 divs stacked, how would you modify this script so that at any one time only 2 divs (at most two) could be open, with the third closing itself?
hmm... the more I think about what I just asked, the more potential problems I see, but the idea was that two adjacent divs would be visible (1 & 2 or 2 & 3). Thanks!
Hi Rolf,
You're correct about
div:gt(0)
. It gets alldiv
s that have anindex
greater than 0, or, in other words, alldiv
s after the first one.About the 2-visible, 1-hidden idea — unfortunately, it is outside the scope of this demo. If you know there will be only three total
div
, though, you could easily do it by setting one.click()
handler for$('h3:eq(0)')
and another one for$('h3:gt(0)')
. Hope that helps point you in the right direction.Hi Karl,
In your example here, when I hover over the h3 titles I see a little hand icon that indicates I can click on them as if they were links. On my pages I only see a caret.
What do I do to show those hand icons?
Thanks!
Well Karl... I just found the answer to my question by reading your next article...
:-)
Thanks all the same.
Hey Philip,
Glad to see that you found what you were looking for. Cheers!
Hi,
I can't seem to get the following to work:
$('input[@type=checkbox]').each(function(i) {
this.hide();
});
or
$('input[@type=checkbox]').each().hide();
Are there limita