Friends!
How in JS to hang such a handler on all the buttons of the collection, so that when each of them is clicked, something happens, and the handler is removed from all the buttons in the collection? I managed to make it so that it is removed only from the one I click on (while the class is removed from everyone):
document.querySelectorAll('.string').forEach((field, i, fields) => {
const listen = () => {
const res = document.createElement('div');
res.textContent = field.textContent;
document.getElementById('container').append(res);
fields
.forEach((field, i, fields) => {
field.classList.remove('bright');
field.removeEventListener('click', listen);
});
};
field.addEventListener('click', listen);
});
https://codepen.io/andreymi/pen/RwMOvOq
CodePudding user response:
Rather than adding a listener to all and then removing a listener from all, how about adding a single listener to a container element with event delegation, which can then be removed easily by referencing the function name again?
const buttons = document.querySelectorAll('.string');
const flex = document.querySelector('.flex');
flex.addEventListener('click', function handler(e) {
if (!e.target.matches('.string')) {
return;
}
flex.removeEventListener('click', handler);
const res = document.createElement('div');
res.textContent = e.target.textContent;
document.getElementById('container').append(res);
for (const button of buttons) {
button.classList.remove('bright');
}
});
.wrap {
background: #808080;
font-size: 2em;
}
.flex {
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
gap: 1em;
}
.string {
display: flex;
flex-grow: 1;
height: 80px;
background: #FFFFFF;
justify-content: center;
align-items: center;
font-size: 1em;
}
.bright {
background: #58afd1;
}
<body >
<div >
<button type='button' >1</button>
<button type='button' >2</button>
<button type='button' >3</button>
<button type='button' >4</button>
<button type='button' >5</button>
</div>
<div id='container'></div>
</body>
Or perhaps have the buttons inherit their style from their container.
const buttons = document.querySelectorAll('.string');
const flex = document.querySelector('.flex');
flex.addEventListener('click', function handler(e) {
if (!e.target.matches('.string')) {
return;
}
flex.removeEventListener('click', handler);
const res = document.createElement('div');
res.textContent = e.target.textContent;
document.getElementById('container').append(res);
flex.classList.remove('bright');
});
.wrap {
background: #808080;
font-size: 2em;
}
.flex {
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
gap: 1em;
}
.string {
display: flex;
flex-grow: 1;
height: 80px;
background: #FFFFFF;
justify-content: center;
align-items: center;
font-size: 1em;
}
.bright .string {
background: #58afd1;
}
<body >
<div >
<button type='button' >1</button>
<button type='button' >2</button>
<button type='button' >3</button>
<button type='button' >4</button>
<button type='button' >5</button>
</div>
<div id='container'></div>
</body>
That works for the code in your question, but if the code you're actually using doesn't match what's in your question, then:
- If your buttons have child elements, the target may be one of those child elements, and not one of the buttons, and so to check that the clicked element is a descendant of a button, change to
e.target.closest('.string')
- If, in addition to the above, you may have a
.string
element that's a container of the.flex
, clicks on the.flex
that aren't on the buttons will be registered. If the HTML you're actually using has that, then check that the.string
is a descendant by searching through only descendants of the.flex
container with.contains
. starting from the.flex
container
const buttons = document.querySelectorAll('.string');
const flex = document.querySelector('.flex');
flex.addEventListener('click', function handler(e) {
if (!flex.contains(e.target.closest('.string'))) {
return;
}
flex.removeEventListener('click', handler);
const res = document.createElement('div');
res.textContent = e.target.textContent;
document.getElementById('container').append(res);
flex.classList.remove('bright');
});
.wrap {
background: #808080;
font-size: 2em;
}
.flex {
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
gap: 1em;
}
.string {
display: flex;
flex-grow: 1;
height: 80px;
background: #FFFFFF;
justify-content: center;
align-items: center;
font-size: 1em;
}
.bright .string {
background: #58afd1;
}
<body >
<div >
<button type='button' >1</button>
<button type='button' >2</button>
<button type='button' >3</button>
<button type='button' >4</button>
<button type='button' >5</button>
</div>
<div id='container'></div>
</body>
- If some of your child elements of
.flex
aren't buttons, and some descendants of those elements also have theflex
class, and those.flex
descendants also have some.string
descendants of their own, then to guard against the possibility of selecting one of those buttons instead of one of the immediate children buttons, after selecting the closest.string
, check that its parent element is theflex
the listener was attached to.
const buttons = document.querySelectorAll('.string');
const flex = document.querySelector('.flex');
flex.addEventListener('click', function handler(e) {
const button = e.target.closest('.string');
if (!button || !button.matches('.string') || !button.parentElement === flex) {
return;
}
flex.removeEventListener('click', handler);
const res = document.createElement('div');
res.textContent = e.target.textContent;
document.getElementById('container').append(res);
flex.classList.remove('bright');
});
.wrap {
background: #808080;
font-size: 2em;
}
.flex {
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
gap: 1em;
}
.string {
display: flex;
flex-grow: 1;
height: 80px;
background: #FFFFFF;
justify-content: center;
align-items: center;
font-size: 1em;
}
.bright .string {
background: #58afd1;
}
<body >
<div >
<button type='button' >1</button>
<button type='button' >2</button>
<button type='button' >3</button>
<button type='button' >4</button>
<button type='button' >5</button>
</div>
<div id='container'></div>
</body>
But if the HTML structure you have is what's actually in your question, you may consider there to be no need to complicate things.
CodePudding user response:
your code isnt working because the listen function get recreated for each element
var fields=document.querySelectorAll('.string');
const listen = () => {
const res = document.createElement('div');
res.textContent = this.textContent; // <- note 'this' keyword
document.getElementById('container').append(res);
fields.forEach((field, i, fields) => {
field.classList.remove('bright');
field.removeEventListener('click', listen);
console.log('remove',i);
});
};
fields.forEach((field, i, fields) => {
console.log('add',i);
field.addEventListener('click', listen);
});