Reputation: 1289
I am trying to create a small script which shows different hidden HTML
items after clicking on a button or link.
Requirements:
display: none
not hardcoded into source code but located in the stylesheet<style>display: none;</style>
Script base:
I used this Pure Javascript Show/Hide Toggle script as base and modified it.
My modified Code Snippet:
var button = document.querySelector('.toggle-button');
var menu = document.querySelector('.item');
button.addEventListener('click', function (event) {
if (menu.style.display == "") {
menu.style.display = "none";
button.innerHTML = "Show more items";
} else {
menu.style.display = "";
button.innerHTML = "Hide items";
}
}
);
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item hidden-item" style="display: none;">Item 3</div>
<div class="item hidden-item" style="display: none;">Item 4</div>
<div class="item hidden-item" style="display: none;">Item 5</div>
<button class="toggle-button">Show more items</button>
Codepen:
https://codepen.io/anon/pen/ePxeVE
Problem:
My modification does not show the hidden items. I already spent some hours on the issue but I don't get the script working. Any ideas from a JavaScript pro how to show and hide multiple elements when clicking the toggle button?
Upvotes: 1
Views: 8881
Reputation: 65806
.querySelector()
returns the first element that matches the query. You'll need .querySelectorAll()
to get all the matches and then you'll have to loop over all the found elements to determine if they should be hidden or not. Avoid .getElementsByClassName()
as this returns a "live node list" that is not right for most use cases and impedes performance.
You'll also probably want another class that simply indicates whether an item is hideable or not so that when all items are shown, you'll know which of them to hide again later.
Also, avoid inline styles when possible. Just set up CSS classes ahead of time and either add or remove them with element.classList.add()
and element.classList.remove()
.
Lastly, only use .innerHTML
when the string you are getting/setting actually contains HTML that needs to be parsed and when you are in complete control of that string. When used improperly, it wastes resources and can open up security holes in your code. When the string you are working with does not contain any HTML or doesn't need to be parsed by the HTML parser, use .textContent
.
Comments are inline below:
var button = document.querySelector('.toggle-button');
button.addEventListener('click', function (event) {
// Get all the hideable items into an Array so that .forEach() can be safely used to loop over them
let hiddenItems = Array.prototype.slice.call(document.querySelectorAll('.hideable'));
// Are we hiding or showing?
let showing = button.textContent === "Show more items";
// Loop over the items
hiddenItems.forEach(function(item){
if(!showing){
// If we are hiding, then add the .hidden-item class
item.classList.add("hidden-item");
} else {
// Otherwise remove the .hidden-item class
item.classList.remove("hidden-item");
}
// Update the button text:
button.textContent = button.textContent === "Show more items" ? "Hide items" : "Show more items";
});
});
/* This could be placed in an external .css file */
.hidden-item { display:none; }
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item hidden-item hideable">Item 3</div>
<div class="item hidden-item hideable">Item 4</div>
<div class="item hidden-item hideable">Item 5</div>
<button class="toggle-button">Show more items</button>
Upvotes: 2
Reputation: 370689
You need querySelectorAll
instead of querySelector
to retrieve and iterate over multiple elements. I'd suggest using a .hidden
class instead, and then you can iterate over the .hidden-item
s and just toggle the class on each:
const button = document.querySelector('.toggle-button');
const hiddenItems = document.querySelectorAll('.hidden-item');
let isHidden = true;
button.addEventListener('click', () => {
button.textContent = isHidden
? 'Hide items'
: 'Show more items';
isHidden = !isHidden;
hiddenItems.forEach(item => item.classList.toggle('hidden'));
});
.hidden {
display: none;
}
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item hidden-item hidden">Item 3</div>
<div class="item hidden-item hidden">Item 4</div>
<div class="item hidden-item hidden">Item 5</div>
<button class="toggle-button">Show more items</button>
Upvotes: 2