Mastering CSS ‘:nth-child of’ Selector for Elegant Date Range Pickers
This guide demonstrates how to use the CSS ‘:nth-child of’ selector syntax together with minimal JavaScript to build a fully functional date‑range picker, covering selector mechanics, HTML structure, CSS grid layout, range‑selection logic, and visual styling of the selected period.
A date‑range selector lets users pick a start and end date, which is useful for travel bookings, chronological sorting, and schedule planning.
Understanding the “n of selector” syntax
The :nth-child selector can be extended with the n of selector syntax. First, it filters elements that match the given selector, then it selects them based on their order within that filtered set.
<p>Land cultivation...</p>
<p>The earliest cultivation dates back to...</p>
<p class="accent">By 1996, a total...</p>
<p>Many cultivations have...</p>
<p class="accent">Land legislators...</p> .accent {
color: red;
}
.accent:nth-child(2) {
font-weight: bold; /* does not work */
}
:nth-child(2 of .accent) {
text-decoration: underline;
}When two .accent paragraphs contain red text, .accent:nth-child(2) fails because it looks for the second child of the parent, not the second .accent element. In contrast, :nth-child(2 of .accent) correctly selects the second .accent element.
Layout – Building a month calendar
The calendar can be created with a few lines of HTML and CSS grid.
<ul id="calendar">
<li class="day">Mon</li>
<li class="day">Tue</li>
<!-- … until Sat -->
<li class="date">01<input type="checkbox" value="01"></li>
<li class="date">02<input type="checkbox" value="02"></li>
<!-- … until 31 -->
</ul> #calendar {
display: grid;
grid-template-columns: repeat(7, 1fr); /* 7 days per week */
}Selecting exactly two dates
JavaScript is needed to manage the checking/unchecking of the inputs. The “n of selector” syntax remains useful for locating the checked boxes.
When a third date is clicked, the script updates the range by removing one of the previously selected dates based on its position.
const CAL = document.getElementById('calendar');
const DT = Array.from(CAL.getElementsByClassName('date'));
CAL.addEventListener('change', e => {
if (!CAL.querySelector(':checked')) return;
// Add a class when exactly two checkboxes are checked
CAL.className = CAL.querySelector(':nth-child(2 of :has(:checked))') ? 'isRangeSelected' : '';
// When three checkboxes are checked, adjust the range
if (CAL.querySelector(':nth-child(3 of :has(:checked))')) {
switch (DT.indexOf(e.target.parentElement)) {
case DT.indexOf(CAL.querySelector(':nth-child(1 of :has(:checked))')):
CAL.querySelector(':nth-child(2 of :has(:checked)) input').checked = 0;
break;
case DT.indexOf(CAL.querySelector(':nth-child(2 of :has(:checked))')):
CAL.querySelector(':nth-child(3 of :has(:checked)) input').checked = 0;
break;
case DT.indexOf(CAL.querySelector(':nth-child(3 of :has(:checked))')):
CAL.querySelector(':nth-child(2 of :has(:checked)) input').checked = 0;
break;
}
}
});The script first obtains the index of the newly selected date ( DT.indexOf(e.target.parentElement)) and compares it with the first, second, or third selected date using :nth-child(n of :has(:checked)). It then unchecks the appropriate checkbox to keep the range to two dates.
Styling the selected range
/* When two dates are selected */
.isRangeSelected {
/* Select dates that follow the first selected date but are not the second */
:nth-child(1 of :has(:checked)) ~ :not(:nth-child(2 of :has(:checked)) ~ .date) {
background-color: rgb(228 239 253);
}
}With two dates selected, the dates between the first ( 1 of :has(:checked)) and the second ( 2 of :has(:checked)) are given a light‑blue background, visually indicating the range.
The color is declared in a compound selector that targets dates following the first selected date but excludes the second selected date, achieving a clear visual range.
Code Mala Tang
Read source code together, write articles together, and enjoy spicy hot pot together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
