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.