Home > Software engineering >  Is there a shorter more concise way to hide & show div with Javascript?
Is there a shorter more concise way to hide & show div with Javascript?

Time:06-14

I am creating a dashboard with approximately 20 divs starting with "display: none;".

When the .onClick() in the sidebar will be used, it will show a specific div and keep hidden all the others. I have used the classic solution of creating a function for each div, however, is extremely lengthy and the code looks like a mess.

Is there a better cleaner way to achieve this with Javascript?

Here is my code:

function presale() {
  var x = document.getElementById("presale");
  var y = document.getElementById("claim");
  var z = document.getElementById("stake");
  if (x.style.display === "grid") {
    x.style.display = "none";
  } else {
    x.style.display = "grid";
    y.style.display = "none";
    z.style.display = "none";
  }
}

function claim() {
  var x = document.getElementById("presale");
  var y = document.getElementById("claim");
  var z = document.getElementById("stake");
  if (y.style.display === "grid") {
    y.style.display = "none";
  } else {
    x.style.display = "none";
    y.style.display = "grid";
    z.style.display = "none";
  }
}

function stake() {
  var x = document.getElementById("presale");
  var y = document.getElementById("claim");
  var z = document.getElementById("stake");
  if (z.style.display === "grid") {
    z.style.display = "none";
  } else {
    x.style.display = "none";
    y.style.display = "none";
    z.style.display = "grid";
  }
}
*,
html {
  color: #fff;
  background-color: black;
}

#presale,
#claim,
#stake
/* Here I have many other divs like below */

