Let's say you have 4 elements and only one of them can have class active
. The active element is updated a few times per second (here it's a dummy example in the snippet).
A naive way is to do:
- remove
active
from every element - set
active
to the currently chosen element
The problem is that if the chosen element hasn't changed, we are removing the class, and resetting it again (which can cause display glitch), which is useless.
Of course we could add another rule to not remove if it's still the chosen one, but the code would become less readable.
Question: is there a standard pattern to do this?
var i = 0;
setInterval(() => {
i = 1;
document.querySelectorAll('.active').forEach(item => item.classList.remove('active'));
document.querySelector('#item' Math.floor(i /4)).classList.add('active');
}, 500);
.active { color: red; }
<div id="item0" >item 0</div>
<div id="item1">item 1</div>
<div id="item2">item 2</div>
<div id="item3">item 3</div>
CodePudding user response:
Recap
So it sounds like you you are looking for a more elegant way of adding and removing an active
class to a set of elements. Rather than removing the active
class from all elements and then adding it back to the currently active element (which can cause potential issues/visual glitches). And it seems like you are trying to avoid adding too much code to check whether or not the active
class needs to be removed/added (in the event that active element has not changed).
Potential Solution
Personally, I don't think this is the best way of doing this (using the ternary operator for code execution can be messy), but I think the code is simple enough and fairly readable overall.
Essentially, you can merge the code to add or remove the active
class. The add()
function will only add a class if it is currently not on the element, and the opposite applies to the remove()
function (only removing a class if it exists). This means you can call the add()
function on the active element and not worry about it causing any visual glitches if the active element has not changed.
Using this logic, we just need to loop through all possible elements and then using the ternary operator, we can check to see if an element is the currently active one. If so, call add()
(which won't matter if the active element did not change), otherwise, call remove()
.
let i = 0,
interval;
interval = setInterval(() => {
i = 1;
document.querySelectorAll("[id^=item]").forEach(el => (el.id == `item${Math.floor(i / 4)}`) ? el.classList.add("active") : el.classList.remove("active"));
if(i >= 24) clearInterval(interval);
}, 500);
.active { color: red; }
<div id="item0" >item 0</div>
<div id="item1">item 1</div>
<div id="item2">item 2</div>
<div id="item3">item 3</div>
<div id="item4">item 4</div>
<div id="item5">item 5</div>
<div id="item6">item 6</div>
Notes
This does assume you can loop through all possible active elements. In this situation they all have an id
that starts with item
. If there is no identifier that can be used to loop through all possible active elements, it adds a bit more code.
CodePudding user response:
There's no need to improve your code, because style rendering are blocked when JavaScript are executing. No glitches can be seen as the "intermediate" DOM won't be shown. You can try the following snippet:
window.setTimeout(function () {
let eles = document.getElementsByTagName("p");
eles[0].classList.remove("active");
for (let i = 1; i <= 100_000; i ) {
eles[0].textContent = String(i);
}
eles[1].classList.add("active");
}, 2000);
.active {
color: red;
}
<p >1</p>
<p>2</p>