Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
在动态生成列表中实现拖放功能_创想鸟

在动态生成列表中实现拖放功能

在动态生成列表中实现拖放功能

在动态生成的HTML列表中实现拖放(Drag and Drop)功能,关键在于采用事件委托机制来处理事件,而不是直接为每个动态元素绑定监听器。本文将详细讲解如何利用`insertAdjacentHTML`等方法创建动态列表,并通过父元素监听`dragstart`、`dragover`和`drop`事件,结合`dataTransfer`对象和DOM操作(如`cloneNode`、`insertBefore`、`removeChild`),实现列表项的自由拖放排序,并提供同步数据模型的建议。

动态列表拖放功能实现指南

在现代Web应用中,动态生成内容并赋予其交互性是常见需求。其中,列表项的拖放排序功能能够显著提升用户体验。当列表项是使用JavaScript动态插入到DOM中时,传统的事件绑定方式可能会失效或效率低下。本文将介绍一种健壮的方法,通过事件委托和HTML5拖放API,在动态生成的列表中实现拖放功能,即使是使用insertAdjacentHTML等方法创建的元素也能完美支持。

理解动态元素的事件处理挑战

当使用insertAdjacentHTML()或innerHTML等方法向DOM中插入HTML字符串时,这些新创建的元素并不会自动继承或绑定到在它们创建之前设置的事件监听器。如果为每个新元素单独绑定事件,会增加代码复杂性和内存消耗,尤其是在列表项数量较多或频繁变动时。

解决方案是事件委托(Event Delegation)。通过将事件监听器绑定到父元素(例如

容器),我们可以利用事件冒泡机制,捕获发生在子元素上的事件。在事件处理函数中,再通过event.target或event.target.closest()方法判断是哪个子元素触发了事件,并进行相应处理。

HTML5拖放API基础

实现拖放功能主要依赖以下HTML属性和JavaScript事件:

draggable=”true” 属性:将此属性添加到任何HTML元素上,即可使其成为可拖动的元素。通常用于列表项(

)或自定义的拖动句柄。

拖放事件:

dragstart:当用户开始拖动元素时触发。在此事件中,通常需要设置拖动的数据(dataTransfer.setData())。dragover:当可拖动的元素在有效放置目标上拖动时,每隔几百毫秒触发一次。必须调用event.preventDefault()来允许放置。dragleave:当可拖动的元素离开放置目标时触发。drop:当元素在放置目标上被释放时触发。在此事件中,通常会获取拖动的数据(dataTransfer.getData())并执行DOM操作。dragend:拖放操作结束时触发(无论成功或失败)。

示例:实现动态列表的拖放排序

我们将通过一个具体的例子来演示如何实现动态列表的拖放功能。

1. HTML 结构

首先,定义一个空的无序列表容器,用于动态插入列表项。

            动态列表拖放教程        

可拖放的任务列表

2. CSS 样式

为了提供更好的用户体验,添加一些CSS样式,特别是用于拖放过程中的视觉反馈。

ul {  margin: 0;  padding: 0;  list-style: none;}li.task-item {  background-color: #f9f9f9;  border: 1px solid #ddd;  margin-bottom: 5px;  padding: 10px;  cursor: grab; /* 鼠标样式 */}li.task-item:active {  cursor: grabbing; /* 拖动时的鼠标样式 */}li div {  display: flex;  flex-direction: row;  align-items: center;}.description {    margin-left: 10px;    flex-grow: 1;}/* 拖动到目标上方时的样式 */.over {  border-top: solid 2px dodgerblue; /* 在目标上方显示蓝色边框 */}

3. JavaScript 逻辑

这是核心部分,我们将使用事件委托来处理拖放事件。

// 模拟初始数据const activities = [{    index: 1,    description: "学习 JavaScript"  },  {    index: 2,    description: "编写拖放教程"  },  {    index: 3,    description: "优化前端性能"  },  {    index: 4,    description: "提交代码审查"  }];const display = document.getElementById('task-list-display');/** * 渲染活动列表到DOM * @param {Array} activities - 活动数据数组 */const renderList = (activitiesToRender) => {  display.innerHTML = ''; // 清空现有列表,避免重复渲染  activitiesToRender.forEach((activity) => {    display.insertAdjacentHTML('beforeend', `     
  • ${activity.description}

  • `); });};// 1. 监听 dragstart 事件display.addEventListener("dragstart", e => { // 确保拖动的是一个列表项 if (e.target.classList.contains('draggable')) { // 将被拖动元素的data-id存储到dataTransfer对象中 // "text/plain" 是数据类型,e.target.dataset.id 是要传输的数据 e.dataTransfer.setData("text/plain", e.target.dataset.id); // 可选:添加一个类名,用于在拖动过程中提供视觉反馈 e.target.classList.add('dragging'); }});// 2. 监听 dragover 事件display.addEventListener("dragover", e => { // 阻止默认行为,允许放置 e.preventDefault(); // 移除所有列表项上的 'over' 类,防止多个元素高亮 [...display.querySelectorAll('li.task-item')].forEach(li => li.classList.remove('over')); // 找到当前鼠标下方的最近的列表项作为放置目标 const targetLi = e.target.closest('li.task-item'); if (targetLi) { // 为放置目标添加 'over' 类,提供视觉反馈 targetLi.classList.add('over'); }});// 3. 监听 dragleave 事件display.addEventListener("dragleave", e => { // 当拖动元素离开某个列表项时,移除其 'over' 类 const targetLi = e.target.closest('li.task-item'); if (targetLi) { targetLi.classList.remove('over'); }});// 4. 监听 drop 事件display.addEventListener("drop", e => { // 阻止默认行为,防止浏览器进行默认的放置操作(如打开链接) e.preventDefault(); // 移除所有列表项上的 'over' 类 [...display.querySelectorAll('li.task-item')].forEach(li => li.classList.remove('over')); // 获取拖动开始时设置的数据(被拖动元素的data-id) let draggedItemId = e.dataTransfer.getData("text/plain"); let original = document.querySelector(`li[data-id="${draggedItemId}"]`); // 找到放置目标元素 let target = e.target.closest('li.task-item'); // 确保拖动元素和放置目标都存在且不是同一个元素 if (original && target && original !== target) { // 克隆原始节点,保留所有属性和子节点 let clone = original.cloneNode(true); // 插入克隆节点到目标节点之前 display.insertBefore(clone, target); // 移除原始节点 display.removeChild(original); // 可选:移除拖动时添加的类 clone.classList.remove('dragging'); // !!! 重要:更新底层数据模型 !!! // 在DOM操作完成后,需要同步更新activities数组,以保持数据和UI的一致性。 // 这通常涉及到重新排序activities数组,并可能重新渲染或仅更新索引。 // 以下是一个简化的数据更新示例: updateActivityOrder(); }});// 5. 监听 dragend 事件display.addEventListener("dragend", e => { // 拖放操作结束后,移除拖动元素的 'dragging' 类 if (e.target.classList.contains('dragging')) { e.target.classList.remove('dragging'); }});/** * 更新底层数据模型(activities数组)的顺序 * 这是一个简化的示例,实际应用中可能需要更复杂的逻辑来处理数据同步。 */const updateActivityOrder = () => { const updatedActivities = []; // 遍历当前DOM中的所有列表项,根据它们的顺序重建activities数组 display.querySelectorAll('li.task-item').forEach((listItem, index) => { const id = parseInt(listItem.dataset.id); const description = listItem.querySelector('.description').textContent; const completed = listItem.querySelector('input[type="checkbox"]').checked; // 找到原始活动对象并更新其索引 const originalActivity = activities.find(act => act.index === id); if (originalActivity) { updatedActivities.push({ ...originalActivity, index: index + 1 // 根据新顺序重新分配索引 }); } }); // 更新全局的activities数组 activities.length = 0; // 清空旧数组 activities.push(...updatedActivities); // 填充新顺序的活动 console.log("更新后的活动列表:", activities); // 如果需要,可以在这里将更新后的数据保存到localStorage或发送到服务器};// 初始渲染列表renderList(activities);

    代码解析:

    renderList 函数:负责根据activities数组动态生成列表项。注意每个元素都添加了draggable=”true”属性和data-id属性,data-id用于唯一标识列表项,这在拖放过程中非常重要。事件委托:所有的拖放事件监听器都绑定到了display元素(即容器)上,而不是每个。dragstart:e.dataTransfer.setData(“text/plain”, e.target.dataset.id):这是关键一步。它将当前被拖动元素的唯一标识(data-id)存储在dataTransfer对象中。”text/plain”是数据的MIME类型,可以自定义。e.target.classList.add(‘dragging’):为被拖动元素添加一个类名,提供拖动时的视觉反馈。dragover:e.preventDefault():至关重要! 默认情况下,浏览器不允许在大多数HTML元素上放置。调用preventDefault()会阻止这一默认行为,从而允许放置。e.target.closest(‘li.task-item’):用于查找事件目标(e.target)的最近的祖先元素。这是因为e.target可能是内部的

    、

    或。

    classList.add(‘over’):为当前鼠标下方的目标列表项添加高亮样式。drop:e.preventDefault():同样重要,阻止浏览器默认的放置行为。e.dataTransfer.getData(“text/plain”):获取在dragstart中存储的数据,即被拖动元素的data-id。document.querySelector(…):根据data-id找到原始的被拖动元素。original.cloneNode(true):克隆原始节点。true表示深度克隆,包括所有子节点。display.insertBefore(clone, target):将被拖动元素的克隆版本插入到放置目标元素之前。display.removeChild(original):从DOM中移除原始的被拖动元素。updateActivityOrder():在DOM操作完成后,务必调用此函数来同步更新底层的JavaScript数据模型(activities数组)。否则,虽然UI看起来正确,但实际数据仍然是旧的顺序,可能导致后续操作(如保存、加载)出现问题。dragleave 和 dragend:用于清除在拖动过程中添加的临时样式。

    注意事项与最佳实践

    数据模型同步:这是最容易被忽视但至关重要的一点。仅仅修改DOM并不能改变你的JavaScript数据结构。在drop事件处理完成后,务必更新你的activities数组或其他数据模型,使其与DOM的顺序保持一致。updateActivityOrder函数提供了一个基本示例,实际应用中可能需要根据你的数据结构进行更复杂的更新。性能优化:对于非常大的列表,频繁的DOM操作可能影响性能。可以考虑使用虚拟列表(Virtual List)或仅在拖放结束后批量更新DOM。用户体验:提供清晰的视觉反馈,例如在拖动元素和放置目标上添加不同的样式。确保拖放区域足够大且易于操作。考虑边缘情况,例如拖动到列表之外或拖动到空列表时。兼容性:HTML5拖放API在现代浏览器中支持良好,但对于旧版浏览器可能需要Polyfill或备用方案。可访问性:拖放操作可能对键盘用户或使用辅助技术的用户不友好。考虑提供键盘操作的替代方案,以确保可访问性。

    总结

    通过事件委托机制,我们能够有效地在动态生成的HTML列表中实现拖放功能,而无需担心insertAdjacentHTML等DOM操作方式带来的事件绑定问题。核心在于将事件监听器绑定到父容器,利用事件冒泡,并通过dataTransfer对象在dragstart和drop事件之间传递数据,最终结合DOM操作(cloneNode、insertBefore、removeChild)完成列表项的重新排序。最重要的是,不要忘记在DOM更新后同步更新底层的JavaScript数据模型,以确保数据的一致性。遵循这些原则,你就可以构建出功能强大且用户友好的动态拖放列表。

    以上就是在动态生成列表中实现拖放功能的详细内容,更多请关注创想鸟其它相关文章!

    版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
    如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
    发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1529716.html

    赞 (0)
    打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
    解决浏览器中npm包ES模块导入失败:模块打包实践指南
    上一篇 2025年12月20日 21:22:19
    优化jQuery AJAX POST请求:正确处理JSON数据格式
    下一篇 2025年12月20日 21:22:30

    相关推荐

    • Windows&Linux双系统安装流程

      Windows&Linux双系统安装流程Windows&Linux双系统安装流程Windows&Linux双系统安装流程Windows&Linux双系统安装流程

      大家好,很高兴再次见到大家,我是你们的朋友全栈君。 注意事项:在安装Windows与Linux双系统时,建议先安装Windows系统,否则可能会导致grub引导被覆盖的问题。 Windows 10系统安装 制作启动盘(优启通链接)https://www.php.cn/link/219b87ff108…

      2026年9月21日 • 用户投稿
      100
    • VSCode中怎么使用REM_VSCode移动端REM布局编写与换算教程

      答案:REM_VSCode插件可自动将像素转换为REM,需配置rootFontSize和precision,支持自动与手动转换,确保与html的font-size一致,配合media query适配不同屏幕,若插件异常可检查配置、重启或重装,替代工具有postcss-pxtorem、在线转换工具及浏…

      2026年9月21日
      000
    • UC浏览器如何开启省流模式_UC浏览器开启省流模式方法

      开启省流模式可减少UC浏览器流量消耗,通过设置菜单、首页快捷入口或搜索功能三种方式均可启用,系统会压缩网页内容以节省资源。 如果您在使用UC浏览器时希望减少数据流量消耗,尤其是在移动网络环境下,可以通过开启省流模式来优化网页加载方式。该功能会压缩页面内容,降低图片质量和资源体积,从而节省流量。 本文…

      2026年9月21日
      100
    • MySQL性能模式监控资源_MySQL瓶颈定位精确工具

      MySQL性能模式监控资源_MySQL瓶颈定位精确工具MySQL性能模式监控资源_MySQL瓶颈定位精确工具MySQL性能模式监控资源_MySQL瓶颈定位精确工具MySQL性能模式监控资源_MySQL瓶颈定位精确工具

      mysql性能模式通过事件记录精准定位瓶颈,核心步骤包括:1.启用并配置performance schema,选择性开启消费者和仪器;2.监控等待事件、sql语句、阶段、i/o、内存及锁等关键指标;3.分析events_waits_summary_global_by_event_name等表识别资源…

      2026年9月21日 • 用户投稿
      000
    • 三星在电视端首发Perplexity AI应用程序,带来更具创新性AI体验

      10 月 23 日消息,三星电子于美国当地时间 21 日宣布,率先在电视终端推出 perplexity ai 应用程序,为三星电视用户带来更富创新的 ai 使用体验。 借助该应用程序,用户在安排日常生活、查找特定影视内容、创建梦幻体育联赛阵容或策划万圣节活动等场景中,可获得 AI 以卡片式回复框形式…

      2026年9月21日
      000
    • 帕鲁高管回应《幻兽帕鲁:帕鲁农场》疑似碰瓷《宝可梦 pokopia》:乱讲阴谋论

      帕鲁高管回应《幻兽帕鲁:帕鲁农场》疑似碰瓷《宝可梦 pokopia》:乱讲阴谋论帕鲁高管回应《幻兽帕鲁:帕鲁农场》疑似碰瓷《宝可梦 pokopia》:乱讲阴谋论帕鲁高管回应《幻兽帕鲁:帕鲁农场》疑似碰瓷《宝可梦 pokopia》:乱讲阴谋论帕鲁高管回应《幻兽帕鲁:帕鲁农场》疑似碰瓷《宝可梦 pokopia》:乱讲阴谋论

      在不久前的任天堂直面会上,官方公布了一款宝可梦ip的衍生新作——《宝可梦 pokopia》。这款作品让玩家化身一只能够变身成人类训练家的百变怪,主打种田与建造玩法,属于模拟经营类游戏。 视频欣赏: 无独有偶,几天后,《幻兽帕鲁》的开发商PocketPair也正式公布了他们的全新衍生作《幻兽帕鲁:帕鲁…

      2026年9月21日 • 用户投稿
      000
    • 如何使用mysql设计客户信息管理项目

      答案:设计客户信息管理系统需先明确功能需求,再合理规划数据库结构。1. 根据客户需求划分模块,包括客户基本信息、分类、状态、跟进记录等;2. 创建核心表如customers、company_info、follow_ups和users,确保字段完整且符合业务逻辑;3. 在关键字段上建立索引以提升查询效…

      2026年9月21日
      300
    • VSCode怎么新建ipynb文件_VSCode创建和编辑Jupyter笔记本文件教程

      答案:在VSCode中运行Jupyter笔记本需准备Python环境、安装Python扩展并确保安装ipykernel;通过命令面板或文件菜单新建笔记本,编辑时可添加代码或Markdown单元格,运行代码后结果实时显示;通过右上角内核选择器切换Python环境,推荐为不同项目配置独立虚拟环境以避免依…

      2026年9月21日
      000
    • UC浏览器下载速度慢怎么提升_UC浏览器下载速度提升方法

      开启极速模式、更换APN接入点、更改下载路径至内部存储、启用云端加速、清理缓存及切换网络环境可提升UC浏览器下载速度。 如果您在使用UC浏览器下载文件时遇到速度缓慢的问题,这可能是由于网络设置、缓存堆积或下载路径不佳所导致。以下是针对此问题的多种解决方法。 本文运行环境:小米14 Pro,Andro…

      2026年9月21日
      200
    • 夸克Ai搜索如何设置默认_夸克Ai搜索默认引擎更改

      首先在夸克APP中将默认搜索引擎设为AI引擎,再开启相关AI功能开关以启用AI搜索服务。具体步骤:1、打开夸克APP,点击右下角菜单进入设置;2、选择“通用”选项,点击“搜索引擎”;3、选择“AI引擎”或“夸克AI搜索”作为默认服务;4、返回主界面测试搜索关键词,确认AI结果是否展示;5、进入“AI…

      2026年9月21日
      400
    • Linux查看系统日志的常用命令

      答案是查看Linux日志需综合使用journalctl、dmesg、tail、grep等工具。journalctl用于systemd系统集中查询服务及内核日志,支持时间、优先级、字段等多维度过滤;dmesg专注内核启动与硬件问题;tail -f实时监控日志动态;cat、grep、less结合正则和管…

      用户投稿 2026年9月21日
      000
    • Java中设计可扩展类的技巧与经验

      设计可扩展类应优先组合而非继承,通过接口解耦;明确开放protected扩展点并封闭关键逻辑;提供详细文档说明扩展规则;谨慎处理状态与初始化,避免构造器中调用可重写方法;多数场景推荐接口与组合,必要时才允许继承。 在Java中设计可扩展类时,核心目标是让类既能满足当前需求,又便于未来被安全、可控地继…

      2026年9月21日
      100
    • mysql如何实现后台管理系统

      答案:基于MySQL的%ignore_a_1%需设计用户、权限、日志等表结构,通过后端语言实现安全的CRUD接口与JWT认证,前端展示数据并控制权限,确保系统安全稳定。 实现一个基于 MySQL 的后台管理系统,核心是构建一个安全、稳定、可扩展的系统架构,将数据库作为数据存储层,配合后端语言和前端界…

      2026年9月21日
      000
    • Workerman服务启动失败的排查步骤

      workerman服务启动失败的排查步骤如下:1. 检查配置文件,确保无语法错误;2. 查看系统日志,寻找错误线索;3. 检查端口占用情况,确保端口未被占用;4. 调整文件权限,确保workerman有足够权限;5. 检查php环境,确保版本兼容且扩展已安装。 关于Workerman服务启动失败的排…

      2026年9月21日
      200
    • 如何为VSCode设置自定义的代码高亮颜色?

      答案:通过settings.json中的editor.tokenColorCustomizations可自定义VSCode代码高亮颜色,支持全局或特定主题下修改关键字、字符串等元素颜色,结合textMateRules和作用域精确控制,提升代码可读性。 为 VSCode 设置自定义的代码高亮颜色,可以…

      2026年9月21日
      000
    • 百度浏览器自动跳转怎么办 百度浏览器页面跳转广告拦截方法

      百度浏览器自动跳转通常由恶意软件或设置被篡改引起,需检查浏览器设置、清除异常插件、修复快捷方式与注册表,并使用安全软件扫描清理,同时启用广告拦截与隐私保护功能以彻底解决问题。 百度浏览器出现自动跳转,通常不是浏览器本身的问题,而是由恶意软件、插件或设置被篡改导致的。解决这个问题需要从多个方面入手,检…

      2026年9月21日
      100
    • 压力测试(Benchmark)Swoole服务的工具与方法

      进行swoole服务的压力测试是为了确保服务在高负载下稳定运行。1. 选择工具:apache jmeter、wrk、locust。2. 使用方法:jmeter通过脚本配置,wrk通过命令行,locust通过python脚本。3. 注意事项:环境隔离、数据监控、脚本设计。4. 优化点:内存泄漏、连接池…

      2026年9月21日
      000
    • vivo浏览器如何开启硬件加速提升流畅度_vivo浏览器开启硬件加速提升浏览体验的方法

      开启硬件加速可改善vivo手机浏览器卡顿,首先在浏览器设置中查找并开启“硬件加速”功能;若无此选项,可进入系统“开发者选项”,启用“强制进行GPU渲染”;同时建议检查系统更新,确保系统版本最新以保障功能兼容性。 如果您在使用vivo手机内置浏览器时遇到页面加载缓慢或动画卡顿的情况,可能是由于硬件加速…

      2026年9月21日
      000
    • Windows11内存占用率过高怎么解决_Windows11内存占用过高修复方法

      1、通过任务管理器结束高内存占用进程;2、禁用Superfetch(SysMain)服务以降低内存负担;3、优化启动项减少后台负载;4、升级物理内存条提升系统性能。 如果您发现Windows 11系统运行缓慢,并且任务管理器显示内存占用率持续处于高位,这可能是由于后台进程过多、系统服务占用资源或硬件…

      2026年9月21日
      100
    • mysql常用存储引擎有哪些

      InnoDB是现代MySQL应用的首选存储引擎,因其支持事务(ACID)、行级锁、外键约束、崩溃恢复和MVCC,适用于高并发、数据完整性要求高的OLTP场景;MyISAM虽读取快但仅支持表级锁且无事务和外键,适用于读多写少的简单场景,已逐渐被淘汰;Memory引擎将数据存于内存,速度快但易失,适合临…

      2026年9月21日
      000

    发表回复

    登录后才能评论
    关注微信