Drag and Drop HTML

Ziehen können, nicht müssen

Karte, Griff, Zielspalte: jeder Zug braucht eine Alternative per Schaltfläche.

Website prüfen
Beispiel im Prüfbericht Markierter Befund
Markierter Befund · Kritisch · WCAG 4.1.2Tauschen-Button ohne Namen

Ein Zug, zwei Wege

Die Karte darf springen

Ziehen bleibt möglich. Klick, Touch und Tastatur erreichen dasselbe Ergebnis.

Illustratives BedienmodellAufgabenboard · „Angebot prüfen“ verschieben

Zu erledigen

2 Karten
  1. Vertrag gegenlesen
  2. Ausgewählte KarteAngebot prüfen
    Nach obenNach untenNach Erledigt
Ziehbewegung
  1. Greifen
  2. Gedrückt halten
  3. Bewegen
  4. Loslassen
Alternative ohne Ziehen
  1. Schaltfläche erreichen
  2. „Nach Erledigt“ aktivieren

KlickTouchEnterLeertaste

Erledigt

1 Karte
  1. Angebot prüfenPlatz 1 von 1
Inhalt der Statusmeldung„Angebot prüfen: Platz 1 von 1 in Erledigt.“role="status"
Zwei getrennte Anforderungen

WCAG 2.1.1 verlangt Tastaturbedienung. WCAG 2.5.7 verlangt zusätzlich eine klick- oder tippbare Alternative ohne Ziehen. Dieselben Schaltflächen können beides leisten.

Was nach jedem Zug synchron bleiben muss

1DOM-Reihenfolge

Die Karte wird in die echte Liste verschoben, nicht nur optisch versetzt.

2Fokus

Nach einer Schaltflächenaktion bleibt der Fokus auf der mitbewegten Aktion.

3Status

Kartenname, Position, Anzahl und Zielspalte stehen in der Live-Region.

Vollständiges Grundmuster

HTML: Karte, Alternativen und Live-Region

Der Griff ist visuell. Die benannten Schaltflächen tragen die Alternative.

<div class="brett">
  <section aria-labelledby="offen-title">
    <h2 id="offen-title">Zu erledigen</h2>
    <ul class="spalte" data-name="Zu erledigen">
      <li class="karte" draggable="true">
        <span class="griff" aria-hidden="true">⠿</span>
        <span class="titel">Vertrag gegenlesen</span>
        <a href="/aufgaben/1">Details</a>
        <div class="aktionen">
          <button type="button" data-move="up"
            aria-label="Vertrag gegenlesen nach oben">Nach oben</button>
          <button type="button" data-move="down"
            aria-label="Vertrag gegenlesen nach unten">Nach unten</button>
          <button type="button" data-move="lane"
            aria-label="Vertrag gegenlesen nach Erledigt">Nach Erledigt</button>
        </div>
      </li>
      <li class="karte" draggable="true">
        <span class="griff" aria-hidden="true">⠿</span>
        <span class="titel">Angebot prüfen</span>
        <a href="/aufgaben/2">Details</a>
        <div class="aktionen">
          <button type="button" data-move="up"
            aria-label="Angebot prüfen nach oben">Nach oben</button>
          <button type="button" data-move="down"
            aria-label="Angebot prüfen nach unten">Nach unten</button>
          <button type="button" data-move="lane"
            aria-label="Angebot prüfen nach Erledigt">Nach Erledigt</button>
        </div>
      </li>
    </ul>
  </section>

  <section aria-labelledby="fertig-title">
    <h2 id="fertig-title">Erledigt</h2>
    <ul class="spalte" data-name="Erledigt"></ul>
  </section>
</div>

<p id="move-status" role="status" aria-atomic="true"></p>

JavaScript: beide Wege ändern dieselben Listen

const board = document.querySelector('.brett');
const lanes = [...board.querySelectorAll('.spalte')];
const status = document.querySelector('#move-status');
let dragged = null;

function cards(lane) {
  return [...lane.querySelectorAll(':scope > .karte')];
}

function announce(card) {
  const lane = card.parentElement;
  const rows = cards(lane);
  const title = card.querySelector('.titel').textContent;
  status.textContent = title + ': Platz ' + (rows.indexOf(card) + 1) +
    ' von ' + rows.length + ' in ' + lane.dataset.name + '.';
}

function syncControls() {
  lanes.forEach((lane, laneIndex) => {
    const rows = cards(lane);
    rows.forEach((card, index) => {
      const title = card.querySelector('.titel').textContent;
      const up = card.querySelector('[data-move="up"]');
      const down = card.querySelector('[data-move="down"]');
      up.disabled = index === 0;
      down.disabled = index === rows.length - 1;
      up.setAttribute('aria-label', title + ' nach oben');
      down.setAttribute('aria-label', title + ' nach unten');
      const laneButton = card.querySelector('[data-move="lane"]');
      laneButton.textContent = laneIndex === 0
        ? 'Nach Erledigt' : 'Nach Zu erledigen';
      laneButton.setAttribute('aria-label', title + ' ' + laneButton.textContent);
    });
  });
}

board.addEventListener('click', (event) => {
  const button = event.target.closest('button[data-move]');
  if (!button) return;
  const card = button.closest('.karte');
  const lane = card.parentElement;

  if (button.dataset.move === 'up') {
    lane.insertBefore(card, card.previousElementSibling);
  } else if (button.dataset.move === 'down') {
    lane.insertBefore(card.nextElementSibling, card);
  } else {
    const target = lanes.find((item) => item !== lane);
    target.append(card);
  }

  syncControls();
  button.focus();
  announce(card);
});

board.addEventListener('dragstart', (event) => {
  dragged = event.target.closest('.karte');
  if (!dragged) return;
  event.dataTransfer.effectAllowed = 'move';
  event.dataTransfer.setData('text/plain', '');
});

board.addEventListener('dragover', (event) => {
  if (!dragged || !event.target.closest('.spalte')) return;
  event.preventDefault();
  event.dataTransfer.dropEffect = 'move';
});

board.addEventListener('drop', (event) => {
  const lane = event.target.closest('.spalte');
  if (!dragged || !lane) return;
  event.preventDefault();
  const before = event.target.closest('.karte');
  before && before !== dragged
    ? lane.insertBefore(dragged, before)
    : lane.append(dragged);
  syncControls();
  announce(dragged);
  dragged = null;
});

board.addEventListener('dragend', () => {
  dragged = null;
});

syncControls();

So prüfen Sie das Ergebnis

Nur TastaturVerschieben Sie die zweite Karte nach oben und in die andere Spalte. Der Fokus bleibt erhalten.

Nur Klick oder TouchErreichen Sie dieselben Positionen ohne gedrückte Zeigerbewegung.

VorleseprogrammNach jedem Zug sind Kartenname, Platz, Anzahl und Spalte als Status verfügbar.

ZeigerZiehen Sie eine Karte vor eine andere. Danach stimmt die DOM-Reihenfolge mit der Ansicht überein.

Aus dem Prüfbericht

Drei Regeln an einer Karte

Zum Mitnehmen

Die ziehbare Karte, korrigiert

Ein Name, ein Mindestmaß, eine Alternative zum Ziehen.

Korrektur in 3 Schritten ansehen
  1. Dem Griff Namen geben und Link trennen

    Der Griff bekommt einen Namen, der Link wandert daneben. So entstehen zwei getrennte Bedienelemente mit zwei klaren Tabstopps.

    KorrekturvergleichSchritt 1 · HTML

    Vorher

    <button class="griff" draggable="true">
      <span aria-hidden="true">⠿</span>
      <a href="/karte/12">Details</a>
    </button>

    Nachher

    <button class="griff" draggable="true" aria-label="Karte verschieben">
      <span aria-hidden="true">⠿</span>
    </button>
    <a href="/karte/12">Details</a>

    Markierte Zeilen wurden geändert

    So prüfen Sie es Tabben Sie über die Karte. Sie halten zweimal: am Griff und am Link.

  2. Den Griff auf 44 Pixel bringen

    Die aktive Fläche zählt, nicht das Symbol. WCAG 2.5.8 nennt im Regelfall 24 mal 24 CSS-Pixel oder ausreichenden Abstand. Das Beispiel nutzt 44 mal 44 Pixel.

    KorrekturvergleichSchritt 2 · CSS

    Vorher

    .griff {
      width: 18px;
      height: 18px;
    }

    Nachher

    .griff {
      display: inline-grid;
      place-items: center;
      min-width: 44px;
      min-height: 44px;
    }

    Markierte Zeilen wurden geändert

    So prüfen Sie es Messen Sie den Griff in den Entwicklerwerkzeugen. Die Fläche ist 44 mal 44 Pixel.

  3. Verschieben ohne Ziehen anbieten

    Ziehen ist eine Bewegung, die nicht jede Hand ausführt. Zwei Schaltflächen erreichen dasselbe Ziel mit einem Klick oder der Leertaste.

    KorrekturvergleichSchritt 3 · HTML

    Vorher

    <li class="karte" draggable="true">
      <span>Angebot prüfen</span>
    </li>

    Nachher

    <li class="karte" draggable="true">
      <span>Angebot prüfen</span>
      <button type="button" data-move="up">Nach oben</button>
      <button type="button" data-move="down">Nach unten</button>
    </li>

    Markierte Zeilen wurden geändert

    So prüfen Sie es Verschieben Sie eine Karte nur mit der Tastatur. Sie landet in derselben Reihenfolge.

Werbung

Kostenloser Website-Barrierefreiheits-Check

Website auf Barrierefreiheit prüfen.

Technische WCAG-Warnsignale erkennen. Kritische Stellen zuerst prüfen.

Der Website-Check ist bereit.

Werbung

Ein letzter Schritt

Prüfen, bevor Sie gehen.

Finden Sie automatisch erkennbare Barrieren auf Ihrer eigenen Website.

Website prüfen

Kostenlos starten · keine Installation

Automatisches Screening. Tastaturbedienung und Screenreader bleiben manuelle Prüfung.