JavaScript/jQuery Draggable - Getting Started
jQuery
Angular
Vue
React
This tutorial guides you through the following steps:
- Add a Draggable to a page.
- Configure Draggable options.
- Handle events.
Each section in this tutorial covers a single configuration step. You can find the complete source code in the following GitHub repository:
Create Draggable
jQuery
Add DevExtreme to your jQuery application and use the following code to create a Draggable:
$(function() {
$("#draggable").dxDraggable({});
});
<html>
<head>
<!-- ... -->
<script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<link rel="stylesheet" href="https://cdn3.devexpress.com/jslib/25.2.4/css/dx.fluent.blue.light.css">
<script type="text/javascript" src="https://cdn3.devexpress.com/jslib/25.2.4/js/dx.all.js"></script>
<script type="text/javascript" src="index.js"></script>
</head>
<body class="dx-viewport">
<div id="draggable"></div>
</body>
</html>Angular
Add DevExtreme to your Angular application and use the following code to create a Draggable:
<dx-draggable></dx-draggable>
import { Component } from '@angular/core';
import { type DxDraggableTypes } from 'devextreme-angular/ui/draggable';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { DxDraggableModule } from 'devextreme-angular';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
DxDraggableModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
{
// ...
"styles": [
"node_modules/devextreme/dist/css/dx.fluent.blue.light.css",
],
}Vue
Add DevExtreme to your Vue application and use the following code to create a Draggable:
<template>
<DxDraggable />
</template>
<script setup lang="ts">
import { DxDraggable, type DxDraggableTypes } from 'devextreme-vue/draggable';
import 'devextreme/dist/css/dx.fluent.blue.light.css';
</script>React
Add DevExtreme to your React application and use the following code to create a Draggable:
import React, { JSX, useState } from 'react';
import { Draggable, type DraggableTypes } from 'devextreme-react/draggable';
import 'devextreme/dist/css/dx.fluent.blue.light.css';
function App(): JSX.Element {
return (
<Draggable />
);
}
export default App;Draggable does not include default visual elements. Specify custom markup as follows:
jQuery
$(function() {
$("#note-1").dxDraggable({});
});
<html>
<head>
<!-- ... -->
</head>
<body class="dx-viewport">
<div id="note-1" class="note">
<div class="color-indicator blue"></div>
<div class="text-container">
<div class="body-text-box">Install New Router in Dev Room</div>
<div class="detail-text-box">Amelia Harper</div>
</div>
</div>
</body>
</html>This example creates four Draggable components inside a common .board container:
$(function() {
$("#note-1").dxDraggable({});
$("#note-2").dxDraggable({});
$("#note-3").dxDraggable({});
$("#note-4").dxDraggable({});
});
<div class="board">
<div id="note-1" class="note">
<div class="color-indicator blue"></div>
<div class="text-container">
<div class="body-text-box">Install New Router in Dev Room</div>
<div class="detail-text-box">Amelia Harper</div>
</div>
</div>
<div id="note-2" class="note">
<div class="color-indicator green"></div>
<div class="text-container">
<div class="body-text-box">👨💻 Launch New Website</div>
<div class="detail-text-box">Brett Wade</div>
</div>
</div>
<div id="note-3" class="note">
<div class="color-indicator red"></div>
<div class="text-container">
<div class="body-text-box">Prepare 2026 Marketing Plan</div>
<div class="detail-text-box">Robert Reagan</div>
</div>
</div>
<div id="note-4" class="note">
<div class="color-indicator yellow"></div>
<div class="text-container">
<div class="body-text-box">🖥️ Approve Personal Computer Upgrade Plan</div>
<div class="detail-text-box">Bart Arnaz</div>
</div>
</div>
</div>Angular
<dx-draggable id="note-1" class="note">
<div class="color-indicator"></div>
<div class="text-container">
<div class="body-text-box">Install New Router in Dev Room</div>
<div class="detail-text-box">Amelia Harper</div>
</div>
</dx-draggable>This example defines Draggable markup within a custom component, note.
This component configures the NoteInfo interface to specify note information. Use this interface within an array to define four notes. Implement the note component within a @for directive to add these notes to the app:
<div>
<dx-draggable
[id]="id()"
[group]="group()"
[boundary]="boundary()"
class="note"
>
<div class="color-indicator"></div>
<div class="text-container">
<div class="body-text-box">{{ task() }}</div>
<div class="detail-text-box">{{ assignee() }}</div>
</div>
</dx-draggable>
</div>
import { Component, input, model } from '@angular/core';
export interface NoteInfo {
id: string;
task: string;
assignee: string;
}
@Component({
selector: 'note',
templateUrl: './note.component.html',
styleUrls: ['../app/app.component.scss'],
})
export class NoteComponent {
id = input<string>();
group = input<string>('notes');
boundary = input<string>('.board')
task = input<string>();
assignee = input<string>();
// ...
}
<div class="boundary-text">Dragging Boundary</div>
<div class="board">
@for (note of notes; track note) {
<note
[id]="note.id"
[task]="note.task"
[assignee]="note.assignee"
></note>
}
</div>
<div class="boundary-text">Dragging Boundary</div>
import { Component } from '@angular/core';
import { type NoteInfo } from 'src/note/note.component';
// ...
export class AppComponent {
notes: NoteInfo[] = [{
id: 'note-1',
task: 'Install New Router in Dev Room',
assignee: 'Amelia Harper',
}, {
id: 'note-2',
task: '👨💻 Launch New Website',
assignee: 'Brett Wade',
}, {
id: 'note-3',
task: 'Prepare 2026 Marketing Plan',
assignee: 'Robert Reagan',
}, {
id: 'note-4',
task: '🖥️ Approve Personal Computer Upgrade Plan',
assignee: 'Bart Arnaz',
}];
}Vue
<template>
<DxDraggable id="note-1">
<div class="note">
<div class="color-indicator"></div>
<div class="text-container">
<div class="body-text-box">Install New Router in Dev Room</div>
<div class="detail-text-box">Amelia Harper</div>
</div>
</div>
</DxDraggable>
</template>This example defines Draggable markup within a custom component, Note.
This component configures two interfaces:
export interface NoteInfo: Specifies note information.interface NoteProps: Defines properties of the Note component.
Create an array of NoteInfo objects to define four notes. Implement the Note component within a <template> tag and use the v-for directive to add these notes to the app:
<template>
<DxDraggable
:id="id"
:group="group"
:boundary="boundary"
>
<div class="note">
<div class="color-indicator"></div>
<div class="text-container">
<div class="body-text-box">{{ task }}</div>
<div class="detail-text-box">{{ assignee }}</div>
</div>
</div>
</DxDraggable>
</template>
<script setup lang="ts">
// ...
export interface NoteInfo {
id: string;
task: string;
assignee: string;
}
interface NoteProps extends NoteInfo {
group?: string;
boundary?: string;
// ...
}
const {
id,
group = 'notes',
boundary = '.board',
task,
assignee,
// ...
} = defineProps<NoteProps>();
</script>
<template>
<div class="boundary-text">Dragging Boundary</div>
<div class="board">
<template v-for="note in notes">
<Note
:id="note.id"
:task="note.task"
:assignee="note.assignee"
/>
</template>
</div>
<div class="boundary-text">Dragging Boundary</div>
</template>
<script setup lang="ts">
// ...
import Note, { type NoteInfo } from "./Note.vue";
const notes: NoteInfo[] = [
{
id: 'note-1',
task: 'Install New Router in Dev Room',
assignee: 'Amelia Harper',
},
{
id: 'note-2',
task: '👨💻 Launch New Website',
assignee: 'Brett Wade',
},
{
id: 'note-3',
task: 'Prepare 2026 Marketing Plan',
assignee: 'Robert Reagan',
},
{
id: 'note-4',
task: '🖥️ Approve Personal Computer Upgrade Plan',
assignee: 'Bart Arnaz',
},
];
</script>React
import { Draggable } from 'devextreme-react/draggable';
function App(): JSX.Element {
return (
<Draggable id="note-1" className="note">
<div className="color-indicator blue"></div>
<div className="text-container">
<div className="body-text-box">Install New Router in Dev Room</div>
<div className="detail-text-box">Amelia Harper</div>
</div>
</Draggable>
);
}This example defines Draggable markup within a custom component, Note.
This component configures two interfaces:
export interface NoteInfo: Specifies note information.interface NoteProps: Defines properties of the Note component.
Create an array of NoteInfo objects to define four notes. Implement the Note component within an Array.map() callback to add these notes to the app:
import { Draggable, type DraggableTypes } from 'devextreme-react/draggable';
import { type NoteProps } from './Note.types';
// ...
function Note({
id,
group = 'notes',
boundary = '.board',
task,
assignee,
// ...
}: NoteProps) {
return (
<Draggable
id={id}
group={group}
boundary={boundary}
>
<div
className={`note ${isOverlapped ? 'overlapped' : ''}`}
>
<div className="color-indicator"></div>
<div className="text-container">
<div className="body-text-box">{task}</div>
<div className="detail-text-box">{assignee}</div>
</div>
</div>
</Draggable>
);
}
export default Note;
export interface NoteInfo {
id: string;
task: string;
assignee: string;
}
export interface NoteProps extends NoteInfo {
group?: string;
boundary?: string;
// ...
}
import Note from './components/Note/Note.tsx';
import { type NoteInfo } from './components/Note/Note.types.tsx';
// ...
const notes: NoteInfo[] = [{
id: 'note-1',
task: 'Install New Router in Dev Room',
assignee: 'Amelia Harper',
}, {
id: 'note-2',
task: '👨💻 Launch New Website',
assignee: 'Brett Wade',
}, {
id: 'note-3',
task: 'Prepare 2026 Marketing Plan',
assignee: 'Robert Reagan',
}, {
id: 'note-4',
task: '🖥️ Approve Personal Computer Upgrade Plan',
assignee: 'Bart Arnaz',
}];
function App(): JSX.Element {
return (
<div className="boundary-text">Dragging Boundary</div>
<div className="board">
{notes.map((note) => (
<Note
{...note}
key={note.id}
/>
))}
</div>
<div className="boundary-text">Dragging Boundary</div>
);
}Configure Draggable Options
This example specifies the following Draggable properties:
- boundary: Constrains Draggable movement inside a container (
.board). - group: Specifies an interaction group (used in events such as onDragMove).
Handle Events
jQuery
This example specifies an onDragStart handler to update the note's Z-index (bring to front):
$(function() {
let z = 1;
$("#note-1").dxDraggable({
onDragStart(e) {
changeZIndex($(e.element[0]));
}
// ...
});
function changeZIndex(el) {
el.css("z-index", z);
z++;
}
});This example also specifies handlers for four common events (not specific to Draggable):
clickdxdragenterdxdragleavedxdrop
To attach handlers to common events, call Draggable.on():
$(function() {
$("#note-1").dxDraggable({
group: 'notes',
// ...
}).on({
"click": handleClick,
"dxdragenter": handleDragEnter,
"dxdragleave": handleDragStop,
"dxdrop": handleDragStop,
});
// ...
function handleClick(e) {
changeZIndex($(e.currentTarget));
}
function handleDragEnter(e) {
e.target.classList.add('overlapped');
}
function handleDragStop(e) {
e.target.classList.remove('overlapped');
}
});
.note.overlapped {
outline: 1px dashed var(--dx-color-primary);
}dxdragenter, dxdragleave, and dxdrop handlers).Angular
This example specifies the following event handlers to update the note's Z-index (bring to front while dragging):
This example also implements a (click) handler on the parent <div> element within the note component:
<div (click)="handleClick()">
<dx-draggable ...
[style.z-index]="currentZIndex"
[class.overlapped]="isOverlapped()"
(onDragMove)="handleDragMove($event)"
(onDragEnd)="handleDragEnd()"
(onDragStart)="handleDragStart()"
>
<!-- ... -->
</dx-draggable>
</div>
import { type DxDraggableTypes } from 'devextreme-angular/ui/draggable';
// ...
export class NoteComponent {
// ...
overlappedComponentId: string | null = null;
currentZIndex: number = 0;
updateZIndex(): void {
const updatedZIndex = this.zIndex() + 1;
this.currentZIndex = updatedZIndex;
this.zIndex.update(() => updatedZIndex);
console.log(this.currentZIndex)
}
handleDragStart(): void {
this.updateZIndex();
}
handleDragMove(e: DxDraggableTypes.DragMoveEvent): void {
if (e.toComponent !== e.component) {
const toComponentId = e.toComponent.element().id;
this.startOverlap()(toComponentId);
this.overlappedComponentId = toComponentId;
} else {
this.stopOverlap()();
this.overlappedComponentId = null;
}
}
handleDragEnd(): void {
this.stopOverlap()();
}
handleClick(): void {
this.updateZIndex();
}
}
<div class="boundary-text">Dragging Boundary</div>
<div class="board">
@for (note of notes; track note) {
<note ...
[isOverlapped]="overlappedId === note.id"
[(zIndex)]="zIndex"
[startOverlap]="startOverlap"
[stopOverlap]="stopOverlap"
></note>
}
</div>
<div class="boundary-text">Dragging Boundary</div>
import { Component } from '@angular/core';
// ...
export class AppComponent {
// ...
zIndex: number = 0;
overlappedId: string | null = null;
startOverlap: (id: string) => void = (id) => {
this.overlappedId = id;
}
stopOverlap: () => void = () => {
this.overlappedId = null;
}
handleDragEnd: () => void = () => {
this.stopOverlap();
}
}
.note.overlapped {
outline: 1px dashed var(--dx-color-primary);
}Vue
This example specifies the following event handlers to update the note's Z-index (bring to front while dragging):
This example also implements a @click handler on the parent <div> element within the Note component:
<template>
<DxDraggable ...
:style="{zIndex: currentZIndex}"
@drag-start="handleDragStart"
@drag-move="handleDragMove"
@drag-end="handleDragEnd"
>
<div
:class="`note ${isOverlapped ? 'overlapped' : ''}`"
@click="handleClick"
>
<!-- ... -->
</div>
</DxDraggable>
</template>
<script setup lang="ts">
import { ref } from "vue";
// ...
const overlappedComponentId = ref<string | null>(null);
const currentZIndex = ref<number>(0);
function updateZIndex() {
const zIndexToUpdate = zIndex.value + 1;
currentZIndex.value = zIndexToUpdate;
zIndex.value = zIndexToUpdate;
}
function handleDragStart() {
updateZIndex();
}
function handleDragMove(e: DxDraggableTypes.DragMoveEvent) {
if (e.toComponent !== e.component) {
const toComponentId = e.toComponent.element().id;
startOverlap(toComponentId);
overlappedComponentId.value = toComponentId;
} else {
stopOverlap();
overlappedComponentId.value = null;
}
}
function handleDragEnd() {
stopOverlap();
}
function handleClick() {
updateZIndex();
}
</script>
<template>
<div class="boundary-text">Dragging Boundary</div>
<div class="board">
<template v-for="note in notes">
<Note ...
:is-overlapped="overlappedId === note.id"
v-model:z-index="zIndex"
:start-overlap="startOverlap"
:stop-overlap="stopOverlap"
/>
</template>
</div>
<div class="boundary-text">Dragging Boundary</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
// ...
const overlappedId = ref<string | null>(null);
const zIndex = ref<number>(0);
function startOverlap(id: string) {
overlappedId.value = id;
}
function stopOverlap() {
overlappedId.value = null;
}
</script>
<style scoped>
.note.overlapped {
outline: 1px dashed var(--dx-color-primary);
}
</style>React
This example specifies the following event handlers to update the note's Z-index (bring to front while dragging):
This example also implements an onClick handler on the parent <div> element within the Note component:
import React, {
useCallback, useMemo, useRef, useState,
} from 'react';
// ...
function Note({ ... }: NoteProps) {
const overlappedComponentId = useRef<string | null>(null);
const [currentZIndex, setCurrentZIndex] = useState(0);
const style = useMemo(() => ({ zIndex: currentZIndex }), [currentZIndex]);
const updateZIndex = useCallback(() => {
const zIndexToUpdate = zIndex.current + 1;
setCurrentZIndex(zIndexToUpdate);
zIndex.current = zIndexToUpdate;
}, []);
const handleDragStart = useCallback(() => {
updateZIndex();
}, []);
const handleDragMove = useCallback((e: DraggableTypes.DragMoveEvent) => {
if (e.toComponent !== e.component) {
const toComponentId = e.toComponent.element().id;
startOverlap(toComponentId);
overlappedComponentId.current = toComponentId;
} else {
stopOverlap();
overlappedComponentId.current = null;
}
}, []);
const onClick = useCallback(() => {
updateZIndex();
}, [updateZIndex]);
const handleDragEnd = useCallback(() => {
stopOverlap();
}, []);
return (
<Draggable ...
style={style}
onDragStart={handleDragStart}
onDragMove={handleDragMove}
onDragEnd={handleDragEnd}
>
<div
className={`note ${isOverlapped ? 'overlapped' : ''}`}
onClick={onClick}
>
{/* ... */}
</div>
</Draggable>
);
}
import { useCallback, useRef, useState } from 'react';
// ...
function App(): JSX.Element {
const zIndex = useRef(0);
const [overlappedId, setOverlappedId] = useState<string | null>(null);
const startOverlap = useCallback((id: string) => {
setOverlappedId(id);
}, []);
const stopOverlap = useCallback(() => {
setOverlappedId(null);
}, []);
return (
<div className="demo-container dx-theme-fluent-typography">
<div className="boundary-text">Dragging Boundary</div>
<div className="board">
{notes.map((note) => (
<Note
{...note}
key={note.id}
isOverlapped={overlappedId === note.id}
zIndex={zIndex}
startOverlap={startOverlap}
stopOverlap={stopOverlap}
/>
))}
</div>
<div className="boundary-text">Dragging Boundary</div>
</div>
);
}
.note.overlapped {
outline: 1px dashed var(--dx-color-primary);
}Refer to the following repository for the complete source code of this tutorial: