data attributes are readable from CSS as well as JavaScript

A data-* attribute is usually introduced as a place to hang values for JavaScript to read. It is also an ordinary attribute, which means CSS can select on it — so a row’s state can live in one place instead of being mirrored into a class that some code path forgets to update.

<tr data-status="overdue" data-invoice-id="4471">…</tr>

tr[data-status="overdue"] td { background: #fff4f4; }
tr[data-status="paid"]    td { color: #999; }

/* one write, and the row restyles itself */
row.setAttribute('data-status', 'paid');

The trap is jQuery. .data('status') reads the attribute once and then caches the value in an internal store, and .data('status', 'paid') writes only to that store — the attribute never changes, so the selector never matches and the row keeps its old colour. Use .attr() whenever CSS is reading the same value. .data() also coerces on the way out, so data-invoice-id="4471" comes back as a number and fails a strict comparison against a string. Attribute selectors work in IE8 in standards mode and carry the same specificity as a class.