You could use a reset() function.
With two different event listeners:
(like in your example)
const reset = () => {
btns.forEach(btn => {
btn.removeEventListener('click', second);
btn.addEventListener('click', first);
});
};
You would call it before you change the event listeners in first() or before your console.log() in second(),
const first = (e) => {
e.stopImmediatePropagation();
reset();
e.target.removeEventListener('click', first);
e.target.addEventListener('click', second);
};
const second = (e) => {
e.stopImmediatePropagation();
reset();
console.log(`${e.target.getAttribute('data-val')} ${e.target.id}`);
};
or if you click outside of a button.semafore.
document.addEventListener('click', reset);
Working example:
const btns = document.querySelectorAll('.semafore');
const reset = () => {
btns.forEach(btn => {
btn.removeEventListener('click', second);
btn.addEventListener('click', first);
});
};
const first = (e) => {
e.stopImmediatePropagation();
reset();
e.target.removeEventListener('click', first);
e.target.addEventListener('click', second);
};
const second = (e) => {
e.stopImmediatePropagation();
reset();
console.log(`${e.target.getAttribute('data-val')} ${e.target.id}`);
};
btns.forEach(b => { b.addEventListener('click', first); });
document.addEventListener('click', reset);
.semafores { display: block; width: auto; height: 40px; }
.r { background: red; }
.g { background: green; }
.b { background: blue; }
.semafore {
float: left;
width: 30px;
height: 30px;
border: 1px solid black;
border-radius: 15px;
display: block;
text-align: center;
font-family: sans-serif;
color: white;
transition: width 0.5s ease-in-out, color 0.2s;
}
.semafore.r:focus,
.semafore.g:focus,
.semafore.b:focus { width: auto; padding: 0 10px; }
.semafore.r:focus::after,
.semafore.g:focus::after,
.semafore.b:focus::after { content: attr(data-val); }
<div class="semafores">
<button id="1r" data-val='remove' class="r semafore"></button>
<button id="1g" data-val='change' class="g semafore"></button>
<button id="1b" data-val='report' class="b semafore"></button>
</div>
<div class="semafores">
<button id="2r" data-val='remove' class="r semafore"></button>
<button id="2g" data-val='change' class="g semafore"></button>
<button id="2b" data-val='report' class="b semafore"></button>
</div>
With data attributes:
const reset = () => {
const active_btn = document.querySelector('[data-active="true"]');
if (active_btn) {
active_btn.dataset.active = 'false';
}
};
You could handle both clicks in one handler:
const handleClick = (e) => {
e.stopImmediatePropagation();
if (e.target.dataset.active == 'true') {
reset();
console.log(`${e.target.getAttribute('data-val')} ${e.target.id}`);
}
else {
reset();
e.target.dataset.active = 'true';
}
};
Working example:
const btns = document.querySelectorAll('.semafore');
const reset = () => {
const active_btn = document.querySelector('[data-active="true"]');
if (active_btn) {
active_btn.dataset.active = 'false';
}
};
const handleClick = (e) => {
e.stopImmediatePropagation();
if (e.target.dataset.active == 'true') {
reset();
console.log(`${e.target.getAttribute('data-val')} ${e.target.id}`);
}
else {
reset();
e.target.dataset.active = 'true';
}
};
btns.forEach(b => { b.addEventListener('click', handleClick); });
document.addEventListener('click', reset);
.semafores { display: block; width: auto; height: 40px; }
.r { background: red; }
.g { background: green; }
.b { background: blue; }
.semafore {
float: left;
width: 30px;
height: 30px;
border: 1px solid black;
border-radius: 15px;
display: block;
text-align: center;
font-family: sans-serif;
color: white;
transition: width 0.5s ease-in-out, color 0.2s;
}
.semafore.r:focus,
.semafore.g:focus,
.semafore.b:focus { width: auto; padding: 0 10px; }
.semafore.r:focus::after,
.semafore.g:focus::after,
.semafore.b:focus::after { content: attr(data-val); }
<div class="semafores">
<button id="1r" data-val='remove' data-active="false" class="r semafore"></button>
<button id="1g" data-val='change' data-active="false" class="g semafore"></button>
<button id="1b" data-val='report' data-active="false" class="b semafore"></button>
</div>
<div class="semafores">
<button id="2r" data-val='remove' data-active="false" class="r semafore"></button>
<button id="2g" data-val='change' data-active="false" class="g semafore"></button>
<button id="2b" data-val='report' data-active="false" class="b semafore"></button>
</div>