I am trying to track the center of a carousel where the elements can be dynamically fully replaced when looping. With the full element replacement in mind, simply watching for the index to change and adding an active
class causes a moment of jank while the styles and transitions apply after the carousel has already moved, producing a poor effect.
To overcome this I decided to use CSS to target the 2nd out of 3 elements so that regardless of how the change occurs, the centre of the three should always be targeted via css. Hoever, this isn't working as expected since the nth-child doesn't appear to recalculate when I change the targetted set of elements via JavaScript.
I have reduced the example to find that the nth-child
css rule I'm using doesn't appear to be recalculated when changing the set of active
elements using JavaScript.
For example:
set of five elements, three of which have the additional active
class. Targeting nth-child(3n+2)
to aim at the middle of the three active classes at all times.
On a button press, a javascript function removes the first active class and applies it to the next non-active element instead.
Expected: the new centre element should be selected by the nth-child
css selector
Actual: the css rules remain applied to the same element and do not get updated with what should be the new position.
Visually, the effect looks like this:
Before button press:
After button press:
Here's my test case code, It's a simple as I can get it and replicates exactly the behaviour I'm seeing in my real-world case.
// extremely crude but always have 3 active blocks and shift them by 1 on each button press, n.b. it doesn't wrap so it'll just break if you go off the end.
nav = e => {
var active = document.querySelectorAll('.stuff.active');
var first = active[0];
var last = active[2];
first.classList.remove('active');
document.querySelector('.active + :not(.active)').classList.add('active');
}
.list-of-stuff {
height: 450px;
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: center;
}
.stuff {
flex-grow: 1;
background-color: blue;
}
.active {
background-color: green;
}
.active:nth-child(3n+2) {
background-color: red;
}
button {
display: block;
background: orange;
color: white;
border: 0;
outline: 0;
padding: 10px 15px;
margin-bottom: 10px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<button onclick="nav()">go</button>
<div class="list-of-stuff">
<div class="stuff active"></div>
<div class="stuff active"></div>
<div class="stuff active"></div>
<div class="stuff"></div>
<div class="stuff"></div>
<div class="stuff"></div>
<div class="stuff"></div>
<div class="stuff"></div>
<div class="stuff"></div>
<div class="stuff"></div>
</div>
</body>
</html>
Live example: