為現有的浮動元素 (例如面板、調色盤、工具列、檢查器或對話方塊) 新增拖曳功能。拖曳動作不會決定元素的顯示、關閉、堆疊或聚焦方式。
使用專屬帳號代碼
如果元素包含可選取的文字、按鈕、輸入內容或連結,請從專用控點開始拖曳。這樣一來,正常內容仍可使用。讓控制點可透過鍵盤移動,並為控制點提供無障礙元素名稱,說明如何使用方向鍵互動。
請勿將 role="application" 套用至控制代碼。這會不必要地變更輔助技術互動。
將 cursor: grab/grabbing、touch-action: none,以及 user-select: none 和 -webkit-user-select: none 套用至控制代碼,而非整個可拖曳元素,這樣控制代碼外的文字仍可選取,觸控手勢也能維持正常行為:
.drag-handle {
cursor: grab;
touch-action: none;
-webkit-user-select: none;
user-select: none;
}
.drag-handle.dragging {
cursor: grabbing;
}
保留指標位置和可觸及性
將移動範圍限制在可視區域時,請使用與可視區域相對的定位模型,例如 position: fixed。
- 僅針對主要指標按鈕 (
event.button === 0) 開始拖曳。 - 記錄
pointerdown上的指標偏移,並在每次移動時使用,這樣元素就不會在從角落抓取時跳動。 - 指標離開控點後,繼續拖曳。預設為
setPointerCapture(),並清除lostpointercapture的拖曳狀態。如果控點可能會在拖曳過程中移除或重新算繪,請改用文件層級的pointermove、pointerup和pointercancel監聽器,因為在這種情況下會失去擷取功能。 - 您也可以選擇等待移動約 4 像素後,再進入拖曳狀態,這樣點選控點就不會觸發拖曳樣式。
使用指標擷取功能,移動事件監聽器可以保留在控點上:
handle.addEventListener('pointerdown', (event) => {
if (event.button !== 0 || event.target.closest('button, input, select, textarea, a')) return;
const rect = draggable.getBoundingClientRect();
offsetX = event.clientX - rect.left;
offsetY = event.clientY - rect.top;
handle.setPointerCapture(event.pointerId);
});
handle.addEventListener('pointermove', (event) => {
if (!handle.hasPointerCapture(event.pointerId)) return;
handle.classList.add('dragging');
moveTo(event.clientX - offsetX, event.clientY - offsetY);
});
handle.addEventListener('lostpointercapture', () => {
handle.classList.remove('dragging');
});
function moveTo(left, top) {
const maxLeft = Math.max(0, window.innerWidth - draggable.offsetWidth);
const maxTop = Math.max(0, window.innerHeight - draggable.offsetHeight);
draggable.style.left = `${Math.max(0, Math.min(left, maxLeft))}px`;
draggable.style.top = `${Math.max(0, Math.min(top, maxTop))}px`;
}
透過同一個輔助函式限制每個指標和鍵盤位置,讓元素保持可觸及。在可視區域或元素大小變更後,重新限制目前的位置。對於版面配置繁重的元素,建議在拖曳期間進行平移,避免每影格版面配置,然後在放開時將最終位置提交至 left 和 top。
提供鍵盤移動功能
當控點取得焦點時,請使用方向鍵移動元素,並為這些按鍵呼叫 preventDefault()。使用與指標移動相同的限制定位輔助程式。建議一開始先以 15 像素為增量單位,再根據元件的大小和精確度需求調整。
對話方塊專屬附註
<dialog> 或 [popover] 可以使用這項機制,並以其標頭做為控制代碼。請先設定 margin: 0,再指派 left 和 top,因為瀏覽器的預設邊界會干擾手動定位。如要瞭解對話方塊和彈出式視窗的開啟、關閉、焦點和關閉行為,請參閱「宣告式對話方塊和彈出式視窗控制項」和「平台控制項關閉對話方塊」。
備用策略
如果拖曳動作只會重新定位元件,請將其視為漸進增強:在沒有 JavaScript 的情況下,請將元素、其內容和控制項保留在合理的預設位置,並保持可見、可讀和可運作。請勿顯示無法運作的拖曳控點:如果控點是獨立的抓取控制項,請預設隱藏,並在 JavaScript 初始化時顯示;如果標頭兼作控點,請在 JavaScript 初始化時附加拖曳功能。
在 user-select: none 前加入 -webkit-user-select: none,這樣在 Safari 中拖曳時,文字選取功能就會保持停用狀態。