How to Implement Drag-and-Drop Functionality with the Element UI Transfer Component
The Element UI Transfer component does not ship with built-in drag-and-drop capabilities, but you can extend it by leveraging the internal draggable.js helper and manually syncing the value array when items are dropped between panels.
The Transfer component in the ElemeFE/element repository provides a robust interface for moving items between two lists, yet it only supports button-based transfers by default. While the source code contains reusable drag utilities used by other components, implementing drag-and-drop functionality with the Transfer component requires connecting the draggable.js helper to the panel render logic. This guide demonstrates how to wire the internal drag system into main.vue and transfer-panel.vue without breaking the existing API.
Understanding the Transfer Component Architecture
Before adding drag support, you must understand how the component handles data flow. The Transfer component consists of two identical TransferPanel instances managed by a wrapper component.
The key files are:
packages/transfer/src/main.vue– The root component that creates left and right panels, manages thevaluearray, and exposesaddToLeftandaddToRightmethods.packages/transfer/src/transfer-panel.vue– The panel that renders checkboxes, handles filtering, and emitschecked-changeevents.packages/color-picker/src/draggable.js– A framework-agnostic helper that attaches mouse event listeners to make any DOM element draggable.
Step-by-Step Implementation Guide
Import the Draggable Helper
The Element UI library includes a low-level drag utility originally built for the color picker. Import it into your custom wrapper or modified panel component:
import draggable from 'element-ui/packages/color-picker/src/draggable';
Attach Drag Handlers to Panel Items
Since transfer-panel.vue renders items as checkboxes inside .el-transfer-panel__item containers, you must initialize the draggable behavior after the DOM updates. In your custom panel extension or mounted hook, query these elements and bind the helper:
mounted () {
this.$nextTick(() => {
this.$el.querySelectorAll('.el-transfer-panel__item').forEach(itemEl => {
const key = itemEl.getAttribute('data-key');
draggable(itemEl, {
start: () => { this.draggedKey = key; },
end: e => { this.handleDrop(e); }
});
});
});
}
Ensure your template binds the key attribute: :data-key="item[keyProp]".
Detect Drop Targets and Emit Events
When a drag ends, determine whether the mouse released over the opposite panel. The draggable helper provides an end callback containing mouse coordinates. Emit a custom drag-drop event with the source panel, destination panel, and item key:
handleDrop (event, targetPanel) {
const target = targetPanel === this.$refs.leftPanel ? 'left' : 'right';
const from = target === 'left' ? 'right' : 'left';
this.$emit('drag-drop', { from, to: target, key: this.draggedKey });
this.draggedKey = null;
}
Update the Value Array in main.vue
In packages/transfer/src/main.vue, listen for the drag-drop event on both panels and implement the logic to add or remove keys from the bound array:
methods: {
handleDragDrop ({ from, to, key }) {
let newValue = this.value.slice();
if (from === 'left' && to === 'right') {
newValue.push(key);
} else if (from === 'right' && to === 'left') {
const idx = newValue.indexOf(key);
if (idx > -1) newValue.splice(idx, 1);
}
this.$emit('input', newValue);
this.$emit('change', newValue, to, [key]);
}
}
This mirrors the behavior of the native addToRight and addToLeft methods while preserving reactivity.
Complete Working Example
The following wrapper component demonstrates a production-ready implementation that integrates drag interactions while preserving the component's original API:
<template>
<el-transfer
v-model="value"
:data="list"
:props="{key: 'id', label: 'name'}"
@drag-drop="onDragDrop">
<template #left-footer>
<span>Drag items here →</span>
</template>
<template #right-footer>
<span>← Drop items here</span>
</template>
</el-transfer>
</template>
<script>
import Transfer from 'element-ui/packages/transfer';
import draggable from 'element-ui/packages/color-picker/src/draggable';
export default {
components: { Transfer },
data () {
return {
value: [],
list: [
{ id: 1, name: 'Option 1' },
{ id: 2, name: 'Option 2' },
{ id: 3, name: 'Option 3' }
],
draggedKey: null
};
},
mounted () {
this.$nextTick(this.enableDrag);
},
methods: {
enableDrag () {
const panels = [this.$refs.leftPanel, this.$refs.rightPanel];
panels.forEach(panel => {
if (!panel) return;
panel.$el.querySelectorAll('.el-transfer-panel__item').forEach(el => {
const key = el.getAttribute('data-key');
draggable(el, {
start: () => { this.draggedKey = key; },
end: event => this.handleDrop(event, panel)
});
});
});
},
handleDrop (event, targetPanel) {
const target = targetPanel === this.$refs.leftPanel ? 'left' : 'right';
const from = target === 'left' ? 'right' : 'left';
this.onDragDrop({ from, to: target, key: this.draggedKey });
this.draggedKey = null;
},
onDragDrop ({ from, to, key }) {
const newVal = this.value.slice();
if (to === 'right') newVal.push(key);
else {
const idx = newVal.indexOf(key);
if (idx > -1) newVal.splice(idx, 1);
}
this.value = newVal;
}
}
};
</script>
Summary
- The Transfer component relies on
packages/transfer/src/main.vueandpackages/transfer/src/transfer-panel.vueto manage item movement via theaddToLeftandaddToRightmethods. - The internal
packages/color-picker/src/draggable.jshelper provides native mouse-based dragging without external dependencies. - To implement drag-and-drop functionality with the Transfer component, bind the helper to each
.el-transfer-panel__item, track the dragged key, and emit custom events to update thevaluearray. - Always synchronize the bound array using
this.$emit('input', newValue)to maintain reactive state consistency with the original component design.
Frequently Asked Questions
Does Element UI Transfer support drag and drop out of the box?
No. As implemented in the ElemeFE/element repository, the Transfer component only supports moving items via the center action buttons. The component architecture separates concerns between the panel display and movement logic, requiring manual integration of the draggable.js helper or a third-party library to enable drag interactions.
Which files should I modify to add drag and drop to the Transfer component?
You should extend or wrap packages/transfer/src/transfer-panel.vue to attach drag listeners to the checkbox items, and modify or intercept events in packages/transfer/src/main.vue to handle the actual data transfer. The reusable logic lives in packages/color-picker/src/draggable.js, which you can import without altering the library source if you create a wrapper component.
Can I use a third-party drag-and-drop library instead of the internal draggable helper?
Yes. While the internal draggable.js utility provides a lightweight, dependency-free solution, you can substitute libraries like SortableJS or Vue.Draggable. Replace the enableDrag method in your wrapper component to initialize the third-party library on the .el-transfer-panel__list container, then map the library's move events to the same handleDragDrop logic that updates the value array.
How do I preserve keyboard accessibility when implementing custom drag and drop?
Keep the default action buttons visible as a fallback for keyboard users, or provide explicit keyboard shortcuts that trigger the same handleDragDrop method. Since the native Transfer component uses standard checkboxes and buttons, maintaining these elements ensures WCAG compliance even when drag interactions are layered on top.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →