정렬 가능한 목록

Sortable list

손잡이 아이콘을 끌어 항목의 순서를 바꾸는 세로 목록.

다른 이름: Reorderable listDraggable list
···
html
<ul class="slw" id="slw">
  <li draggable="true"><i class="slgrip">⋮⋮</i>결제 수단 확인</li>
  <li draggable="true"><i class="slgrip">⋮⋮</i>배송지 입력</li>
  <li draggable="true"><i class="slgrip">⋮⋮</i>주문 검토</li>
  <li draggable="true"><i class="slgrip">⋮⋮</i>최종 결제</li>
</ul>
css
.slw{list-style:none;margin:0;padding:0;width:min(220px,90%);display:grid;gap:6px}
.slw li{display:flex;align-items:center;gap:8px;background:var(--surface);border:1px solid var(--line);border-radius:8px;padding:8px 10px;font-size:11px;color:var(--fg);cursor:grab;transition:transform .25s,opacity .25s}
.slw li[data-drag]{opacity:.4}
.slgrip{font-style:normal;letter-spacing:-2px;color:var(--muted);font-size:11px}
js
const list = document.getElementById('slw');
let dragEl = null;
list.querySelectorAll('li').forEach((li) => {
  li.addEventListener('dragstart', () => { auto = false; dragEl = li; li.setAttribute('data-drag', ''); });
  li.addEventListener('dragend', () => li.removeAttribute('data-drag'));
  li.addEventListener('dragover', (e) => {
    e.preventDefault();
    if (!dragEl || dragEl === li) return;
    const rect = li.getBoundingClientRect();
    const before = e.clientY < rect.top + rect.height / 2;
    list.insertBefore(dragEl, before ? li : li.nextSibling);
  });
});
let auto = true;
function swap() {
  if (!auto) return;
  const items = [...list.children];
  const i = Math.floor(Math.random() * (items.length - 1));
  items[i].after(items[i + 1]);
  setTimeout(swap, 1300);
}
setTimeout(swap, 900);

이 항목은 "순서가 바뀐 결과의 목록 UI"를 다루고, 마우스 좌표를 프레임 단위로 좇아 슬롯을 계산하는 구현 로직 자체는 interaction 카테고리의 "드래그로 순서 바꾸기" 항목에서 자세히 다룹니다 — 둘은 같은 패턴을 다른 각도(무엇을 보여줄지 vs 어떻게 계산할지)에서 설명합니다.

칸반 보드와 다른 점은 위에서 다룬 대로 무엇이 바뀌느냐입니다 — 정렬 가능한 목록은 하나의 목록 안에서 순위(몇 번째)만 바뀌고, 항목이 속한 분류 자체는 그대로입니다. 순서 자체가 의미(우선순위, 재생목록 순번, 체크리스트 진행 순서)를 가지므로 마크업은 <ul>보다 순서가 있는 <ol>이 더 정확할 때가 많습니다.

드래그는 마우스 전용 조작이라, 키보드 사용자를 위해 각 항목에 "위로 이동"/"아래로 이동" 버튼을 함께 두거나 항목을 선택한 뒤 화살표 키로 순서를 바꿀 수 있게 하는 대안이 필요합니다.