Bulk action bar
A bar that appears when rows are selected in a data table: how many, what to do with them, and a way to clear. Select some rows below.
Select rows to show the bar
Put the bar after a table and point it at the table with data-table. It stays out of sight until a row is selected, then shows the count and the actions. Clear unselects every row.
| Order | Customer | Total | |
|---|---|---|---|
| #110963 | Emma Wilson | $79.00 | |
| #110962 | Olivia Brown | $26.00 | |
| #110961 | James Carter | $364.50 | |
| #110960 | Noah Davis | $12.00 |
<div class="card">
<div class="scroll-area" tabindex="0">
<table class="table" id="bulk-demo">
<thead>
<tr>
<th scope="col"><input type="checkbox" aria-label="Select all orders" /></th>
<th scope="col">Order</th>
<th scope="col">Customer</th>
<th scope="col" data-align="end">Total</th>
</tr>
</thead>
<tbody>
<tr><td><input type="checkbox" aria-label="Select order 110963" /></td><td>#110963</td><td>Emma Wilson</td><td data-align="end">$79.00</td></tr>
<tr><td><input type="checkbox" aria-label="Select order 110962" /></td><td>#110962</td><td>Olivia Brown</td><td data-align="end">$26.00</td></tr>
<tr><td><input type="checkbox" aria-label="Select order 110961" /></td><td>#110961</td><td>James Carter</td><td data-align="end">$364.50</td></tr>
<tr><td><input type="checkbox" aria-label="Select order 110960" /></td><td>#110960</td><td>Noah Davis</td><td data-align="end">$12.00</td></tr>
</tbody>
</table>
</div>
<div class="bulk-bar" data-table="bulk-demo" role="region" aria-label="Selected orders">
<p aria-live="polite"><span data-bulk-count>0</span> selected</p>
<div>
<button type="button" class="btn" data-variant="secondary">Archive</button>
<button type="button" class="btn" data-variant="danger">Delete</button>
</div>
<button type="button" class="btn" data-variant="ghost" data-bulk-clear>Clear</button>
</div>
</div>
Rows that start selected
A server-rendered selection shows the bar on load. The actions are ordinary buttons: read the rows with tsunami.table.selected(table) in the click handler.
| Order | Customer | Total | |
|---|---|---|---|
| #110963 | Emma Wilson | $79.00 | |
| #110962 | Olivia Brown | $26.00 | |
| #110961 | James Carter | $364.50 | |
| #110960 | Noah Davis | $12.00 |
Nothing exported yet.
<div class="card">
<div class="scroll-area" tabindex="0">
<table class="table" id="bulk-demo2">
<thead>
<tr>
<th scope="col"><input type="checkbox" aria-label="Select all orders" /></th>
<th scope="col">Order</th>
<th scope="col">Customer</th>
<th scope="col" data-align="end">Total</th>
</tr>
</thead>
<tbody>
<tr><td><input type="checkbox" aria-label="Select order 110963" /></td><td>#110963</td><td>Emma Wilson</td><td data-align="end">$79.00</td></tr>
<tr><td><input type="checkbox" aria-label="Select order 110962" checked /></td><td>#110962</td><td>Olivia Brown</td><td data-align="end">$26.00</td></tr>
<tr><td><input type="checkbox" aria-label="Select order 110961" checked /></td><td>#110961</td><td>James Carter</td><td data-align="end">$364.50</td></tr>
<tr><td><input type="checkbox" aria-label="Select order 110960" /></td><td>#110960</td><td>Noah Davis</td><td data-align="end">$12.00</td></tr>
</tbody>
</table>
</div>
<div class="bulk-bar" data-table="bulk-demo2" role="region" aria-label="Selected orders">
<p aria-live="polite"><span data-bulk-count>0</span> selected</p>
<div>
<button type="button" class="btn" data-variant="secondary" id="bulk-export">Export</button>
</div>
<button type="button" class="btn" data-variant="ghost" data-bulk-clear>Clear</button>
</div>
</div>
<p id="bulk-log" aria-live="polite">Nothing exported yet.</p>
<script>
document.getElementById("bulk-export").addEventListener("click", () => {
const rows = tsunami.table.selected(document.getElementById("bulk-demo2"));
document.getElementById("bulk-log").textContent =
"Exported " + rows.map((r) => r.cells[1].textContent).join(", ");
});
</script>
Behavior
The bar is sticky at the bottom of its container. While nothing
is selected it is invisible but keeps its space, so the page does
not move when it appears. The count sits in
[data-bulk-count]; put
aria-live="polite" on its paragraph so screen
readers hear the change. Without data-table, the bar
uses the first .table in its parent. Selection
comes from the checkboxes of the Data table, so select-all,
filters and pages work as they do there.
API
js/bulk-bar.js also works with bars added later.
There is no event of its own: listen for
tsunami:select on the table.
| Function | Description |
|---|---|
tsunami. | Counts the selected rows again. Call it after a script changes the rows without a change event. |
tsunami. | Unselects every row of the bar's table. Same as the Clear button. |
| Event | On | Detail |
|---|---|---|
tsunami:select | table (bubbles) | { rows, count }. Fired by the Data table; the bar updates from it. |
Attributes
| Attribute / element | Values | Description |
|---|---|---|
. | Sticky bar with a top border | |
data-table | table id | The table whose selection the bar shows. Default: the first .table in the parent. |
data-active | true, false | Set by the script: whether any row is selected. |
[data-bulk-count] | Receives the number of selected rows. | |
[data-bulk-clear] | A button that unselects every row. | |
. | The count text, muted | |
. | Group of action buttons |