You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:无需外部库,用Stencil复现指定可拖拽排序列表

基于Stencil实现无外部库的可拖拽排序列表

需求概述

需要实现一个功能完整的可拖拽排序列表:拖拽列表项时,其他列表项自动上下位移腾出位置,完成拖拽后列表顺序永久更新;要求基于Stencil框架开发,不依赖任何外部拖拽库。当前已实现基础拖拽功能,但存在拖拽时列表项无法向上移动或互换位置的问题。

完整实现示例

以下是独立的Stencil组件,可直接复用实现所需功能:

组件代码(draggable-list.tsx)

import { Component, State, h } from '@stencil/core';

@Component({
  tag: 'draggable-list',
  styleUrl: 'draggable-list.css',
  shadow: true,
})
export class DraggableList {
  @State() items = [
    '列表项 1',
    '列表项 2',
    '列表项 3',
    '列表项 4',
    '列表项 5',
  ];
  private draggedItemIndex: number | null = null;

  handleDragStart(e: DragEvent, index: number) {
    this.draggedItemIndex = index;
    if (e.dataTransfer) {
      e.dataTransfer.setData('text/plain', index.toString());
    }
  }

  handleDragOver(e: DragEvent, targetIndex: number) {
    e.preventDefault();
    if (this.draggedItemIndex === null || this.draggedItemIndex === targetIndex) return;

    const draggedItem = this.items[this.draggedItemIndex];
    const newItems = [...this.items];
    newItems.splice(this.draggedItemIndex, 1);
    newItems.splice(targetIndex, 0, draggedItem);
    
    this.items = newItems;
    this.draggedItemIndex = targetIndex;
  }

  handleDragEnd() {
    this.draggedItemIndex = null;
  }

  render() {
    return (
      <ul>
        {this.items.map((item, index) => (
          <li
            draggable="true"
            onDragStart={(e) => this.handleDragStart(e, index)}
            onDragOver={(e) => this.handleDragOver(e, index)}
            onDragEnd={this.handleDragEnd.bind(this)}
          >
            {item}
          </li>
        ))}
      </ul>
    );
  }
}

样式代码(draggable-list.css)

ul {
  list-style: none;
  padding: 0;
  width: 300px;
}

li {
  padding: 12px 16px;
  margin: 8px 0;
  background: #f5f5f5;
  border-radius: 4px;
  cursor: grab;
  transition: background 0.2s;
}

li:active {
  cursor: grabbing;
}

li.dragging {
  opacity: 0.5;
}

li.drag-over {
  background: #e0e0e0;
}

关键逻辑说明

  1. 拖拽启动:记录当前拖拽项的索引,通过原生dataTransfer传递索引数据,满足拖拽API要求。
  2. 拖拽经过:
    • 必须调用e.preventDefault(),否则无法触发后续drop事件(原生拖拽API规则)。
    • 通过数组splice方法实时重排序数据,更新组件状态触发视图重渲染,实现列表项的动态位移。
    • 实时更新拖拽项索引,确保拖拽过程中方向判断始终准确。
  3. 拖拽结束:清空拖拽项索引,重置组件状态。

解决“无法向上移动/互换”问题的核心

之前的问题大概率是未在dragover事件中实时更新数组顺序和拖拽项索引。本示例中,每次拖拽经过目标项时都会立即调整数据排序并更新状态,从而支持任意方向的位置互换。

内容的提问来源于stack exchange,提问作者Mary Jo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 00:54:12