pragmatic-drag-androp 如何接管和禁用锚点与图片元素的默认拖拽行为?
【免费下载链接】pragmatic-drag-and-dropFast drag and drop for any experience on any tech stack项目地址: https://gitcode.com/GitHub_Trending/pr/pragmatic-drag-and-drop
页面里的<a>和<img>元素在浏览器中默认就是可拖拽的(相当于自带draggable="true")。当你在页面中使用 pragmatic-drag-and-drop 的 element adapter 时,会遇到两种需求:要么希望这些元素被 element adapter 接管、走你自定义的拖拽逻辑(事件、drop target、预览);要么根本不希望它们被拖出去。这篇文章基于核心包文档中的 Unregistered elements 与 Element adapter 两页,说明这两条路径的具体操作方式。
前提:element adapter 只响应显式注册的元素
在开始之前需要理解一个关键事实(来自文档原文):
- 任何
HTMLElement都可以通过加draggable="true"属性变得可拖拽; <a>和<img>元素默认可拖拽,不需要任何属性;- element adapter只会被显式注册过的元素激活(即调用过
draggable({element})的元素),页面上其他可拖拽元素不会触发它。
这意味着:一个没有注册过的<a>被拖拽时,走的是浏览器原生拖拽行为,你的onDragStart、onDropTargetChange等回调都不会被调用。要接管,就必须注册;要禁用,则直接改属性。
准备:安装核心包
核心包是 vanilla JS 库(用 TypeScript 编写),可与任意视图库搭配使用。安装命令见 核心包文档:
# install the core package yarn add @atlaskit/pragmatic-drag-and-dropelement adapter 位于独立入口点@atlaskit/pragmatic-drag-and-drop/element/adapter(核心包把各模块拆分成各自入口点,以便只引入用到的代码)。
路径一:用 draggable() 接管锚点或图片的拖拽
把目标元素传给draggable()即可。接管后的效果:该元素的拖拽由 element adapter 驱动,你在注册时传入的事件回调(onGenerateDragPreview、onDragStart、onDrag、onDropTargetChange、onDrop)都会生效,它也能被页面上的dropTargetForElements感知。
import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'; draggable({ element: myAnchor, });myAnchor是你对<a>(或<img>)元素的引用,HTMLElement正是支持"draggable"属性的接口。draggable()的返回值是一个 cleanup 函数,在框架环境(如 React)中通常在 effect 的清理阶段调用它,避免重复注册。
接管后需要注意的默认行为:外部数据
文档明确说明:注册锚点或图片为draggable()不会改变浏览器为它们附加外部数据(external data)的默认行为。拖拽<a>时,浏览器会自动附加"text/plain"和"text/uri-list",其中"text/uri-list"的值就是该锚点的 URL。
如果你不希望拖出的数据是锚点默认 URL,可以用getInitialDataForExternal()覆盖或追加:
import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'; draggable({ element: myAnchor, getInitialDataForExternal: () => ({ // overriding the standard "text/uri-list" value 'text/uri-list': someOtherUrl, // adding some new value 'application/x.something-custom': myCustomData, }), });这里someOtherUrl与myCustomData是文档示例中的变量,替换为你自己的值。需要警惕文档中的一条警告:附加给外部消费者的数据可以被用户拖放到的任何外部消费者访问,不要把私密数据放进去。
接管后需要注意的默认行为:拖拽预览
即使元素已注册为draggable(),浏览器在拖拽<a>/<img>时仍会自动生成原生拖拽预览。控制方式与其他元素相同(见 Drag previews),常用两种:
自定义原生预览,用setCustomNativeDragPreview()挂载一个新元素作为预览图:
import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview'; draggable({ element: myAnchor, onGenerateDragPreview: ({ nativeSetDragImage }) => { setCustomNativeDragPreview({ render({ container }) { const preview = document.createElement('div'); preview.textContent = 'My Preview'; Object.assign(preview.style, { padding: '20px', backgroundColor: 'lightpink', }); container.appendChild(preview); }, nativeSetDragImage, }); }, });或者干脆不要原生预览(例如你想自己渲染预览,或做类似 resizing 的体验):
import { disableNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/disable-native-drag-preview'; draggable({ element: myAnchor, onGenerateDragPreview({ nativeSetDragPreview }) { disableNativeDragPreview({ nativeSetDragPreview }); }, });disableNativeDragPreview的实现是渲染一张1x1的透明图片作为原生预览,文档称这是在多个浏览器和设备上效果最好的隐藏方式。
路径二:禁用锚点与图片的默认拖拽
如果你不需要这些元素被拖(无论是拖给浏览器原生行为还是拖给库),最简做法是把属性设为draggable="false":
<a href="/home" draggable="false">Home</a>文档给出的就是这个用法:对<a>和<img>设置draggable="false"可以关闭浏览器对它们的默认draggable="true"设置。<img>同理,把draggable="false"加到<img>标签上即可。
另一条可编程的替代路径是注册后条件禁用——用canDrag()返回false来阻止拖拽:
draggable({ element: myAnchor, // disable dragging canDrag: () => false, });但文档特别提醒了这个用法的边界:当某个draggable的canDrag()返回false时,@atlaskit/pragmatic-drag-and-drop会在内部调用event.preventDefault()取消整个拖拽操作,这会阻止页面上其他draggable被拖动(一旦拖拽事件开始,单个draggable无法单独退出、把机会让给其他元素)。如果只是想禁用某个元素但仍允许父级draggable被拖,文档建议改成条件性地调用draggable()本身(例如在 React 中根据状态决定是否在 effect 里注册),而不是用canDrag()。
对于「就是不想让锚点/图片被拖」这个标题场景,draggable="false"是最直接、副作用最小的方案,因为它不涉及库,也不会影响页面上其他已注册的拖拽。
如何验证结果
文档提供的事件回调可以作为观察点:
- 验证接管成功:在
draggable({ element: myAnchor })中传入onDragStart/onDrop(如() => console.log('drag started')),拖动锚点时如果这些回调开始被调用,说明拖拽已由 element adapter 驱动,而不是浏览器原生行为;也可以注册一个 monitor 全局观察:
import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'; const cleanup = monitorForElements({ onDragStart: () => console.log('Dragging an element'); });- 验证禁用成功:对设置
draggable="false"后的<a>/<img>执行拖拽,此时既不会产生浏览器原生拖拽预览,element adapter 的相关事件(monitor 的onDragStart等)也不会被该元素的拖拽触发。
边界与限制
draggable()注册不改变浏览器为锚点/图片附加外部数据的默认行为;要改数据必须显式提供getInitialDataForExternal()。- 外部数据对任何外部消费者可见,不要附加私密数据(文档警告)。
canDrag()返回false会取消整个拖拽并连带阻止其他draggable,不要把它当作「只禁用单个元素」的手段;需要局部禁用时优先条件注册或draggable="false"。- 浏览器仍会为已注册的
<a>/<img>自动生成拖拽预览,需要自定义或关闭时按上文用setCustomNativeDragPreview()或disableNativeDragPreview()处理。
更多细节可参考仓库中的 unregistered-elements 文档、drag-previews 文档 与 element adapter 文档。
【免费下载链接】pragmatic-drag-and-dropFast drag and drop for any experience on any tech stack项目地址: https://gitcode.com/GitHub_Trending/pr/pragmatic-drag-and-drop
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考