skip to Main Content

I am using the following code form w3schools that uses a button to toggle text:

function myFunction() {
    var x = document.getElementById("myDIV");
    if (x.style.display === "none") {
        x.style.display = "block";
    } else {
        x.style.display = "none";
    }
}
<button onclick="myFunction()">Try it</button>

<div id="myDIV">
This is my DIV element.
</div>

Now, I want to have a page with many buttons that toggle different texts (every button corresponds to a unique text). Of course, I can make it work by copying the function each time I create a new text; myFunction1 <-> myDIV1, myFunction2 <-> myDIV2 etc.

But is there a smarter way to do it? I am thinking it can be done with a single function that select id’s in a specific form but I don’t know how.

3

Answers


  1. Since you are learning JavaScript, I suggest learning to not use some traditionally bad habits.

    One thing that is generally more hassle than it’s worth are inline click handlers. Meaning onclick='myfunction()' on your elements. Instead using addEventListener offers more capabilities.

    Also, another cool thing to learn is toggling a CSS class on your elements instead of changing the style directly in JavaScript.

    So for my answer I actually add a click handler to the body. Then when you click I check to see if the element you clicked has a specific class. I gave your buttons a class of btnToggle.

    Next on each button I use data attributes to dictate what that button should target. This is done by using data-target="#divID".

    Now in the click handler, I get that target by getting the clicked element and data.target.

    Now I use a CSS class called active that sets display to display.

    Then I simply use the clicked element’s class list to toggle that class.

    document.body.addEventListener("click",(e) => {
       const el = e.target;
       if(el.classList.contains("btnToggle")){
          const targetDiv = document.querySelector(el.dataset.target)
          targetDiv.classList.toggle("active")
       }
    });
    .contentDiv{display:none}
    .active{
      display:block
    }
    <button data-target="#myDIV1" class="btnToggle">Try it 1</button>
    
    <div id="myDIV1" class="contentDiv">
    This is my DIV element. 1
    </div>
    
    <button data-target="#myDIV2" class="btnToggle">Try it 2</button>
    
    <div id="myDIV2" class="contentDiv">
    This is my DIV element. 2
    </div>
    Login or Signup to reply.
  2. First, let me say that W3Schools is well-known to have incomplete, out of date, or just plain wrong information. You should stay as far away from it as a learning resource as possible. Instead, the Mozilla Developer Network (who are the stewards of the JavaScript language) has what is widely considered to be the authoritative source for learning and documentation on many web technologies and languages.

    As to your question (and because you got the code from W3Schools) we have to back up a bit.

    • The way they show you how to set up event handling functions is a 25+
      year old approach that we used before we had any standardized ways of
      doing things. This technique still works for legacy reasons, but
      should not be used today.
    • Setting styles directly on elements via the style property is
      discouraged. This causes what is called an inline style to be added
      to the element and inline styles lead to duplication of code and
      difficulty in overriding those styles when that is invariably needed.
    • The use of id‘s on elements is a very easy way to access portions
      of your page and work with them, but it is also discouraged because
      id based scripting leads to brittle code that doesn’t scale well.
      The use of id‘s should also be avoided in favor of other ways
      (i.e., location of an element in relation to other elements).

    With all that said, the better approach here would be to have your several buttons to press, but have them each just populate one common output element when they are clicked. This way, you don’t need to know about any id.

    See comments inline below:

    // Get your element references you'll need just once,
    // rather than each time the function is called.
    // And note here that we can get the reference by
    // searching for the first instance of an element 
    // that matches the specified class.
    const output = document.querySelector(".output");
    
    // Set up events for your elements in JavaScript, not HTML
    // Here, we'll set up a click event handler for the parent
    // of the 3 sample buttons. When any button gets clicked,
    // the click event will "bubble" up to the parent and be
    // handeled there. This is called "event delegation".
    document.querySelector(".wrapper").addEventListener("click", myFunction);
    
    function myFunction(event) {
      // Check to see if the event was triggered 
      // by an element we care to handle
      if(event.target.classList.contains("topic")){
        // Update the status of the element
        event.target.closest("label").classList.toggle("active");
        
        // Check to see which button was clicked so we know which text to display
        if(event.target.classList.contains("a")){
          // This is called a "ternary operator" and works like this:
          // some value = condition ? value if condition is true : value if false
          output.textContent = output.textContent === "" ? "TOPIC A" : "";
        } else if (event.target.classList.contains("b")) {
          output.textContent = output.textContent === "" ? "TOPIC B" : "";   
        } else {
          output.textContent = output.textContent === "" ? "TOPIC C" : "";   
        }
      }
    }
    .hidden { display:none; }
    .active { color:red; }
    <div class="wrapper">
      <!-- Don't set up events with inline HTML attributes. -->
      <label><input type="checkbox" class="hidden topic a">Try it 1</label>
      <label><input type="checkbox" class="hidden topic b">Try it 2</label>
      <label><input type="checkbox" class="hidden topic c">Try it 3</label>
    </div>
    
    
    <!-- No matter which button gets clicked, the appropriate
         content will go here. -->
    <div class="output"></div>
    Login or Signup to reply.
  3. To try and tie together mine and Keith’s comments – re grouping and event delegation – depending on how your page is arranged you can get away with a more simplified HTML markup, and JS code.

    For example, let’s say you have a series of sections, and each section has a paragraph and corresponding button immediately below it. Using event delegation you can add one listener to the containing element (in this case .container), and have that catch all of the events from its child elements as they "bubble up" the DOM.

    The listener’s handler would first check to see if the element that fired the event matches a button element, and it would then grab that button’s previousElementSibling (the paragraph element). Using CSS and classList‘s toggle method, you can then toggle that text on/off, or to a different colour, etc. This example fades it in/out.

    // Get the container element
    const container = document.querySelector('.container');
    
    // Assign one listener to the container
    container.addEventListener('click', handleClick);
    
    // Using the event from the listener
    // 1) Check the target of the event is a button
    // 2) Grab the button's sibling paragraph element
    // 3) Toggle the show class on the paragraph
    function handleClick(e) {
      if (e.target.matches('button')) {
        const para = e.target.previousElementSibling;
        para.classList.toggle('show');
      }
    }
    .container  { display: grid; grid-template-columns: repeat(2, 100px); gap: 0.25rem;}
    .container section { background-color: #efefef; border: 1px solid #cdcdcd; padding: 0.25rem; text-align: center;}
    p { opacity:0; transition: opacity 0.2s ease-in-out;}
    .show { opacity: 1; }
    <section class="container">
      <section>
        <p>Text one</p>
        <button type="button">Toggle text</button>
      </section>
      <section>
        <p>Text two</p>
        <button type="button">Toggle text</button>
      </section>
      <section>
        <p>Text three</p>
        <button type="button">Toggle text</button>
      </section>
      <section>
        <p>Text four</p>
        <button type="button">Toggle text</button>
      </section>
    </section>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search