Popular features/Highlight position on drag
Loading live demo…from dhtmlxcode.com
Popular features
Highlight position on drag
Show a crosshair and live start and end dates alongside a task bar while it's dragged, so you can see exactly where it will land.
gantt.message({
text: "<b>Drag</b> any blue task: its row, timeline range and intersected cells of the bottom scale are highlighted while live start/end dates update alongside.",
expire: -1
});
gantt.config.date_format = "%Y-%m-%d";
gantt.config.row_height = 34;
gantt.config.bar_height = 22;
gantt.config.scale_height = 52;
let draggedScaleRange = null;
let scaleRenderFrame = null;
function scale(unit, step, format, highlightOnDrag) {
let config = {
unit: unit,
step: step,
format: format
};
/* Only the bottom scale row receives the drag highlight callback. */
if (highlightOnDrag) {
config.css = function (date) {
if (!draggedScaleRange) {
return "";
}
let cellStart = new Date(date);
let cellEnd = gantt.date.add(cellStart, step || 1, unit);
return cellStart < draggedScaleRange.end && cellEnd > draggedScaleRange.start
? "drag_scale_cell"
: "";
};
}
return config;
}
function setDraggedScaleRange(task) {
if (!task || !task.start_date) {
draggedScaleRange = null;
return;
}
let start = new Date(task.start_date);
let end = task.end_date ? new Date(task.end_date) : new Date(start);
/* Milestones have no duration, but still belong to the cell containing them. */
if (end <= start) {
end = new Date(start.valueOf() + 1);
}
draggedScaleRange = { start: start, end: end };
}
function repaintScale() {
if (scaleRenderFrame !== null) {
return;
}
scaleRenderFrame = requestAnimationFrame(function () {
scaleRenderFrame = null;
gantt.render();
});
}
let zoomNames = ["day", "week", "month", "quarter", "year"];
gantt.ext.zoom.init({
levels: [
{ name: "day", scale_height: 52, scales: [
scale("month", 1, "%F %Y", false),
scale("day", 1, "%j %D", true)
] },
{ name: "week", scale_height: 52, scales: [
scale("month", 1, "%F %Y", false),
scale("week", 1, "Week #%W", true)
] },
{ name: "month", scale_height: 52, scales: [
scale("year", 1, "%Y", false),
scale("month", 1, "%M", true)
] },
{ name: "quarter", scale_height: 52, scales: [
scale("year", 1, "%Y", false),
scale("quarter", 1, function (d) {
return "Q" + (Math.floor(d.getMonth() / 3) + 1);
}, true)
] },
{ name: "year", scale_height: 52, scales: [
scale("year", 1, "%Y", true)
] }
],
element: function () { return gantt.$root.querySelector(".gantt_task"); }
});
DHX.zoom.init(gantt, zoomNames, "zoom_seg");
function setZoom(name) { DHX.zoom.set(name); }
gantt.config.columns = [
{ name: "text", label: "Task", tree: true, width: 220, resize: true },
{ name: "start_date", label: "Start", align: "center", width: 100, resize: true },
{ name: "duration", label: "Days", align: "center", width: 58, resize: true },
{ name: "add", width: 40, resize: true }
];
const formatDraggedTaskDate = gantt.date.date_to_str("%d %M");
gantt.templates.leftside_text = function (startDate, endDate, task) {
if (task.id == gantt.getState().drag_id) {
return '<span class="drag_date_label">' + formatDraggedTaskDate(startDate) + "</span>";
}
};
gantt.templates.rightside_text = function (startDate, endDate, task) {
if (task.id == gantt.getState().drag_id) {
return '<span class="drag_date_label">' + formatDraggedTaskDate(endDate) + "</span>";
}
};
gantt.attachEvent("onBeforeTaskDrag", function (id) {
setDraggedScaleRange(gantt.getTask(id));
repaintScale();
return true;
});
gantt.attachEvent("onTaskDrag", function (id, mode, task) {
setDraggedScaleRange(task);
repaintScale();
});
gantt.attachEvent("onAfterTaskDrag", function () {
draggedScaleRange = null;
repaintScale();
});
gantt.attachEvent("onGanttReady", function () {
function addElement(cfg) {
let div = document.createElement("div");
div.style.position = "absolute";
div.className = cfg.css || "";
div.style.left = cfg.left;
div.style.width = cfg.width;
div.style.height = cfg.height;
div.style.lineHeight = cfg.height;
div.style.top = cfg.top;
if (cfg.html) div.innerHTML = cfg.html;
if (cfg.wrapper) cfg.wrapper.appendChild(div);
return div;
}
/* Cross-hair highlight. */
gantt.addTaskLayer({
renderer: function (task) {
let s = gantt.getTaskPosition(task, task.start_date, task.end_date);
let wrap = document.createElement("div");
addElement({ css: "drag_move_vertical", left: s.left + "px", top: 0,
width: s.width + "px", height: gantt.getVisibleTaskCount() * gantt.config.row_height + "px", wrapper: wrap });
addElement({ css: "drag_move_horizontal", left: 0, top: s.top + "px",
width: "100%", height: (gantt.config.row_height - 1) + "px", wrapper: wrap });
return wrap;
},
filter: function (task) { return task.id == gantt.getState().drag_id; }
});
});
/* --- data ---------------------------------------------------------------- */
let data = {
tasks: [
{ id: 1, text: "Nimbus 3.0 Launch", type: "project", open: true, progress: 0.35 },
{ id: 100, text: "Discovery & Planning", type: "project", parent: 1, open: true, progress: 1 },
{ id: 101, text: "Market research", parent: 100, start_date: "2026-01-05", duration: 5, progress: 1 },
{ id: 102, text: "Competitive analysis", parent: 100, start_date: "2026-01-05", duration: 4, progress: 1 },
{ id: 103, text: "Stakeholder interviews", parent: 100, start_date: "2026-01-12", duration: 4, progress: 1 },
{ id: 104, text: "Requirements doc", parent: 100, start_date: "2026-01-16", duration: 5, progress: 0.9 },
{ id: 105, text: "Roadmap sign-off", parent: 100, type: "milestone", start_date: "2026-01-23" },
{ id: 200, text: "Design", type: "project", parent: 1, open: true, progress: 0.6 },
{ id: 201, text: "UX wireframes", parent: 200, start_date: "2026-01-12", duration: 6, progress: 0.8 },
{ id: 202, text: "Information architecture", parent: 200, start_date: "2026-01-12", duration: 5, progress: 0.8 },
{ id: 203, text: "Visual design system", parent: 200, start_date: "2026-01-23", duration: 7, progress: 0.6 },
{ id: 204, text: "Interactive prototype", parent: 200, start_date: "2026-01-30", duration: 5, progress: 0.5 },
{ id: 205, text: "Usability testing", parent: 200, start_date: "2026-02-04", duration: 4, progress: 0.3 },
{ id: 206, text: "Design review", parent: 200, type: "milestone", start_date: "2026-02-10" },
{ id: 300, text: "Development", type: "project", parent: 1, open: true, progress: 0.25 },
{ id: 301, text: "Core API", parent: 300, start_date: "2026-02-04", duration: 10, progress: 0.5 },
{ id: 302, text: "Auth & billing", parent: 300, start_date: "2026-02-04", duration: 8, progress: 0.4 },
{ id: 303, text: "Web app shell", parent: 300, start_date: "2026-02-14", duration: 9, progress: 0.3 },
{ id: 304, text: "Mobile app", parent: 300, start_date: "2026-02-23", duration: 10, progress: 0.2 },
{ id: 305, text: "Third-party integrations", parent: 300, start_date: "2026-02-14", duration: 6, progress: 0.1 },
{ id: 306, text: "Internal QA pass", parent: 300, start_date: "2026-03-05", duration: 6, progress: 0 },
{ id: 307, text: "Code freeze", parent: 300, type: "milestone", start_date: "2026-03-13" },
{ id: 400, text: "Beta & Hardening", type: "project", parent: 1, open: true, progress: 0 },
{ id: 401, text: "Closed beta rollout", parent: 400, start_date: "2026-03-13", duration: 5, progress: 0 },
{ id: 402, text: "Bug triage & fixes", parent: 400, start_date: "2026-03-18", duration: 7, progress: 0 },
{ id: 403, text: "Performance hardening", parent: 400, start_date: "2026-03-25", duration: 5, progress: 0 },
{ id: 404, text: "Accessibility audit", parent: 400, start_date: "2026-03-27", duration: 4, progress: 0 },
{ id: 405, text: "Beta sign-off", parent: 400, type: "milestone", start_date: "2026-04-02" },
{ id: 500, text: "Launch", type: "project", parent: 1, open: true, progress: 0 },
{ id: 501, text: "Marketing assets", parent: 500, start_date: "2026-02-14", duration: 5, progress: 0 },
{ id: 502, text: "Press kit & outreach", parent: 500, start_date: "2026-02-14", duration: 5, progress: 0 },
{ id: 503, text: "Launch day runbook", parent: 500, start_date: "2026-04-02", duration: 4, progress: 0 },
{ id: 504, text: "Post-launch monitoring", parent: 500, start_date: "2026-04-06", duration: 4, progress: 0 },
{ id: 505, text: "Public launch", parent: 500, type: "milestone", start_date: "2026-04-12" }
],
links: [
{ id: 1, source: 101, target: 103, type: "0" },
{ id: 2, source: 102, target: 104, type: "0" },
{ id: 3, source: 103, target: 104, type: "0" },
{ id: 4, source: 104, target: 105, type: "0" },
{ id: 5, source: 101, target: 201, type: "0" },
{ id: 6, source: 102, target: 202, type: "0" },
{ id: 7, source: 201, target: 203, type: "0" },
{ id: 8, source: 202, target: 203, type: "0" },
{ id: 9, source: 105, target: 203, type: "0" },
{ id: 10, source: 203, target: 204, type: "0" },
{ id: 11, source: 204, target: 205, type: "0" },
{ id: 12, source: 205, target: 206, type: "0" },
{ id: 13, source: 203, target: 301, type: "0" },
{ id: 14, source: 203, target: 302, type: "0" },
{ id: 15, source: 301, target: 303, type: "0" },
{ id: 16, source: 302, target: 303, type: "0" },
{ id: 17, source: 303, target: 304, type: "0" },
{ id: 18, source: 301, target: 305, type: "0" },
{ id: 19, source: 304, target: 306, type: "0" },
{ id: 20, source: 305, target: 306, type: "0" },
{ id: 21, source: 306, target: 307, type: "0" },
{ id: 22, source: 307, target: 401, type: "0" },
{ id: 23, source: 401, target: 402, type: "0" },
{ id: 24, source: 402, target: 403, type: "0" },
{ id: 25, source: 402, target: 404, type: "0" },
{ id: 26, source: 403, target: 405, type: "0" },
{ id: 27, source: 404, target: 405, type: "0" },
{ id: 28, source: 206, target: 501, type: "0" },
{ id: 29, source: 206, target: 502, type: "0" },
{ id: 30, source: 405, target: 503, type: "0" },
{ id: 31, source: 502, target: 503, type: "0" },
{ id: 32, source: 503, target: 504, type: "0" },
{ id: 33, source: 504, target: 505, type: "0" }
]
};
gantt.init("gantt_here");
gantt.parse(data);
gantt.ext.zoom.setLevel("week");
DHX.zoom.syncUI();<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Highlight position on drag: dhtmlxGantt</title>
<script src="../../codebase/dhtmlxgantt.js?v=10.0.0"></script>
<link rel="stylesheet" href="../../codebase/dhtmlxgantt.css?v=10.0.0">
<link rel="stylesheet" href="../common/demo-controls/dhx_controls.css">
<script src="../common/demo-controls/dhx_controls.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap">
<style>
body { display: flex; flex-direction: column; }
#gantt_here { flex: 1 1 auto; min-height: 0; width: 100%; }
.gantt_task_line { border: none; border-radius: 7px; box-shadow: 0 1px 2px rgba(16,24,40,.18); }
.gantt_task_line .gantt_task_progress { border-radius: 7px 0 0 7px; }
.gantt_task_line.gantt_project { border-radius: 5px; }
.drag_move_vertical, .drag_move_horizontal {
background-color: rgba(37,99,235,.12);
box-sizing: border-box;
}
.drag_move_vertical { border-left: 1px solid rgba(37,99,235,.55); border-right: 1px solid rgba(37,99,235,.55); }
.drag_move_horizontal { border-top: 1px solid rgba(37,99,235,.55); border-bottom: 1px solid rgba(37,99,235,.55); }
.drag_date_label {
position: relative;
z-index: 2;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 72px;
margin: 0 8px;
padding: 0 8px;
box-sizing: border-box;
border-radius: 999px;
background: var(--dhx-panel);
box-shadow: 0 1px 2px rgba(16,24,40,.18);
color: var(--dhx-info-ink);
font: 600 12px var(--dhx-font);
line-height: 18px;
white-space: nowrap;
pointer-events: none;
}
:root[data-gantt-theme="dark"] .drag_date_label { box-shadow: 0 1px 2px rgba(0,0,0,.5); }
.gantt_side_content.gantt_link_crossing {
margin-top: 0px;
}
.gantt_scale_cell.drag_scale_cell {
background-color: rgba(37,99,235,.18);
box-shadow: inset 0 -2px 0 rgba(37,99,235,.55);
}
</style>
</head>
<body>
<div class="dhx_header" style="justify-content:center">
<div class="dhx_seg" id="zoom_seg">
<button class="dhx_btn" data-zoom="day" onclick="setZoom('day')">Day</button>
<button class="dhx_btn" data-zoom="week" onclick="setZoom('week')">Week</button>
<button class="dhx_btn" data-zoom="month" onclick="setZoom('month')">Month</button>
<button class="dhx_btn" data-zoom="quarter" onclick="setZoom('quarter')">Quarter</button>
<button class="dhx_btn" data-zoom="year" onclick="setZoom('year')">Year</button>
</div>
</div>
<div id="gantt_here"></div>
</body>
</html>