Search code examples
javascripthtmlshow-hide

Javascript hide/show with many elements


in my HTML i have questions and solutions. I want to hide and show solutions for each question with different buttons.

Here is my problem: I am writing a new function for each solution button. For example if i have 100 questions then i need to write 100 different Javascript function.

Is there any better way for doing this. Can i write only one function for each solution button.

Thank you very much in advance.

Here is my html:

<li>Question 1. What is x?</li>
<button class="btn btn-outline-success" 
onclick="myFunction()">Solution</button>
<div id="Solution">
<div class="highlight">
<pre>
X is apple
</pre>

<li>Question 2. What is y?</li>
<button class="btn btn-outline-success" 
onclick="myFunction1()">Solution</button>
<div id="Solution1">
<div class="highlight">
<pre>
Y is orange
</pre>

And here is my Javascript:

document.getElementById( 'Solution' ).style.display = 'none';

function myFunction() {
    var x = document.getElementById('Solution');
    if (x.style.display === 'none') {
        x.style.display = 'block';
    } else {
        x.style.display = 'none';
    }
}

document.getElementById( 'Solution1' ).style.display = 'none';

function myFunction1() {
    var x = document.getElementById('Solution1');
    if (x.style.display === 'none') {
        x.style.display = 'block';
    } else {
        x.style.display = 'none';
    }
}

Note:I saw many questions about hide and show but i could not solve my problem. If this is a duplicate question please warn me and i can remove.


Solution

  • You can reuse a single function by passing an argument that identifies which element in the document should be shown/hidden:

    document.getElementById( 'Solution' ).style.display = 'none';
    
    // The function now expects to be passed the ID of the element
    function myFunction(elementID) {
        var x = document.getElementById(elementID);
        if (x.style.display === 'none') {
            x.style.display = 'block';
        } else {
            x.style.display = 'none';
        }
    }
    

    Now, you can call the function repeatedly and just pass different element id's each time:

     <li>Question 1. What is x?</li>
     <button class="btn btn-outline-success" onclick="myFunction('Solution')">Solution</button>
     <div id="Solution">
       <div class="highlight">
         <pre>X is apple</pre>
       </div>
     </div>
    
    <li>Question 2. What is y?</li>
    <button class="btn btn-outline-success" onclick="myFunction('Solution1')">Solution</button>
    <div id="Solution1">
      <div class="highlight">
        <pre>Y is orange</pre>
      </div>
    </div>
    

    Now, having said that, you should avoid using inline HTML event attributes (onclick, onmouseover, etc.) whenever possible. There are many reasons why. Additionally, instead of repeatedly setting inline styles, you can make this much simpler by toggling the use of a pre-existing CSS class. Also, your HTML syntax is invalid.

    Here's what a cleaned up, valid and modern version of your code would look like. All you have to do is copy the HTML structure of a question and the existing JavaScript will immediately work for it. Each question no longer even needs a name ("Solution1", "Solution2", etc.).

    // Get all the buttons in a node list
    var theButtons = document.querySelectorAll(".btn-outline-success");
    
    // Turn node list into a JS Array
    var buttonArray = Array.from(theButtons);
    
    // Loop over the buttons and give each its click event handler
    buttonArray.forEach(function(button){
      button.addEventListener("click", function(){ 
        // We will pass a reference to the current button to the function
        myFunction(this); 
      });
    });
    
    // The function now expects to be passed a reference to the button that was clicked
    function myFunction(element) { 
      // Get a reference to div that follows the button and then search that div
      // for the first pre element inside of it:
      var answer = element.nextElementSibling.querySelector("pre");
      
      // All we need to do is toggle the visibility of that pre element
      answer.classList.toggle("hidden");
    }
    /* This class will simply be added or removed to show/hide elements */
    /* All answers have this applied by default*/
    .hidden {
      display:none;
    }
    
    li { margin-bottom:10px; }
    <!--
        NOTES:
               1. The onclick has been removed (it is now handled in JavaScript) 
               2. The quesiton names ("Solution", "Solution1", etc.) are no longer needed
               3. Your HTML structure was invalid. Here is the proper way to make bulleted lists.
    -->
    
    <ul>
      <li>Question 1. What is x?
          <button class="btn btn-outline-success">Solution</button>
          <div>
            <div class="highlight">
              <pre class="hidden">X is apple</pre>
            </div>
          </div>
      </li>
        
      <li>Question 2. What is y?
          <button class="btn btn-outline-success">Solution</button>
          <div>
            <div class="highlight">
              <pre class="hidden">Y is orange</pre>
            </div>
          </div>
      </li>
    
      <li>Question 3. What is z?
          <button class="btn btn-outline-success">Solution</button>
          <div>
            <div class="highlight">
              <pre class="hidden">z is mango</pre>
            </div>
          </div>
      </li>
    </ul>