Home > Software design >  Can an element be hidden and shown without Javascript?
Can an element be hidden and shown without Javascript?

Time:07-09

I'm working on an app where I am using the DOMTokenList for shown and hidden an element using add() and remove() methods but the code is getting big. Example:

button.addEventListener("click", () => {
   elem1.classList.add("display-none");
   elem2.classList.remove("display-none");
   elem3.classList.remove("display-none");
   elem4.classList.add("display-none");
});

button2.addEventListener("click", () => {
   elem1.classList.remove("display-none");
   elem2.classList.add("display-none");
   elem3.classList.add("display-none");
   elem4.classList.remove("display-none");
});

I've seven code parts similar to the example and I started looking others ways but I tried with CSS

.box {
    --displayRed: block;
    --displayGreen: none;
    --displayBlue: none;
    height: 100px;
    width: 100px;
}

.box--red { 
    display: var(--displayRed);
    background-color: #ff0000;
}

.box--green { 
    display: var(--displayGreen);
    background-color: #00ff00;
}

.box--blue { 
    display: var(--displayBlue);
    background-color: #0000ff;
}

.box--red:target {
  --displayRed: block;
  --displayGreen: none;
  --displayBlue: none;
}

.box--green:target {
  --displayRed: none;
  --displayGreen: block;
  --displayBlue: none;
}

.box--blue:target {
  --displayRed: none;
  --displayGreen: none;
  --displayBlue: block;
}
<div>
    <div >
        <div id="boxRed" ></div>
        <div id="boxGreen" ></div>
        <div id="boxBlue" ></div>
    </div>
    
    <nav>
      <ul>
        <li><a href="#boxRed">Show red box</a></li>
        <li><a href="#boxGreen">Show green box</a></li>
        <li><a href="#boxBlue">Show blue box</a></li>
      </ul>
    </nav>
</div>

I was expecting the red box to hide when I clicked the link to show the yellow box but it only scrolled down and didn't hide the red box. I tried to find others solution with videos, Pseudo-classes, Selectors, even with two question that are Can text be hidden and shown using just CSS (no JavaScript code)? [closed] and Ocultar y Mostrar un elemento css but I can't find the perfect idea. I ACCEPT SUGGESTIONS

CodePudding user response:

The trick is defaulting to the red box when there's no hash fragment. I'm not sure you can do that with a pure CSS solution.

I don't know if it's possible to do this with CSS variables, but you can definitely show/hide boxes with the old checkbox/radio button trick:

  1. Define the boxes as display: none.

  2. Have invisible radio buttons immediately prior to the box the radio button will relate to in the same parent:

    <input type="radio" name="box-controller" id="chk-box--red" checked>
    <div id="boxRed" ></div>
    
  3. Have label elements that tick the radio button for that box (via id/for) instead of links.

    <label tab-index="0" for="chk-box--red">Show red box</label>
    

    In the above, note that I've added tab-index to the label so it show sup in the tabbing order. We probably also want CSS that underlines it or similar.

  4. Have a CSS rule that says the .box immediately after a checked radio button should be display: block:

    input[name=box-controller]:checked   .box {
        display: block;
    }
    

Live example:

.box {
    height: 100px;
    width: 100px;
}
/* Hide boxes inside the outer box until/unless shown */
.box .box {
    display: none;
}

label {
    text-decoration: underline;
}

input[name=box-controller] {
    display: none;
}
input[name=box-controller]:checked   .box {
    display: block;
}

.box--red { 
    background-color: #ff0000;
}

.box--green { 
    background-color: #00ff00;
}

.box--blue { 
    background-color: #0000ff;
}
<div>
    <div >
        <input type="radio" name="box-controller" id="chk-box--red" checked>
        <div id="boxRed" ></div>
        <input type="radio" name="box-controller" id="chk-box--green">
        <div id="boxGreen" ></div>
        <input type="radio" name="box-controller" id="chk-box--blue">
        <div id="boxBlue" ></div>
    </div>
    
    <nav>
      <ul>
        <li><label tab-index"0" for="chk-box--red">Show red box</label></li>
        <li><label tab-index"0" for="chk-box--green">Show green box</label></li>
        <li><label tab-index"0" for="chk-box--blue">Show blue box</label></li>
      </ul>
    </nav>
</div>

Important caveat: With the above, the URL is no longer driving the process (it doesn't change as you choose boxes). That's because you can't have a label inside an a, and if you put the a inside the label because it prevents the label doing its job. If having the state in the URL is important to you, look at the JavaScript solution (which doesn't repeat itself) below.


You indicated that your concern with the JavaScript solution was that you were repeating a lot of code. There's no need to do that. Just for what it's worth, here's a JavaScript solution driven the hash fragment that works regardless of how many boxes you have; comments within:

// The function that shows the current box, hiding others
function showCurrentBox() {
    // Get the box to show, defaulting to `boxRed`
    const hash = location.hash.replace(/^#/, "") || "boxRed";

    // Hide any box we've previously shown
    // (Note the use of optional chaining, since `querySelector` may return `null`)
    document.querySelector(".box.showing")?.classList.remove("showing");

    // Show the box (again with optional chaining in case the fragment doesn't identify a box)
    document.getElementById(hash)?.classList.add("showing");
}
// Run on startup
showCurrentBox();
// Run whenver the hash changes
window.addEventListener("hashchange", showCurrentBox);
.box {
    height: 100px;
    width: 100px;
}

/* Hide boxes inside the outer box until/unless shown */
.box .box {
    display: none;
}

.box.showing {
    display: block;
}

.box--red { 
    background-color: #ff0000;
}

.box--green { 
    background-color: #00ff00;
}

.box--blue { 
    background-color: #0000ff;
}
<div>
    <div >
        <div id="boxRed" ></div>
        <div id="boxGreen" ></div>
        <div id="boxBlue" ></div>
    </div>
    
    <nav>
      <ul>
        <li><a href="#boxRed">Show red box</a></li>
        <li><a href="#boxGreen">Show green box</a></li>
        <li><a href="#boxBlue">Show blue box</a></li>
      </ul>
    </nav>
</div>

Adding boxes to that is just a matter of defining their CSS and giving them an ID; the JavaScript code doesn't change.

With this solution, the URL does drive the boxes. Bookmark the URL showing the green box, and that's what shows when you come back to it.

CodePudding user response:

You can keep things DRY with JavaScript by caching all the boxes first, and using data attributes instead of ids. Initialise all the boxes as display: none.

Add one event listener to the ul element (using event delegation to watch for events from its child elements as they "bubble up" the DOM), switch off all the boxes by looping over the boxes and removing a show class, and then adding a show class (display: block;) to the element that corresponds to the data-id of the button that was clicked.

You can add as many boxes/buttons as you want as you can see in this example.

// Cache the boxes, and the list element, and attach
// one listener to the list element
const boxes = document.querySelectorAll('.box');
const list = document.querySelector('ul');
list.addEventListener('click', handleClick);

function handleClick(e) {

  // If the child element that fired the event
  // is a button
  if (e.target.matches('button')) {

    // Destructure the id from its dataset
    const { id } = e.target.dataset;

    // Iterate over all the boxes removing the show class
    boxes.forEach(box => box.classList.remove('show'));

    // Grab the box which has a class that corresponds
    // to the id: `.red` for example
    const current = document.querySelector(`.${id}`);

    // And then show that box
    current.classList.add('show');
  }
}
.box { display: none; padding: 0.3em; color: white; }
.show { display: block; }
.red { background-color: red; }
.green { background-color: green; }
.blue { background-color: blue; }
.orange { background-color: orange; }
.black { background-color: black; }
ul { margin-left: 0; padding: 0; }
li { display: inline; }
button:hover { cursor: pointer; background-color: #fffff0; }
<div>
  <nav>
    <ul>
      <li><button data-id="red">Red</button></li>
      <li><button data-id="green">Green</button></li>
      <li><button data-id="blue">Blue</button></li>
      <li><button data-id="orange">Orange</button></li>
      <li><button data-id="black">Black</button></li>
    </ul>
  </nav>
  <div >
    <div >Red</div>
    <div >Green</div>
    <div >Blue</div>
    <div >Orange</div>
    <div >Black</div>
  </div>
</div>

  • Related