skip to Main Content

I am trying to create a piece of javascript code that will only hide and show each form on the page that corresponds with its id property. So imagine having 4 buttons visible on the page and the form elements are hidden by default, once a single button with its matching id to the form is clicked it will only toggle that form. Here is the code i have tried

function _(x) {
  return document.getElementById(x);
}

const catBtn = _("catBtn");
const blogBtn = _("blogBtn");
const videoBtn = _("VideoBtn");
const prodBtn = _("prodBtn");

const catForm = _("catForm");
const blogForm = _("blogForm");
const videoForm = _("VideoForm");
const prodForm = _("prodForm");

document.addEventListener('click', function(e) {
  if (e.target.id === catBtn) {
    // catForm.classList.toggle("showHide");
    console.log("you clicked the cat btn");
  } else if (e.target.id === blogBtn) {
    // blogForm.classList.toggle("showHide");
    console.log("you clicked the blog btn");
  } else if (e.target.id === videoBtn) {
    // videoForm.classList.toggle("showHide");
    console.log("you clicked the video btn");
  } else if (e.target.id === prodForm) {
    // prodForm.classList.toggle("showHide");
    console.log("you clicked the product btn");
  }
})
<div class="new__btn">
  <button id="catBtn">New Category</button>
</div>
<div class="new__btn">
  <button id="blogBtn">New Blog</button>
</div>
<div class="new__btn">
  <button id="videoBtn">New Video</button>
</div>
<div class="new__btn">
  <button id="prodBtn">New Product</button>
</div>

<div id="catForm">
  Hide or show this div for the categories
  <div class="panel">
    <div class="panel__head">Panel Head</div>
    <div class="panel__body">Panel Body</div>
    <div class="panel__footer">Panel Footer</div>
  </div>
</div>

<div id="blogForm">
  Hide or show this div for the blogs
  <div class="panel">
    <div class="panel__head">Panel Head</div>
    <div class="panel__body">Panel Body</div>
    <div class="panel__footer">Panel Footer</div>
  </div>
</div>

<div>
  <div id="videoForm">
    Hide or show this div for the videos
    <div class="panel">
      <div class="panel__head">Panel Head</div>
      <div class="panel__body">Panel Body</div>
      <div class="panel__footer">Panel Footer</div>
    </div>
  </div>

  <div id="prodForm">
    Hide or show this div for the products
    <div class="panel">
      <div class="panel__head">Panel Head</div>
      <div class="panel__body">Panel Body</div>
      <div class="panel__footer">Panel Footer</div>
    </div>
  </div>

5

