Why is const filter undefined sometimes when i press it..I have used data-filter on buttons to sort store items..but sometimes when i press a button with dataset filter i get undefined
const buttons = document.querySelectorAll('.filter-btn');
const storeItems = document.querySelectorAll('.store-item');
buttons.forEach((button) => {
button.addEventListener('click', (e) => {
e.preventDefault()
const filter = e.target.dataset.filter
console.log(filter)
storeItems.forEach((item) => {
if (filter === 'all') {
item.style.display = 'block'
} else {
if (item.classList.contains(filter)) {
item.style.display = 'block'
} else {
item.style.display = 'none'
}
}
})
})
})
.img-wrap img {
width : 300px;
}
<div >
<div >
<h1>OUR <span>STORE</span></h1>
<div >
<button data-filter="all" ><p>ALL</p></button>
<button data-filter="cakes" ><p>CAKES</p></button>
<button data-filter="cupcakes" ><p>CUPCAKES</p></button>
<button data-filter="sweets" ><p>SWEETS</p></button>
<button data-filter="doughnuts" ><p>DOUGHNUTS</p></button>
</div>
<div >
<div >
<i ></i>
</div>
<input type="text" placeholder="Item...">
</div>
</div>
<div >
<div data-item="cupcakes">
<div >
<img src="https://images.unsplash.com/photo-1614707267537-b85aaf00c4b7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80" alt="">
<i ></i>
</div>
<div >
<h3>Cupcake</h3>
<h3>$5</h3>
</div>
</div>
<div data-item="cupcakes">
<div >
<img src="https://images.unsplash.com/photo-1603532648955-039310d9ed75?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80" alt="">
<i ></i>
</div>
<div >
<h3>Cupcake</h3>
<h3>$5</h3>
</div>
</div>
<div data-item="cakes">
<div >
<img src="https://images.unsplash.com/photo-1578985545062-69928b1d9587?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1089&q=80" alt="">
<i ></i>
</div>
<div >
<h3>Cake</h3>
<h3>$5</h3>
</div>
</div>
<div data-item="sweets">
<div >
<img src="https://images.unsplash.com/photo-1600359756098-8bc52195bbf4?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=688&q=80" alt="">
<i ></i>
</div>
<div >
<h3>Sweets</h3>
<h3>$5</h3>
</div>
</div>
</div>
<div >
<div data-item="doughnuts">
<div >
<img src="https://images.unsplash.com/photo-1533910534207-90f31029a78e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80" alt="">
<i ></i>
</div>
<div >
<h3>Doughnuts</h3>
<h3>$5</h3>
</div>
</div>
<div data-item="cakes">
<div >
<img src="https://images.unsplash.com/photo-1602351447937-745cb720612f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=686&q=80" alt="">
<i ></i>
</div>
<div >
<h3>Cake</h3>
<h3>$5</h3>
</div>
</div>
<div data-item="cupcakes">
<div >
<img src="https://images.unsplash.com/photo-1615837136007-701de6015cfb?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80" alt="">
<i ></i>
</div>
<div >
<h3>Cupcakes</h3>
<h3>$5</h3>
</div>
</div>
<div data-item="sweets">
<div >
<img src="https://images.unsplash.com/photo-1600359746315-119f1360d663?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=688&q=80" alt="">
<i ></i>
</div>
<div >
<h3>Sweets</h3>
<h3>$5</h3>
</div>
</div>
</div>
</div>
CodePudding user response:
The problem is occoured for event deligation. You have a p tag in button. So, for event deligatoin, sometimes click goes to the p tag instead of button. You can fix the issue in this way-
const filter = e.target.closest("button").dataset.filter
CodePudding user response:
e.target
will refer to the innermost item clicked. If you add an event listener to the container, and the container has a child element, e.target
may refer to the container (if you clicked outside the child), or it may refer to the child (if you clicked inside the child). That's what's happening here. If you click directly on one of the texts inside the button, you'll be clicking on a <p>
tag, not the button - and that'll be the target.
<button data-filter="all" ><p>ALL</p></button>
^^^^^^^^^^
Refer to the button
you already have in scope instead of to e.target
.
const buttons = document.querySelectorAll('.filter-btn');
const storeItems = document.querySelectorAll('.store-item');
buttons.forEach((button) => {
button.addEventListener('click', (e) => {
e.preventDefault()
const { filter } = button.dataset;
console.log(filter)
storeItems.forEach((item) => {
if (filter === 'all') {
item.style.display = 'block'
} else {
if (item.classList.contains(filter)) {
item.style.display = 'block'
} else {
item.style.display = 'none'
}
}
})
})
})
.img-wrap img {
width : 300px;
}
<div >
<div >
<h1>OUR <span>STORE</span></h1>
<div >
<button data-filter="all" ><p>ALL</p></button>
<button data-filter="cakes" ><p>CAKES</p></button>
<button data-filter="cupcakes" ><p>CUPCAKES</p></button>
<button data-filter="sweets" ><p>SWEETS</p></button>
<button data-filter="doughnuts" ><p>DOUGHNUTS</p></button>
</div>
<div >
<div >
<i ></i>
</div>
<input type="text" placeholder="Item...">
</div>
</div>
<div >
<div data-item="cupcakes">
<div >
<img src="https://images.unsplash.com/photo-1614707267537-b85aaf00c4b7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80" alt="">
<i ></i>
</div>
<div >
<h3>Cupcake</h3>
<h3>$5</h3>
</div>
</div>
<div data-item="cupcakes">
<div >
<img src="https://images.unsplash.com/photo-1603532648955-039310d9ed75?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80" alt="">
<i ></i>
</div>
<div >
<h3>Cupcake</h3>
<h3>$5</h3>
</div>
</div>
<div data-item="cakes">
<div >
<img src="https://images.unsplash.com/photo-1578985545062-69928b1d9587?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1089&q=80" alt="">
<i ></i>
</div>
<div >
<h3>Cake</h3>
<h3>$5</h3>
</div>
</div>
<div data-item="sweets">
<div >
<img src="https://images.unsplash.com/photo-1600359756098-8bc52195bbf4?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=688&q=80" alt="">
<i ></i>
</div>
<div >
<h3>Sweets</h3>
<h3>$5</h3>
</div>
</div>
</div>
<div >
<div data-item="doughnuts">
<div >
<img src="https://images.unsplash.com/photo-1533910534207-90f31029a78e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80" alt="">
<i ></i>
</div>
<div >
<h3>Doughnuts</h3>
<h3>$5</h3>
</div>
</div>
<div data-item="cakes">
<div >
<img src="https://images.unsplash.com/photo-1602351447937-745cb720612f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=686&q=80" alt="">
<i ></i>
</div>
<div >
<h3>Cake</h3>
<h3>$5</h3>
</div>
</div>
<div data-item="cupcakes">
<div >
<img src="https://images.unsplash.com/photo-1615837136007-701de6015cfb?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80" alt="">
<i ></i>
</div>
<div >
<h3>Cupcakes</h3>
<h3>$5</h3>
</div>
</div>
<div data-item="sweets">
<div >
<img src="https://images.unsplash.com/photo-1600359746315-119f1360d663?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=688&q=80" alt="">
<i ></i>
</div>
<div >
<h3>Sweets</h3>
<h3>$5</h3>
</div>
</div>
</div>
</div>
If you had to gain another reference to the element you attached the event listener to and didn't have button
already in scope, you could use e.currentTarget
instead of e.target
- .target
will refer to the innermost element (the one that the event was dispatched to), but e.currentTarget
will refer to the element the listener is attached to.