最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

html - Is there a shorter more concise way to hide & show div with Javascript? - Stack Overflow

programmeradmin3浏览0评论

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=".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>

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?

Share Improve this question asked Jun 14, 2022 at 13:07 Pro GirlPro Girl 9521 gold badge9 silver badges34 bronze badges 4
  • If you were using a front-end framework like React or Vue, you could do it using data binding. – Robert Harvey Commented Jun 14, 2022 at 13:09
  • 4 A framework isn't the panacea for every little problem. – Andy Commented Jun 14, 2022 at 13:20
  • @Andy I don't disagree but a framework does address this (and the other inevitable questions that will follow). Also...frameworks usually have a "right" way to do something. These answers will inevitably be a javascript free-for-all. – rob Commented Jun 14, 2022 at 13:30
  • 2 But the expense in time of learning something like React/Angular/Vue pales in comparison to understanding how to use use data attributes as I think most of these answer have demonstrated. – Andy Commented Jun 14, 2022 at 14:08
Add a comment  | 

8 Answers 8

Reset to default 8

There is no need for JS at all. You can simply use an anchor and use #id as hyper reference. Then you can display the element through CSS by using the :target-selector:

*,
html {
  color: #fff;
  background-color: black;
}

.d-none
/* Here I have many other divs like below */

{
  display: none;
}

div:target {
  display: grid;
}
<!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><a href ="#presale">Presale</a></li>
      <li><a href ="#claim">Claim</a></li>
      <li><a href ="#stake">Stake</a></li>
      <!-- Here I have other 20 options like the above -->
    </ul>
    <div id="presale" class="d-none">
      <h1>Presale</h1>
    </div>
    <div id="claim" class="d-none">
      <h1>Claim</h1>
    </div>
    <div id="stake" class="d-none">
      <h1>Stake</h1>
    </div>
  </div>
</body>

</html>

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" class="content">
  <h1>Presale</h1>
</div>
<div id="claim" class="content">
  <h1>Claim</h1>
</div>
<div id="stake" class="content">
  <h1>Stake</h1>
</div>

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.dataset.toggleSection)
});

buttons.forEach(element => {
    element.addEventListener('click', event => {
        const selected = element.dataset.toggleSection;

        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" class="option-section">
        <h1>Presale</h1>
    </div>
    <div id="claim" class="option-section">
        <h1>Claim</h1>
    </div>
    <div id="stake" class="option-section">
        <h1>Stake</h1>
    </div>
</div>

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;
}
<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 class="my_div" id="presale">
    <h1>Presale</h1>
  </div>
  <div class="my_div" id="claim">
    <h1>Claim</h1>
  </div>
  <div class="my_div" id="stake">
    <h1>Stake</h1>
  </div>
</div>

Here's my attempt. It's sensibly the same as @ztom's answer but I tryed avoiding a foreach.

document.querySelectorAll("li").forEach(e => e.addEventListener("click", () => {
  let shown = document.querySelector(".action:not(.d-none)")
  if(shown){
    shown.classList.add("d-none")
    if(e.dataset.id != shown.id){
      document.getElementById(e.dataset.id).classList.remove("d-none")
    }
  }else{
    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 class="action d-none" id="presale">Presale</div>
<div class="action d-none" id="claim">Claim</div>
<div class="action d-none" id="stake">Stake</div>

When it comes to use the same logic on multiple elements, use classes instead of id's and your solution is shortened by default.

With jQuery, it's basically a 2-liner:

in CSS, create a class .hidden with display:none; Your div and li elements should be grouped, using a class too.

Then you can simply refer to this classes and add the show/hide logic by:

$('h1:contains('+$(this).text()+')').parent().toggleClass("hidden"); 
$('h1:not(:contains('+$(this).text()+'))').parent().addClass("hidden");

$('document').ready(function(){
  $('.toggle').on('click',function(){
    $('h1:contains('+$(this).text()+')').parent().toggleClass("hidden"); 
    $('h1:not(:contains('+$(this).text()+'))').parent().addClass("hidden");
  });
});
*,
html {
  color: #fff;
  background-color: black;
}

.hidden
{
  display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">

  <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 class="toggle">Presale</li>
      <li class="toggle">Claim</li>
      <li class="toggle">Stake</li>
      <!-- Here I have other 20 options like the above -->
    </ul>
    <div class="hidden">
      <h1>Presale</h1>
    </div>
    <div class="hidden">
      <h1>Claim</h1>
    </div>
    <div class="hidden">
      <h1>Stake</h1>
    </div>
  </div>
</body>

</html>

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

However, you can try smth like this:

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

function toggle(elem) {
    elems.map(i => {
        let el = document.getElementById(i);
        el.style.display = "none";          
    });
    let active_el = document.getElementById(elem);
    active_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>

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 elements, the panels container, and the list element
// separately 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 allElements = document.querySelectorAll('.list li, .panels .panel');
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 elements  
    allElements.forEach(el => el.classList.remove('active'));

    // And then add an active class to the list item,
    // and the panel where their ids match
    const selector = `[data-id="${id}"]`;
    const item = list.querySelector(`li${selector}`);
    const panel = panels.querySelector(`.panel${selector}`);
    
    item.classList.add('active');
    panel.classList.add('active');
  
  }

}
.panel { display: none; }
.panel h1 { font-size: 1.2em; color: darkblue; }
ul { list-style-type: none; margin-left: 0; padding: 0; }
li { padding: 0.3em; border: 1px solid white; }
li:hover { background-color: thistle; cursor: pointer; }
li.active { border: 1px solid #454545; background-color: lightyellow; }
.panel.active { display: block; }
<ul class="list">
  <li data-id="presale">Presale</li>
  <li data-id="claim">Claim</li>
  <li data-id="stake">Stake</li>
</ul>

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

Additional documentation

  • classList

  • Destructuring assignment

  • Event delegation

  • matches

  • querySelector / querySelectorAll

  • Template/string literals

发布评论

评论列表(0)

  1. 暂无评论