Answers


  1. First of all you are comparing Nodes, with strings (e.target.id returns a string) which will always return false.

    Second, you are applying an event listener to document, meaning you are adding an eventListener to the entire document, wherever you click it will trigger.

    You need to add an eventListener to every button. You can do something like this:

    
        button.addEventListener('click', (event) => {
          show(getForm(button.id))
        })
    
    

    You can also do a document.querySelectAll('button') and loop over the element, apply the event listener above.

    Hope this helps!

    Login or Signup to reply.
  2. The previous answer covered one option to solve this problem, but the clicks could also be detected in the way you were using earlier, by attaching and event listener to the document. All you need to do is replace e.target.id with just e.target because the code should be comparing an element to an element instead of an id to an element.

    document.addEventListener('click', function(e){
        if(e.target === catBtn) {
            // catForm.classList.toggle("showHide");
            console.log("you clicked the cat btn");
        } else if(e.target === blogBtn) {
            // blogForm.classList.toggle("showHide");
            console.log("you clicked the blog btn");
        } else if(e.target === videoBtn) {
            // videoForm.classList.toggle("showHide");
            console.log("you clicked the video btn");
        } else if(e.target === prodForm) {
            // prodForm.classList.toggle("showHide");
            console.log("you clicked the product btn");
        }
    })
    
    Login or Signup to reply.
  3. I would take an entirely different approach with this problem. What I suggest is tagging your buttons and forms using data attribues and then dynamically showing and hiding the forms based on what button was clicked. This will allow you to easily add new forms.

    Here is an implementation of my approach that uses querySelectory and data attributes. If you have any questions please feel free to ask away and I’ll do my best to elaborate on my solution for you.

    (function(){
    
        // find all the buttons with a data-target-form attribute 
        const buttons = document.body.querySelectorAll('button[data-target-form]')
        
      // loop over each button
      buttons.forEach((btn) => {
        // wire up the event handler for each button
        btn.onclick = (e) => {
            // get the target form name
          let formName = e.target.dataset.targetForm;
          // hide forms that aren't the desired one
          document.body.querySelectorAll(`div[data-form-id]:not([data-form-id="${formName}"])`).forEach((form) => form.style.display = 'none');
          // show the desired form
          document.body.querySelector(`div[data-form-id="${formName}"]`).style.display = 'block';
        }
      });
      
    })()
    /* Hide all forms at first until a button is clicked */
    div[data-form-id]{
      display: none;
    }
    
    /* just putting all the buttons in a row */
    
    .new__btn {
      display: inline-block;
    }
    <div class="new__btn">
      <button id="catBtn" data-target-form="categories">New Category</button>
    </div>
    <div class="new__btn">
      <button id="blogBtn" data-target-form="blogs">New Blog</button>
    </div>
    <div class="new__btn">
      <button id="videoBtn" data-target-form="videos">New Video</button>
    </div>
    <div class="new__btn">
      <button id="prodBtn" data-target-form="products">New Product</button>
    </div>
    
    <div id="catForm" data-form-id="categories">
      Hide or show this div for the categories
      <div class="panel">
        <div class="panel__head">Panel Head</div>
        <div class="panel__body">Panel Body</div>
        <div class="panel__footer">Panel Footer</div>
      </div>
    </div>
    
    <div data-form-id="blogs">
      Hide or show this div for the blogs
      <div class="panel">
        <div class="panel__head">Panel Head</div>
        <div class="panel__body">Panel Body</div>
        <div class="panel__footer">Panel Footer</div>
      </div>
    </div>
    
    <div>
      <div data-form-id="videos">
        Hide or show this div for the videos
        <div class="panel">
          <div class="panel__head">Panel Head</div>
          <div class="panel__body">Panel Body</div>
          <div class="panel__footer">Panel Footer</div>
        </div>
      </div>
    
      <div data-form-id="products">
        Hide or show this div for the products
        <div class="panel">
          <div class="panel__head">Panel Head</div>
          <div class="panel__body">Panel Body</div>
          <div class="panel__footer">Panel Footer</div>
        </div>
      </div>
    Login or Signup to reply.
  4. If you wrap your buttons and forms in containing elements, and add data attributes to both the buttons and forms you can use event delegation. This allows you to attach one listener to the buttons container which will catch events from its child elements as they "bubble up" the DOM.

    The handler can then check that it was a button that was clicked, extract its data id (which will correspond to an identical data id on a form), and use that id in a selector to target that form.

    Then it’s just a matter of using CSS to toggle the "show" class.

    As you can see from the example you can use the same method to toggle an active class to the clicked button too.

    // Cache the forms and buttons containers
    const forms = document.querySelector('.forms');
    const buttons = document.querySelector('.buttons');
    
    // Add one listener to the buttons container
    buttons.addEventListener('click', handleButton);
    
    // Utility helper to remove classes from a set of elements
    function removeClasses(context, selector, classname) {
      context.querySelectorAll(selector).forEach(el => {
        el.classList.remove(classname);
      });
    }
    
    function handleButton(e) {
      
      // If the clicked element that the listener catches
      // is a button
      if (e.target.matches('button')) {
        
        // Destructure its id from its dataset
        const { id } = e.target.dataset;
    
        // Create a selector using a template string which
        // will find a form with a matching data-id attribute
        const selector = `.form[data-id="${id}"]`;
    
        // Find the corresponding form in the forms container
        const form = forms.querySelector(selector);
    
        // If the button is active, remove the active
        // class, and remove the show class from the form
        if (e.target.classList.contains('active')) {
          e.target.classList.remove('active');
          form.classList.remove('show');
    
        // Otherwise remove all the active classes from the buttons,
        // and all the show classes from the forms, and then add the
        // active class to the button, and the show class to the
        // selected form.
        } else {
          removeClasses(buttons, 'button', 'active');
          removeClasses(forms, '.form', 'show');
          e.target.classList.add('active');
          form.classList.add('show');
        }
    
      }
    
    }
    .forms { margin-top: 1em; }
    .form { display: none; border: 1px solid lightgray; padding: 0.5em; }
    .form { margin-top: 0.5em; }
    .show { display: block; }
    button:hover { cursor: pointer; }
    button:hover:not(.active) { background-color: #fffff0; }
    .active { background-color: #ffff00; }
    <div class="buttons">
      <button type="button" data-id="cat">New Category</button>
      <button type="button" data-id="blog">New Blog</button>
      <button type="button" data-id="video">New Video</button>
      <button type="button" data-id="prod">New Product</button>
    </div>
    
    <section class="forms">
      <div class="form" data-id="cat">
        Hide or show this div for the categories
        <div class="panel">
          <div class="panel__head">Panel Head</div>
          <div class="panel__body">Panel Body</div>
          <div class="panel__footer">Panel Footer</div>
        </div>
      </div>
      <div class="form" data-id="blog">
        Hide or show this div for the blogs
        <div class="panel">
          <div class="panel__head">Panel Head</div>
          <div class="panel__body">Panel Body</div>
          <div class="panel__footer">Panel Footer</div>
        </div>
      </div>
      <div>
        <div class="form" data-id="video">
          Hide or show this div for the videos
          <div class="panel">
            <div class="panel__head">Panel Head</div>
            <div class="panel__body">Panel Body</div>
            <div class="panel__footer">Panel Footer</div>
          </div>
        </div>
      </div>
      <div class="form" data-id="prod">
        Hide or show this div for the products
        <div class="panel">
          <div class="panel__head">Panel Head</div>
          <div class="panel__body">Panel Body</div>
          <div class="panel__footer">Panel Footer</div>
        </div>
      </div>
    </section>

    Additional documenation

    Login or Signup to reply.
  5. ok i solved it, with a little research and fine tuning i was able to use this to make everything work as i needed it. So in case anyone else faces this issue here is the code i used

    function toggle_form(id) {
        var e = document.getElementById(id);    
        var allForms = document.querySelectorAll('div[id^=new]');
        
        [].forEach.call(allForms, function(div) {  
            if (div != e) {
                div.style.display = 'none';
            }
            else {
                e.style.display = e.style.display == 'none' ? 'block' : 'none';
            }
        });
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search