References

Beginner-friendly references for web development, with live, editable examples.

The HTML aria-rowcount attribute

ARIA Accessibility
In short

The aria-rowcount attribute declares a table's real row total when the DOM holds only some of the rows. A <table> showing 50 of 2,000 rows carries aria-rowcount="2000", and so does a container with the grid or treegrid role. While the total isn't known, as in an infinite scroll, use -1.

Overview

The aria-rowcount attribute defines the total number of rows in a table, grid or treegrid. It's meant for tables that keep part of their data out of the DOM, tables that can run to hundreds or even millions of rows. A virtual scroller holding 30 rows of 50,000 would set aria-rowcount="50000".

An endless feed has no total to state, so the value must be -1 while the number is unknown, which tells the browser not to calculate a count. Firefox exposes the -1 on the table. Chrome doesn't, and reports a count based on the rows present and their aria-rowindex values.

A <tbody> is the wrong place for the total, because the rowgroup role doesn't support it. The attribute belongs to the table role, which passes it on to grid and treegrid. A native <table> has the table role by default.

A complete table can drop it, since the browser counts the rows already in the DOM. When rows are left out, the attribute has two jobs, stating the size when it's known and stopping the browser from counting when it isn't.

Syntax

<div role="grid" aria-label="Orders" aria-rowcount="2000">
  <div role="row" aria-rowindex="1">
    <span role="columnheader">Order</span>
  </div>
  <div role="row" aria-rowindex="100">
    <span role="gridcell">1099</span>
  </div>
</div>

Values

Value
An integer (use -1 if the total is unknown).

Example

Live example
<p style="margin: 0 0 6px">Press Next page. The total stays unknown until the last page arrives.</p>
<table id="feed" aria-label="Recent orders" style="border-collapse: collapse">
  <thead><tr id="head"><th scope="col">Order</th><th scope="col">Customer</th></tr></thead>
  <tbody id="rows"></tbody>
</table>
<p style="margin: 6px 0"><button id="next">Next page</button></p>
<p role="status" id="state" style="margin: 0"></p>
<script>
const pages = [
  [['1041', 'Ana Silva'], ['1042', 'Ben Cole'], ['1043', 'Chi Okafor']],
  [['1044', 'Dana Hill'], ['1045', 'Eli Moss'], ['1046', 'Fay Lund']],
  [['1047', 'Gus Reyes'], ['1048', 'Hana Ito']]
];
const table = document.getElementById('feed');
const rows = document.getElementById('rows');
const next = document.getElementById('next');
const state = document.getElementById('state');
let page = 0;
let total = -1;

function show() {
  const last = page === pages.length - 1;
  const first = 2 + page * 3;
  const list = pages[page];
  rows.replaceChildren();
  list.forEach((order, i) => {
    const row = rows.insertRow();
    row.setAttribute('aria-rowindex', first + i);
    order.forEach(text => { row.insertCell().textContent = text; });
  });
  if (last) total = first + list.length - 1;
  table.setAttribute('aria-rowcount', total);
  next.textContent = last ? 'Start over' : 'Next page';
  state.textContent = 'The header and rows ' + first + ' to ' + (first + list.length - 1) + ' are in the DOM. aria-rowcount is ' + total + (total === -1 ? ', because more rows may follow.' : ', which counts the header row.');
}

document.getElementById('head').setAttribute('aria-rowindex', 1);
next.addEventListener('click', () => {
  page = (page + 1) % pages.length;
  show();
});
show();
</script>

Best practices

  • Take the aria-rowcount value from your data source, not from table.rows.length. That property counts the rows in the DOM, which is the number you're correcting.
  • Swap -1 for the real total when the last page of data arrives, so the table stops reporting an unknown size.
  • Show the same information on screen, such as Rows 51 to 100 of 2,000. When only some rows are loaded, let every user know that the table is partial.
  • Give each rendered row an aria-rowindex. A 500-row grid that shows its header row and rows 41 to 43 gives them the indexes 1, 41, 42 and 43.

Frequently asked questions

Does virtual scrolling need aria-rowcount?
Yes, virtual scrolling needs aria-rowcount when it leaves rows out of the DOM. Without it, the browser works out a total from what's rendered.
Does aria-rowcount include the header row?
Yes, aria-rowcount counts header rows, since it covers each row in the full table. The header row is row 1, so 1,000 records under one header row make 1,001.
What happens if aria-rowcount is lower than the real row count?
A low aria-rowcount breaks the rule that it must equal the full number of rows. With 5 rows in the DOM and a value of 2, Chrome's accessibility tree drops the attribute, while Firefox's passes the 2 along.
How is aria-rowcount different from aria-rowindex?
The aria-rowcount attribute gives the total and sits on the table. The aria-rowindex attribute gives one row's position and sits on that row or its cells.
Which JavaScript property reads aria-rowcount?
Read aria-rowcount through the ariaRowCount property, which returns a string, or null if the attribute is missing. The property is supported from Safari 12.1, Chrome 81 and Firefox 119.