2
\$\begingroup\$

I have the following script to reveal sections on a page if certain text in an is clicked. How do I collapse all other sections if any one of them is selected and make it scalable for 20 items?

The following solution looks like it needs refactoring, and also when clicking icn_1, for instance, all other sections might already be hidden and putting an if for all sections for each click function seems very redundant.

$(document).ready(function() {
  $('section#1').hide();
  $('section#2').hide();
  $('section#3').hide();
  $('section#4').hide();    

  function reveal1() {
  $("section#1").show("slow");
  $("section#2").hide("fast");
  $("section#3").hide("fast");
  $("section#4").hide("fast");
  }
  $(".icn_1").click(reveal1);

  function reveal2() {
  $("section#2").show("slow");
  $("section#1").hide("fast");
  $("section#3").hide("fast");
  $("section#4").hide("fast");
  }
  $(".icn_2").click(reveal2);

  function reveal3() {
  $("section#3").show("slow");
  $("section#1").hide("fast");
  $("section#2").hide("fast");
  $("section#4").hide("fast");
  }
  $(".icn_3").click(reveal3);

  function reveal4() { 
  $("section#4").show("slow");
  $("section#1").hide("fast");
  $("section#2").hide("fast");
  $("section#3").hide("fast");
  }
  $(".icn_4").click(reveal4);
});
\$\endgroup\$

migrated from stackoverflow.com Dec 12 '11 at 16:47

This question came from our site for professional and enthusiast programmers.

6
\$\begingroup\$

To make it scalable for 20 or more items, do the following:

  1. Give all your clickable things the same class and unique identifying id like: <button class="icn" id="icn_1">.
  2. Give each section a legal id that starts with a character, not a number, but has the same number in it as the clickable item's id it matches up with and a common class name like: <section id="section_1" class="sections">. Keep in mind that legal CSS ids (before HTML5) must not start with a number or symbol.
  3. Then, you can use this one piece of code to control them all:

Code:

$(".icn").click(function() {
    var num = this.id.match(/\d+/)[0];
    $(".sections").hide();
    $("#section_" + num).show();
});

To recap.

  1. The button has an id like `icn_1'.
  2. When the button is clicked, the code extracts the number from the id of the clicked button
  3. Then, all sections are hidden using the .sections class name.
  4. Then, the number we extracted is combined with some text to form an id like `#section_1" and that section is shown.
  5. The result is one and only one section is visible (the one that has the same number in it's ID as the item that was clicked).
  6. This is scalable to thousands of items.
\$\endgroup\$
6
\$\begingroup\$

First, you have multiple functions with the same name. That's not valid.

Assuming these sections are divs, to hide all of them except one, take a look at the not function:

function reveal1() { 
    $("div").not("#section1").hide("slow");
    $("#section1").show();
}

Or more generically:

function revealThisElement(elementId) { 
    $("div").not("#" + elementId).hide("slow");
    $("#" + elementId).show();
}

$(".icn_1").click(function() { revealThisElement("section1"); });
$(".icn_2").click(function() { revealThisElement("section2"); });
$(".icn_3").click(function() { revealThisElement("section3"); });

Or better yet, why not try something like this:

$(".revealable").click(function() { 
   revealMe(this); 
});

function revealMe(element) { 
    $("div.revealable").not("#" + element.id).hide("slow");
    $(element).show();
}
\$\endgroup\$
  • \$\begingroup\$ Your second code option only shows the thing that was clicked on which won't do anything (since it was already visible), so it doesn't show the desired element. Your first code option requires a line of code for every element which isn't the simplest way to scale to lots of elements. \$\endgroup\$ – jfriend00 Dec 12 '11 at 6:55
  • \$\begingroup\$ @jfriend00 - you're right. +1 to you for thinking a little deeper. I would have put the section-to-show's id in a data- attribute on the button, but parsing the id to get the num is cool too. Thanks. \$\endgroup\$ – Adam Rackis Dec 12 '11 at 16:13
2
\$\begingroup\$

Here's a example:

<html>
    <body>
        <button class="b" id="b1">B1</button>
        <button class="b" id="b2">B2</button>
        <button class="b" id="b3">B3</button>
        <hr/>
        <section id="section1" class="hideme">
            section1
        </section>
        <section id="section2" class="hideme">
            section2
        </section>
        <section id="section3" class="hideme">
            section3
        </section>
    </body>
</html>

$(document).ready(function() {
    // hide all sections on load
    $("section.hideme").hide();

    // add click listener on buttons
    $("button.b").click(function() {
        var id = $(this).attr("id"); // button id
        var sectionId = id.replace("b", "section"); // = sectionN
        $("section.hideme").hide(); // hide all sections
        $("section#" + sectionId).fadeIn("slow"); // fadeIn() wanted section
    });
});

http://jsfiddle.net/Regisc/bVfcz/

\$\endgroup\$
1
\$\begingroup\$

You need to think more generically. Let's keep this simple. Your HTML should be literally duplicated like so:

<style type="text/css">
   section { display:none; }
</style>

<div class="section-header">section 1 header</div>
<section>
  section 1 content
</section>
<div class="section-header">section 2 header</div>
<section>
  section 2 content
</section>
<div class="section-header">section 3 header</div>
<section>
  section 3 content
</section>

<script type="text/javascript">
    $('.section-header').click(function(){
        var $mysection = $(this).next();
        $('section').not($mysection).slideUp('fast');
        $mysection.slideDown('fast');
    });
</script>
\$\endgroup\$
  • \$\begingroup\$ This implies that the clicked things are in a particular position relative to the show/hide content that is easy to traverse with next(). That is an assumption or a design that was not provided by the OP. Perhaps their content could be arranged this way, perhaps not. \$\endgroup\$ – jfriend00 Dec 12 '11 at 6:51
  • \$\begingroup\$ You could just as easily within the click handler to this -> var $this = $(this); $('section').not('section:eq('+$this.index()+')').slideUp('fast'); \$\endgroup\$ – emeraldcode.com Dec 12 '11 at 6:59
1
\$\begingroup\$

I would suggest using a class attribute for each icon, then have one function which hides all sections then shows the one corresponding section.

Add an attribute class="icn" to all icons and class="section" to all sections. Change the id of each icon to just be the number (1, 2, 3, etc) Then use the following code

$(".icn").click(reveal); // add event listener to all icons
function reveal(event){
    $(".section").hide();
    $("#section"+event.currentTarget.id).show();
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.