1

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.

Nevzat Korkut
  • 45
  • 2
  • 8
  • Is this an exercise in "vanilla JavaScript" i. e. do you explicitly want to avoid using jquery? If so, it *can* be done, but otherwise, a solution using jquery is almost trivial. – Carsten Massmann Sep 14 '17 at 15:01
  • There is no reason to advocate jQuery here. It is absolutely unrelated to the problem or solution. – Scott Marcus Sep 14 '17 at 15:03
  • I agree, if OP wants to *solve a puzzle* then the answers without jquery are just right. But if he wants to achieve something in a real word context, then maybe the hint towards jquery might be helpful. I am not forcing it on anybody. But `$('.Solution').hide() $('button').on('click',function(){$(this).next().show()})` is definitely shorter than all the other solutions here, see below for a working demo. – Carsten Massmann Sep 14 '17 at 15:11
  • Don't forget to mark a answer as accepted to help the others that come to this question. – Lioo Sep 14 '17 at 15:11
  • @cars10m Since when has shorter ever been accepted as better? Writing one cryptic line of jQuery instead of learning how JavaScrpt (and therefore jQuery) actually work is never a good idea. New JavaScript coders should always learn JavaScript first and then jQuery. This question is really about coding patterns and best-practices. – Scott Marcus Sep 14 '17 at 15:27
  • @Scott Marcus: Yes, of course, you put together a great answer. Well explained and in keeping with all the relevant style guides. I was only trying to show an alternative, since OP asked: "Is there any better way for doing this?". And the jquery code is not all that cryptic if you see it in properly formatted below (there is not much to format anyway ...). I accept that jquery is considered not to be "funky" anymore, but I still like using it very much. – Carsten Massmann Sep 14 '17 at 15:46
  • 1
    Hello again, thank you very much for your advices. I want to share my opinion about this topic. I am a beginner and trying to make some basic pages to practice. I guess there are some other beginners in this platform. So for us sometimes the senior's some answer seem very complicated. I know here is not an education platform but we need more explained answers like @Scott Marcus did in this questions. – Nevzat Korkut Sep 14 '17 at 21:16
  • 1
    So for example for this question the main problem for me is that, some of our senior friends says I can use jquary (i am really thankful for that by the way) and they writes the code but honestly i dont even know where to write that code. So for beginners like me if you explain in detail what are the advantages of each solution or how can we practice it would be very useful or maybe if possible at least one senior do that like @Scott Marcus did, would be very very useful for us. Thank you very much again. – Nevzat Korkut Sep 14 '17 at 21:16

4 Answers4

2

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>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
2

If I understood what you want, you can use parameters.

HTML

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

JS

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

If all the questions appear in the same time, you could use a for loop to show them all too.

Lioo
  • 375
  • 6
  • 20
2

You can do something like this:

  • Enclose the question in a div with class 'question' and similarly solution enclosed with class 'solution'.
  • Also define a single show function with parameter as the button element clicked, which passed by using this as parameter in the onclick of button.
  • The function simply finds the sibling div with class 'solution' and makes it visible by setting display: block

function show_solution(buttonElement) {
  questionDiv = buttonElement.parentNode;
  
  if (questionDiv.querySelector('.solution').style.display == 'none') {
    questionDiv.querySelector('.solution').style.display = 'block';
  } else {
    questionDiv.querySelector('.solution').style.display = 'none';
  }
  
}
.solution {
  display: none;
}

.highlight {
  background: #FF0;
}
<div class="question">
  &bull; Question 1. What is x?
  <button class="btn btn-outline-success" onclick="show_solution(this)">Solution</button>
  <div class="solution">
    <div class="highlight">
      <pre> X is apple </pre>
    </div>
  </div>
</div>

<div class="question">
  &bull; Question 2. What is y?
  <button class="btn btn-outline-success" onclick="show_solution(this)">Solution</button>
  <div class="solution">
    <div class="highlight">
      <pre> Y is orange </pre>
    </div>
  </div>
</div>
Deepansh Sachdeva
  • 1,168
  • 9
  • 16
0

Well, if jquery was allowed, the solution could look like this:

$('.Solution').hide()
$('button').on('click',function(){$(this).next().show()})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<li>Question 1. What is x?</li>
<button class="btn btn-outline-success">Solution</button>
<div class="Solution">
<div class="highlight">
<pre>
X is apple
</pre>
</div></div>
<li>Question 2. What is y?</li>
<button class="btn btn-outline-success">Solution</button>
<div class="Solution">
<div class="highlight">
<pre>
Y is orange
</pre>
</div></div>

Note: this would also simplify your html ... But, of course, the choice is yours!

Carsten Massmann
  • 26,510
  • 2
  • 22
  • 43
  • Hello, thanks for your answer, i really dont know anything about jquary. After your answer i will start learning the basics and practice your solution. Thanks a lot again. – Nevzat Korkut Sep 14 '17 at 21:20