Skip to content

Reorder Lists

DodaTech 3 min read

title: "Reorder Lists — Building Accessible Sortable Lists" description: "Learn how to build accessible reorderable lists with keyboard support, ARIA roles, and screen reader announcements for sorted items." weight: 8 date: 2026-06-28 lastmod: 2026-06-28 tags: [accessibility, drag-drop]


Accessible reorderable lists combine keyboard controls, ARIA roles, focus management, and screen reader announcements for sorting items.

## What You'll Learn

How to build a complete accessible sortable list component.

## Why It Matters

Sortable lists appear in todo apps, playlists, kanban boards, and settings. Inaccessible sort controls block users from organizing their content.

## Real-World Use

A user opens a grocery list app. They press the "Move Down" button on "Milk" to move it below "Eggs." The list reorders, focus stays on "Milk," and the screen reader announces "Milk moved to position 3."

## Complete Reorderable List

```html
<div role="region" aria-label="Grocery list">
  <h2>Grocery List</h2>

  <div id="reorder-status" role="status" aria-live="polite" aria-atomic="true"></div>

  <ol role="list" aria-label="Grocery items" id="grocery-list">
    <li role="listitem" tabindex="0"
        aria-label="Item 1: Bread"
        data-item="Bread">
      <button aria-label="Move Bread up"
              class="btn-up" onclick="reorder(this, -1)">Up</button>
      <span class="item-text">Bread</span>
      <button aria-label="Move Bread down"
              class="btn-down" onclick="reorder(this, 1)">Down</button>
    </li>
    <li role="listitem" tabindex="0"
        aria-label="Item 2: Eggs"
        data-item="Eggs">
      <button aria-label="Move Eggs up"
              class="btn-up" onclick="reorder(this, -1)">Up</button>
      <span class="item-text">Eggs</span>
      <button aria-label="Move Eggs down"
              class="btn-down" onclick="reorder(this, 1)">Down</button>
    </li>
    <li role="listitem" tabindex="0"
        aria-label="Item 3: Milk"
        data-item="Milk">
      <button aria-label="Move Milk up"
              class="btn-up" onclick="reorder(this, -1)">Up</button>
      <span class="item-text">Milk</span>
      <button aria-label="Move Milk down"
              class="btn-down" onclick="reorder(this, 1)">Down</button>
    </li>
  </ol>
</div>

JavaScript

function reorder(button, direction) {
  const item = button.closest('[role="listitem"]');
  const list = item.parentElement;
  const items = Array.from(list.children);
  const index = items.indexOf(item);
  const newIndex = index + direction;

  if (newIndex < 0 || newIndex >= items.length) return;

  if (direction < 0) {
    list.insertBefore(item, items[newIndex]);
  } else {
    list.insertBefore(item, items[newIndex + 1]);
  }

  item.focus();
  item.setAttribute('aria-label',
    `Item ${newIndex + 1}: ${item.dataset.item}`);
  updateLabels(items);

  const status = document.getElementById('reorder-status');
  status.textContent = `${item.dataset.item} moved to position ${newIndex + 1}`;
}

function updateLabels(items) {
  items.forEach((item, i) => {
    const name = item.dataset.item;
    const btnUp = item.querySelector('.btn-up');
    const btnDown = item.querySelector('.btn-down');
    btnUp.setAttribute('aria-label', `Move ${name} up`);
    btnDown.setAttribute('aria-label', `Move ${name} down`);
    btnUp.disabled = i === 0;
    btnDown.disabled = i === items.length - 1;
  });
}

Common Mistakes

1. No role="list" or role="listitem"

Without explicit roles, screen readers may not recognize the list structure.

2. tabindex not on list items

List items need tabindex="0" so keyboard users can Tab to them.

3. Labels not updated after reorder

aria-label on items and buttons must reflect the new position after reorder.

4. Disabled buttons not handled at boundaries

The Move Up button on the first item and Move Down on the last must be disabled.

5. No status live region

Without a live region, screen readers do not announce the reorder result.

Practice Questions

1. What role should the list container have? role="list" for the container and role="listitem" for each item.

2. Why should aria-label be updated after reorder? To reflect the new position number so screen readers announce the correct position.

3. How do you handle boundary conditions? Disable the Move Up button on the first item and Move Down on the last.

Challenge: Extend the reorderable list to support keyboard shortcuts: Alt+ArrowUp and Alt+ArrowDown to move items.

FAQ

Should I use ol or ul for reorderable lists?

Use ol (ordered list) when position matters. Use ul (unordered list) when order does not matter.

How do I handle very long lists?

Consider pagination or virtual scrolling. Focus management must work within the visible set.

Can I animate list reordering?

Yes, but ensure the animation does not delay focus movement or screen reader announcements.

How do I persist the new order?

After each reorder, send the new order to the server or store it in localStorage.

What about touch reordering?

Touch users should have the same move buttons. Long-press to drag is an additional enhancement.

Mini Project

Build a complete reorderable grocery list with 10 items. Implement move buttons, boundary handling, label updates, and keyboard shortcuts.

What's Next

Learn about Drag Handle design for accessible drag-and-drop interfaces.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro