jQuery Even Selector in Dynamic Tables
This article explains how the jQuery :even selector
targets rows in dynamically generated tables, highlighting how
zero-based indexing determines row selection, how timing affects
dynamically inserted elements, and how to maintain consistent styling as
the DOM updates.
Zero-Based Index Selection
The jQuery :even selector matches elements based on a
zero-based index within the matched set. It targets elements at index
positions 0, 2, 4, 6, and so on.
Because counting begins at zero:
- Index 0 corresponds to the first row.
- Index 2 corresponds to the third row.
- Index 4 corresponds to the fifth row.
Consequently, running $('tr:even') visually targets the
1st, 3rd, and 5th (visually odd) rows of the table.
Timing and Execution in Dynamic Tables
The :even selector is not an active listener; it only
evaluates elements currently present in the Document Object Model (DOM)
at the exact moment the selector executes.
When generating a table dynamically—such as via AJAX responses,
fetch requests, or JavaScript loops—the selector will fail
to target rows if executed before those elements are attached to the
document. To target dynamically generated rows, you must execute the
selector after the HTML has been appended to the
DOM:
// Example: Generating dynamic rows and applying the selector
$.getJSON('/api/data', function(data) {
let rows = '';
data.forEach(item => {
rows += `<tr><td>${item.name}</td><td>${item.value}</td></tr>`;
});
// 1. Inject rows into the DOM
$('#dataTable tbody').html(rows);
// 2. Target even rows after DOM insertion
$('#dataTable tbody tr:even').addClass('highlight');
});Table Structure and Parent Scoping
Unscoped selectors like $('tr:even') count every
<tr> tag on the page sequentially. If a table
contains a header (<thead>) with a
<tr>, that header row occupies index 0.
This shifts the indices of the body rows (<tbody>),
causing the second row of the body to receive an even index.
To target dynamically created rows accurately, scope the selector to the table body:
$('#dynamicTable tbody tr:even')This isolates the matched set exclusively to the rows inside
<tbody>, ensuring index 0 is always the first body
row.
Handling Dynamic Row Additions and Removals
If rows are added, removed, or sorted after initial styling, existing class assignments do not automatically recalculate.
To fix index mismatches after dynamic table changes, reset and reapply the selector:
function restripeTable() {
$('#dynamicTable tbody tr')
.removeClass('striped')
.filter(':even')
.addClass('striped');
}Alternative: CSS vs. jQuery
:even
For pure styling (such as zebra-striping) on dynamically generated
tables, the CSS pseudo-class :nth-child() is generally
preferred over jQuery's :even.
Unlike jQuery:
- CSS
:nth-child(even)is 1-based (it targets the 2nd, 4th, 6th rows). - CSS automatically recalculates styling when rows are added, deleted, or reordered dynamically without needing manual script execution.