{
  display: none;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
  <link rel="stylesheet" href="MOD.CSS">
  <script src="main2.js"></script>
  <title>Base Template</title>
</head>

<body>
  <div>
    <ul>
      <!-- Here I have other 20 options like the above -->
      <li onclick="presale()">Presale</li>
      <li onclick="claim()">Claim</li>
      <li onclick="stake()">Stake</li>
      <!-- Here I have other 20 options like the above -->
    </ul>
    <div id="presale">
      <h1>Presale</h1>
    </div>
    <div id="claim">
      <h1>Claim</h1>
    </div>
    <div id="stake">
      <h1>Stake</h1>
    </div>
  </div>
</body>

</html>

Is there a better way to do this without the need to create a function and repeat the same thing over and over for each div?

CodePudding user response:

Here you see a vanilla Javascript solution.

content divs are by default hidden.

If you click an element, the corresponding data-id get the class show.

window.onload = function () {

  document.querySelectorAll('#nav li').forEach((elements) => {
    elements.addEventListener('click', (el) => {
      document.querySelectorAll('.content').forEach((item) => {
        // hide all
        item.classList.remove('show');
      });
      // show one
      document.getElementById(el.target.getAttribute('data-id')).classList.add('show');
    });
  });

};
.content {
  display: none;
}
.show {
  display: block;
}
<ul id="nav">
  <li data-id="presale">Presale</li>
  <li data-id="claim">Claim</li>
  <li data-id="stake">Stake</li>
</ul>
<div id="presale" >
  <h1>Presale</h1>
</div>
<div id="claim" >
  <h1>Claim</h1>
</div>
<div id="stake" >
  <h1>Stake</h1>
</div>

CodePudding user response:

This is a question for Code Review section https://codereview.stackexchange.com/

However, try smth like this:

const elems = ["presale", "claim", "stake"];

function toggle(elem) {
    elems.map(i => {
        let el = document.getElementById(i);
        if (el.style.display === "grid") {
            el.style.display = "none";
        } else {
            el.style.display = "grid";
        }
    })
}

and in html add the elem name as a param, so, replace this

    <li onclick="presale()">Presale</li>
    <li onclick="claim()">Claim</li>
    <li onclick="stake()">Stake</li>

with this

    <li onclick="toggle('presale')">Presale</li>
    <li onclick="toggle('claim')">Claim</li>
    <li onclick="toggle('stake')">Stake</li>

CodePudding user response:

You could simply assign the same class (e.g. my_div) to every showable div, then pass the id to your function (that will show that and hide all the others).

function show_hide(id) {
  document.querySelectorAll('.my_div').forEach(my_div => {
    my_div.style.display = my_div.getAttribute('id') == id ? 'block' : 'none';
  });
}
.my_div {
  display: none;
}
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
<div>
  <ul>
    <li onclick="show_hide('presale')">Presale</li>
    <li onclick="show_hide('claim')">Claim</li>
    <li onclick="show_hide('stake')">Stake</li>
  </ul>
  <div  id="presale">
    <h1>Presale</h1>
  </div>
  <div  id="claim">
    <h1>Claim</h1>
  </div>
  <div  id="stake">
    <h1>Stake</h1>
  </div>
</div>

CodePudding user response:

Something like this using data attributes and classlist toggles should also work.

I would consider minimizing your code (and CSS) by using generic CSS selectors to hide/show the individual sections. This also makes scalability and maintainability easier for the next guy.

This has the added benefit of your styling being controlled 100% using CSS and not arbitrary inline styles set by the javascript.

Adding another section is also easy as can be:

  1. Add a new section with some id (eg. awesome-section)
  2. Add a nav entry with the attribute data-toggle-section with the id as the value <li data-toggle-section="awesome-section">Awesome Section</li>
  3. Profit

You're also not restricted to using just the nav elements themselves as the event listener is bound using the [data-toggle-section] selector which means that basically anything can show or hide a section as long as it has that attribute with the correct value.

const buttons = Array.from(document.querySelectorAll("[data-toggle-section]"));
const sections = buttons.map(element => {
    return document.getElementById(element.attributes['data-toggle-section'].value)
});

buttons.forEach(element => {
    element.addEventListener('click', event => {
        const selected = element.attributes['data-toggle-section'].value;

        sections.forEach(section => {
            if(section.id === selected) {
                section.classList.toggle('shown')
            } else {
                section.classList.remove('shown');
            }
        })
    });
});
*,
html {
    color: #fff;
    background-color: black;
}

.option-section {
    display: none;
}

.option-section.shown {
    display: grid;
}
<div>
    <ul>
        <!-- Here I have other 20 options like the above -->
        <li data-toggle-section="presale">Presale</li>
        <li data-toggle-section="claim">Claim</li>
        <li data-toggle-section="stake">Stake</li>
        <!-- Here I have other 20 options like the above -->
    </ul>
    <div id="presale" >
        <h1>Presale</h1>
    </div>
    <div id="claim" >
        <h1>Claim</h1>
    </div>
    <div id="stake" >
        <h1>Stake</h1>
    </div>
</div>

CodePudding user response:

If you attach data attributes to both the list items and the "panels" you can use one function to match them up, and use a CSS class to determine whether it should be active or not.

// Cache the panels container, and the list element
// adding one event listener to the list. We're using
// event delegation for this - one listener captures all
// the events from its child elements
const panels = document.querySelector('.panels');
const list = document.querySelector('ul');
list.addEventListener('click', handlePanel);

// When the listener is triggered
function handlePanel(e) {
  
  // Check if it's a list item
  if (e.target.matches('li')) {

    // Destructure its id from the dataset
    const { id } = e.target.dataset;
  
    // Remove all the active classes from the panels  
    panels.querySelectorAll('.panel').forEach(panel =>  {
      panel.classList.remove('active');
    });
   
    // And then add an active class to the panel which
    // matches the id from the list item
    const current = panels.querySelector(`.panel[data-id="${id}"]`);
    current.classList.add('active')
  
  }

}
.panel { display: none; }
ul { list-style-type: none; margin-left: 0; padding: 0; }
li { padding: 0.3em; }
li:hover { background-color: thistle; cursor: pointer; }
.active { display: block; }
<ul >
  <li data-id="presale">Presale</li>
  <li data-id="claim">Claim</li>
  <li data-id="stake">Stake</li>
</ul>

<div >
  <div data-id="presale" >
    <h1>Presale</h1>
  </div>
  <div data-id="claim" >
    <h1>Claim</h1>
  </div>
  <div data-id="stake" >
    <h1>Stake</h1>
  </div>
</div>

CodePudding user response:

Here's my attempt :

  • For each line, I add an event on click.
  • On event, I select one .action (only one with .querySelector() because you only want one shown, you need to use querySelectorAll for more) that doesn't have d-none wich means that is the one shown, and I hide it.
  • I select the concerned action with the data-id and remove .d-none to show it

document.querySelectorAll("li").forEach(e => e.addEventListener("click", () => {
  if(document.querySelector(".action:not(.d-none)")){
    document.querySelector(".action:not(.d-none)").classList.add("d-none")  
  }
  document.getElementById(e.dataset.id).classList.remove("d-none")
}))
.action{
  display:grid;
}
.d-none{
  display:none;
}
<ul>
  <li data-id="presale">Presale</li>
  <li data-id="claim">Claim</li>
  <li data-id="stake">Stake</li>
</ul>
<div  id="presale">Presale</div>
<div  id="claim">Claim</div>
<div  id="stake">Stake</div>

  • Related