Popular features/Custom tooltips
Loading live demo…from dhtmlxcode.com
Popular features
Custom tooltips
Render a designed tooltip card for task bars and add targeted tooltips for dependency links and date-scale cells.
gantt.plugins({ tooltip: true });
gantt.message({
text: "Hover a <b>task</b>, a <b>dependency line</b>, or the <b>date scale</b> · templates.tooltip_text renders the task card, ext.tooltips.tooltipFor attaches bespoke tooltips to links and scale cells",
expire: -1
});
gantt.config.date_format = "%Y-%m-%d";
gantt.config.row_height = 54;
gantt.config.bar_height = 38;
gantt.config.scale_height = 56;
gantt.config.min_column_width = 140;
gantt.config.scales = [
{ unit: "month", step: 1, format: "%F %Y" },
{ unit: "week", step: 1, format: function (d) { return "Wk " + gantt.date.date_to_str("%W")(d); } }
];
gantt.templates.tooltip_date_format = gantt.date.date_to_str("%M %j, %Y");
let OWNERS = {
ava: { name: "Ava Reed", color: "#7c3aed" },
leo: { name: "Leo Marsh", color: "#2563eb" },
mia: { name: "Mia Fontana",color: "#059669" }
};
function statusOf(task) {
if (task.type === gantt.config.types.milestone) return { label: "Milestone", bg: "#eef1f4", fg: "#334155", color: "#0f172a" };
if (task.progress >= 1) return { label: "Done", bg: "#e7f6ee", fg: "#067a4a", color: "#16a34a" };
if (task.progress > 0) return { label: "In progress", bg: "#e0edff", fg: "#1d4ed8", color: "#2563eb" };
return { label: "Not started", bg: "#eef1f4", fg: "#64748b", color: "#94a3b8" };
}
gantt.config.columns = [
{ name: "text", label: "Activity", tree: true, width: 230, 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 }
];
/* Colour bars by status so the tooltip matches the chart. */
gantt.attachEvent("onTaskLoading", function (task) {
if (task.type !== gantt.config.types.project) task.color = statusOf(task).color;
return true;
});
let STATUS_DARK = {
"Milestone": { bg: "#2a313b", fg: "#c1c8d6", color: "#8fa3bd" },
"Done": { bg: "#12331f", fg: "#5fd99a", color: "#22c55e" },
"In progress": { bg: "#1a2c47", fg: "#8fbcf7", color: "#3b82f6" },
"Not started": { bg: "#2a313b", fg: "#98a2b3", color: "#94a3b8" }
};
function themed(st) {
if (document.documentElement.getAttribute("data-gantt-theme") !== "dark") return st;
let dark = STATUS_DARK[st.label];
return dark ? { label: st.label, bg: dark.bg, fg: dark.fg, color: dark.color } : st;
}
/* --- the task tooltip card ---------------------------------------------- */
gantt.templates.tooltip_text = function (start, end, task) {
let st = themed(statusOf(task));
let pct = Math.round((task.progress || 0) * 100);
let owner = OWNERS[task.owner];
let html = '<div class="tt">' +
'<div class="tt__bar" style="background:' + st.color + '"></div>' +
'<div class="tt__body">' +
'<div class="tt__title">' + task.text + "</div>" +
'<span class="tt__chip" style="background:' + st.bg + ';color:' + st.fg + '">' + st.label + "</span>";
if (task.type !== gantt.config.types.milestone) {
html += '<div class="tt__row"><span>Start</span><b>' + gantt.templates.tooltip_date_format(start) + "</b></div>" +
'<div class="tt__row"><span>Finish</span><b>' + gantt.templates.tooltip_date_format(new Date(end.valueOf() - 1)) + "</b></div>" +
'<div class="tt__row"><span>Progress</span><b>' + pct + '%</b></div>' +
'<div class="tt__prog"><i style="width:' + pct + "%;background:" + st.color + '"></i></div>';
} else {
html += '<div class="tt__row"><span>Date</span><b>' + gantt.templates.tooltip_date_format(start) + "</b></div>";
}
if (owner) {
html += '<div class="tt__owner"><span class="tt__av" style="background:' + owner.color + '">' +
DHX.ui.initials(owner.name) + "</span>" + owner.name + "</div>";
}
return html + "</div>";
};
function linkTypeToString(type) {
let L = gantt.config.links;
if (type == L.start_to_start) return "Start to start";
if (type == L.start_to_finish) return "Start to finish";
if (type == L.finish_to_start) return "Finish to start";
if (type == L.finish_to_finish) return "Finish to finish";
return "";
}
gantt.attachEvent("onGanttReady", function () {
let tooltips = gantt.ext.tooltips;
/* Dependency link tooltip. */
tooltips.tooltipFor({
selector: ".gantt_task_link",
html: function (event, node) {
let linkId = node.getAttribute(gantt.config.link_attribute);
if (!linkId) return;
let link = gantt.getLink(linkId);
return '<div class="tt--link"><b>' + linkTypeToString(link.type) + "</b><br>" +
gantt.getTask(link.source).text + " → " + gantt.getTask(link.target).text + "</div>";
}
});
/* Date-scale tooltip. */
tooltips.tooltipFor({
selector: ".gantt_scale_cell",
html: function (event) {
let pos = gantt.utils.dom.getRelativeEventPosition(event, gantt.$task_scale);
return '<div class="tt--date">' + gantt.templates.tooltip_date_format(gantt.dateFromPos(pos.x)) + "</div>";
}
});
});
/* --- data ---------------------------------------------------------------- */
/* Three workstreams run in parallel (rather than one long sequential chain),
* so each task spans more days and its label has room to render in full. */
let data = {
tasks: [
{ id: 1, text: "Autumn Campaign", type: "project", open: true, progress: 0.4 },
{ id: 11, text: "Creative concepts", parent: 1, start_date: "2026-09-01", duration: 12, progress: 1, owner: "ava" },
{ id: 12, text: "Landing page & email build", parent: 1, start_date: "2026-09-13", duration: 15, progress: 0.6, owner: "leo" },
{ id: 13, text: "Paid & organic launch", parent: 1, start_date: "2026-09-28", duration: 18, progress: 0.3, owner: "mia" },
{ id: 14, text: "Campaign launch", parent: 1, type: "milestone", start_date: "2026-10-16" },
{ id: 15, text: "Post-launch analytics & reporting", parent: 1, start_date: "2026-10-16", duration: 14, progress: 0, owner: "mia" },
{ id: 16, text: "Case study & wrap report", parent: 1, start_date: "2026-10-30", duration: 10, progress: 0, owner: "leo" },
{ id: 2, text: "Platform Migration", type: "project", open: true, progress: 0.4 },
{ id: 21, text: "Architecture & requirements", parent: 2, start_date: "2026-09-01", duration: 14, progress: 1, owner: "leo" },
{ id: 22, text: "Core services migration", parent: 2, start_date: "2026-09-15", duration: 20, progress: 0.5, owner: "mia" },
{ id: 23, text: "Data migration & QA", parent: 2, start_date: "2026-10-05", duration: 16, progress: 0.2, owner: "ava" },
{ id: 24, text: "Cutover", parent: 2, type: "milestone", start_date: "2026-10-21" },
{ id: 25, text: "Post-migration monitoring", parent: 2, start_date: "2026-10-21", duration: 12, progress: 0, owner: "leo" },
{ id: 3, text: "Ops Readiness", type: "project", open: true, progress: 0.4 },
{ id: 31, text: "Vendor onboarding", parent: 3, start_date: "2026-09-08", duration: 12, progress: 1, owner: "ava" },
{ id: 32, text: "Support runbook & tooling", parent: 3, start_date: "2026-09-20", duration: 15, progress: 0.4, owner: "mia" },
{ id: 33, text: "Team training sessions", parent: 3, start_date: "2026-10-05", duration: 10, progress: 0.1, owner: "leo" },
{ id: 34, text: "Go-live readiness review", parent: 3, type: "milestone", start_date: "2026-10-15" },
{ id: 35, text: "Post go-live support", parent: 3, start_date: "2026-10-15", duration: 14, progress: 0, owner: "ava" },
{ id: 4, text: "Customer Success", type: "project", open: true, progress: 0.4 },
{ id: 41, text: "Onboarding playbook design", parent: 4, start_date: "2026-09-01", duration: 12, progress: 1, owner: "ava" },
{ id: 42, text: "CRM integration setup", parent: 4, start_date: "2026-09-13", duration: 18, progress: 0.5, owner: "leo" },
{ id: 43, text: "Customer training rollout", parent: 4, start_date: "2026-10-01", duration: 15, progress: 0.2, owner: "mia" },
{ id: 44, text: "Feedback loop live", parent: 4, type: "milestone", start_date: "2026-10-16" },
{ id: 45, text: "Success metrics dashboard", parent: 4, start_date: "2026-10-16", duration: 14, progress: 0, owner: "ava" },
{ id: 46, text: "Renewal playbook update", parent: 4, start_date: "2026-10-30", duration: 10, progress: 0, owner: "leo" }
],
links: [
{ id: 1, source: 11, target: 12, type: "0" },
{ id: 2, source: 12, target: 13, type: "0" },
{ id: 3, source: 13, target: 14, type: "0" },
{ id: 4, source: 14, target: 15, type: "0" },
{ id: 5, source: 15, target: 16, type: "0" },
{ id: 6, source: 21, target: 22, type: "0" },
{ id: 7, source: 22, target: 23, type: "0" },
{ id: 8, source: 23, target: 24, type: "0" },
{ id: 9, source: 24, target: 25, type: "0" },
{ id: 10, source: 31, target: 32, type: "0" },
{ id: 11, source: 32, target: 33, type: "0" },
{ id: 12, source: 33, target: 34, type: "0" },
{ id: 13, source: 34, target: 35, type: "0" },
{ id: 14, source: 41, target: 42, type: "0" },
{ id: 15, source: 42, target: 43, type: "0" },
{ id: 16, source: 43, target: 44, type: "0" },
{ id: 17, source: 44, target: 45, type: "0" },
{ id: 18, source: 45, target: 46, type: "0" }
]
};
gantt.init("gantt_here");
gantt.parse(data);<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Custom tooltips: 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; }
.gantt_tooltip {
border: none !important;
border-radius: 12px !important;
box-shadow: 0 8px 24px rgba(16,24,40,.16), 0 2px 6px rgba(16,24,40,.10) !important;
padding: 0 !important;
font-family: var(--dhx-font) !important;
background: var(--dhx-panel) !important;
overflow: hidden;
}
.tt { width: 244px; }
.tt__bar { height: 4px; }
.tt__body { padding: 12px 14px 14px; }
.tt__title { font: 600 14px var(--dhx-font); color: var(--dhx-ink); margin-bottom: 8px; }
.tt__row { display: flex; justify-content: space-between; gap: 12px; font: 500 12px var(--dhx-font); color: var(--dhx-muted); margin-top: 4px; }
.tt__row b { color: var(--dhx-ink-2); font-weight: 600; }
.tt__chip { display: inline-block; padding: 2px 8px; border-radius: 999px; font: 600 11px var(--dhx-font); }
.tt__prog { height: 6px; border-radius: 999px; background: var(--dhx-neutral-bg); margin-top: 10px; overflow: hidden; }
:root[data-gantt-theme="dark"] .gantt_tooltip {
box-shadow: 0 8px 24px rgba(0,0,0,.5), 0 2px 6px rgba(0,0,0,.4) !important;
}
.tt__prog > i { display: block; height: 100%; border-radius: 999px; }
.tt__owner { display: inline-flex; align-items: center; gap: 6px; margin-top: 10px; font: 600 12px var(--dhx-font); color: var(--dhx-ink-2); }
.tt__av { width: 20px; height: 20px; border-radius: 50%; color: #fff; font: 700 10px var(--dhx-font); display: inline-flex; align-items: center; justify-content: center; }
.tt--link { padding: 10px 12px; font: 500 12px var(--dhx-font); color: var(--dhx-ink-2); }
.tt--link b { color: var(--dhx-ink); }
.tt--date { padding: 8px 12px; font: 600 12px var(--dhx-font); color: var(--dhx-ink); }
</style>
</head>
<body>
<div id="gantt_here"></div>
</body>
</html>