Professional demos/Big dataset
Loading live demo…from dhtmlxcode.com
Professional demos
Big dataset
Demonstrates DHTMLX Gantt performance with a large number of tasks and links, featuring optimized rendering.
gantt.plugins({ marker: true, tooltip: true });
gantt.config.row_height = 22;
gantt.config.bar_height = 17;
gantt.config.min_column_width = 40;
gantt.config.scale_height = 54;
gantt.config.work_time = true;
gantt.config.date_format = "%Y-%m-%d";
gantt.config.scales = [
{ unit: "month", step: 1, format: "%M %Y" },
{ unit: "week", step: 1, format: "%d" }
];
gantt.templates.timeline_cell_class = function (task, date) {
return gantt.isWorkTime({ date, task }) ? "" : "week_end";
};
/* ---------------------------------------------------------------------------
* HEADER CONTROLS
* ------------------------------------------------------------------------ */
function pickTotal(n) {
DHX.numberPicker.pick("inp_total", n, "#menu_total");
clearLoadError();
}
function pickProj(n) {
DHX.numberPicker.pick("inp_proj", n, "#menu_proj");
clearLoadError();
}
DHX.dd.initClickOutside();
/* ---------------------------------------------------------------------------
* DATA GENERATOR
* ------------------------------------------------------------------------ */
let TASK_NAMES = [
"Design", "Development", "Testing", "Review", "Deploy",
"Planning", "Analysis", "Implementation", "QA", "Research",
"Prototype", "Integration", "Maintenance", "Documentation", "Sign-off"
];
let PROJECT_NAMES = [
"Website Redesign", "Mobile App", "API Integration", "Cloud Migration",
"Security Audit", "Data Pipeline", "Portal Launch", "Performance Tuning",
"CRM Update", "DevOps Setup", "Analytics Dashboard", "Payment Gateway",
"Onboarding Flow", "Notification System", "Search Refactor"
];
function makeRng(seed) {
let state = seed >>> 0;
return function () {
state = (1664525 * state + 1013904223) >>> 0;
return state / 4294967296;
};
}
function rnd(rand, min, max) {
return min + Math.floor(rand() * (max - min));
}
function fmtDate(d) {
let dd = d.getDate(), mm = d.getMonth() + 1, yy = d.getFullYear();
return `${yy}-${mm < 10 ? "0" : ""}${mm}-${dd < 10 ? "0" : ""}${dd}`;
}
let RANGE_START = new Date(2026, 0, 1);
let RANGE_DAYS = 365; /* 2026 */
function generateData(totalCount, projSize) {
let tasks = [], links = [], id = 1;
let rand = makeRng(totalCount * 31 + projSize * 17);
let projectCount = Math.ceil(totalCount / projSize);
for (let p = 0; p < projectCount && tasks.length < totalCount; p++) {
let pStart = new Date(RANGE_START);
let pOffset = rnd(rand, 0, RANGE_DAYS);
pStart.setDate(RANGE_START.getDate() + (p === 0 ? 0 : pOffset));
let pId = id++;
let pName = PROJECT_NAMES[p % PROJECT_NAMES.length];
if (p >= PROJECT_NAMES.length) pName += ` ${Math.floor(p / PROJECT_NAMES.length) + 1}`;
let childTasks = [];
let cursor = new Date(pStart);
let prevId = null;
for (let t = 0; t < projSize && (tasks.length + childTasks.length) < totalCount; t++) {
let tId = id++;
let tDur = rnd(rand, 3, 18);
childTasks.push({
id: tId,
text: `${TASK_NAMES[rnd(rand, 0, TASK_NAMES.length)]} ${t + 1}`,
start_date: fmtDate(cursor),
duration: tDur,
progress: Math.round(rand() * 10) / 10,
parent: pId
});
/* sequential 65 %, parallel overlap 35 % */
let step = rand() > 0.35
? rnd(rand, Math.ceil(tDur * 0.6), tDur + 2)
: rnd(rand, 1, 4);
cursor.setDate(cursor.getDate() + step);
if (prevId && rand() > 0.2) {
links.push({ id: links.length + 1, source: prevId, target: tId, type: "0" });
}
prevId = tId;
}
let pDur = Math.max(1, gantt.calculateDuration({ start_date: pStart, end_date: cursor }));
tasks.push({
id: pId, text: pName,
type: "project", open: p < 5,
start_date: fmtDate(pStart), duration: pDur, parent: 0
});
for (let i = 0; i < childTasks.length; i++) tasks.push(childTasks[i]);
}
return { tasks, links };
}
/* ---------------------------------------------------------------------------
* PERFORMANCE TOAST
* ------------------------------------------------------------------------ */
function _toastRow(label, value) {
return `<div class="dhx_perf_toast__row">
<span class="dhx_perf_toast__label">${label}</span>
<span class="dhx_perf_toast__val">${value}</span>
</div>`;
}
function showPerfToast(tc, lc, genMs, parseMs, renderMs) {
let el = document.getElementById("perf_toast");
let totalMs = genMs + parseMs + renderMs;
el.innerHTML = `<div class="dhx_perf_toast__title">Load statistics</div>
${_toastRow("Tasks", tc.toLocaleString())}
${_toastRow("Links", lc.toLocaleString())}
<div class="dhx_perf_toast__divider"></div>
${_toastRow("Generate", `${(genMs / 1000).toFixed(2)} s`)}
${_toastRow("Parse", `${(parseMs / 1000).toFixed(2)} s`)}
${_toastRow("Render", `${(renderMs / 1000).toFixed(2)} s`)}
<div class="dhx_perf_toast__divider"></div>
${_toastRow("Total", `${(totalMs / 1000).toFixed(2)} s`)}`;
el.classList.add("visible");
}
/* ---------------------------------------------------------------------------
* LOAD
* ------------------------------------------------------------------------ */
let startMarkerId = null;
function clearLoadError() {
DHX.ui.setText("load_error", "");
DHX.numberPicker.markError("field_total", false);
DHX.numberPicker.markError("field_proj", false);
}
function setLoadWarning(message, fieldId) {
DHX.ui.setText("load_error", message);
DHX.numberPicker.markError("field_total", fieldId === "field_total");
DHX.numberPicker.markError("field_proj", fieldId === "field_proj");
}
function readBoundedNumber(inputId, fieldId, label, min, max = Infinity) {
let value = parseInt(document.getElementById(inputId).value, 10);
if (!Number.isFinite(value) || value < min) {
setLoadWarning(
`${label} must be at least ${min.toLocaleString()}.`,
fieldId
);
return null;
}
if (value > max) {
setLoadWarning(
`${label} must not exceed ${max.toLocaleString()}.`,
fieldId
);
return null;
}
return value;
}
function applyLoad() {
clearLoadError();
let total = readBoundedNumber("inp_total", "field_total", "Total tasks", 1);
if (total == null) return;
let proj = readBoundedNumber("inp_proj", "field_proj", "Tasks per project", 1, 500);
if (proj == null) return;
DHX.numberPicker.sync("#menu_total", total);
DHX.numberPicker.sync("#menu_proj", proj);
if (total > 200000) {
setLoadWarning(
"No Gantt task limit, but 200,000+ tasks may strain your browser or device.",
"field_total"
);
requestAnimationFrame(function () {
requestAnimationFrame(function () {
load(total, proj);
});
});
} else {
load(total, proj);
}
}
function load(totalCount, projSize) {
document.getElementById("perf_toast").classList.remove("visible");
gantt.clearAll();
if (startMarkerId) {
gantt.deleteMarker(startMarkerId);
startMarkerId = null;
}
let t0 = performance.now();
let data = generateData(totalCount, projSize);
let genMs = performance.now() - t0;
let renderStart = performance.now();
gantt.parse(data);
let parseMs = performance.now() - t0 - genMs;
startMarkerId = gantt.addMarker({ start_date: RANGE_START, css: "project_start", text: "Start" });
let tc = gantt.getTaskCount(), lc = gantt.getLinkCount();
gantt.showDate(RANGE_START);
requestAnimationFrame(function () {
requestAnimationFrame(function () {
let renderMs = performance.now() - renderStart;
showPerfToast(tc, lc, genMs, parseMs, renderMs);
});
});
}
/* ---------------------------------------------------------------------------
* INIT
* ------------------------------------------------------------------------ */
function sizeGantt() {
document.getElementById("gantt_here").style.height = (window.innerHeight - 60) + "px";
}
window.addEventListener("resize", sizeGantt);
sizeGantt();
gantt.init("gantt_here");
load(10000, 50);<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Large Dataset Performance: 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>
#gantt_here { width: 100%; }
.gantt_task_cell.week_end { background-color: var(--dhx-gantt-base-colors-background-alt); }
.dhx_perf_toast {
position: fixed;
top: 72px;
right: 18px;
z-index: 200;
background: var(--dhx-overlay);
border: 1px solid var(--dhx-line);
border-radius: 12px;
box-shadow: 0 4px 16px rgba(16,24,40,.12), 0 1px 4px rgba(16,24,40,.06);
padding: 14px 16px 12px;
min-width: 220px;
font: 13px var(--dhx-font);
color: var(--dhx-ink-2);
opacity: 0;
transform: translateY(-6px);
transition: opacity .22s ease, transform .22s ease;
pointer-events: none;
}
.dhx_perf_toast.visible {
opacity: 1;
transform: translateY(0);
}
.dhx_perf_toast__title {
font: 600 10px var(--dhx-font);
color: var(--dhx-muted);
text-transform: uppercase;
letter-spacing: .6px;
margin-bottom: 9px;
}
.dhx_perf_toast__row {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 16px;
padding: 2px 0;
}
.dhx_perf_toast__label { color: var(--dhx-muted); }
.dhx_perf_toast__val { font-weight: 600; color: var(--dhx-ink); }
.dhx_perf_toast__divider {
height: 1px;
background: var(--dhx-line);
margin: 7px 0;
}
#load_error {
display: inline-block;
max-width: 400px;
white-space: normal;
overflow-wrap: anywhere;
line-height: 1.35;
text-align: left;
}
</style>
</head>
<body>
<div class="dhx_header dhx_header--centered">
<div class="dhx_dd" id="dd_total">
<div class="dhx_number" id="field_total">
<span class="dhx_number__label">Total tasks</span>
<input id="inp_total" class="dhx_number__input" type="number" min="1" value="10000" onkeydown="if(event.key==='Enter')applyLoad()">
<button class="dhx_number__arrow" onclick="DHX.dd.toggle('dd_total')" tabindex="-1" type="button" aria-label="Choose total tasks">
<svg class="dhx_ic" viewBox="0 0 12 12"><polyline points="2 4 6 8 10 4"/></svg>
</button>
</div>
<div class="dhx_menu dhx_number_menu" id="menu_total">
<div class="dhx_menu__item" data-value="1000" onclick="pickTotal(1000)">1 000</div>
<div class="dhx_menu__item" data-value="5000" onclick="pickTotal(5000)">5 000</div>
<div class="dhx_menu__item active checked" data-value="10000" onclick="pickTotal(10000)">10 000</div>
<div class="dhx_menu__item" data-value="30000" onclick="pickTotal(30000)">30 000</div>
<div class="dhx_menu__item" data-value="50000" onclick="pickTotal(50000)">50 000</div>
<div class="dhx_menu__item" data-value="100000" onclick="pickTotal(100000)">100 000</div>
</div>
</div>
<div class="dhx_dd" id="dd_proj">
<div class="dhx_number" id="field_proj">
<span class="dhx_number__label">Tasks per project</span>
<input id="inp_proj" class="dhx_number__input" type="number" min="1" max="500" value="50" onkeydown="if(event.key==='Enter')applyLoad()">
<button class="dhx_number__arrow" onclick="DHX.dd.toggle('dd_proj')" tabindex="-1" type="button" aria-label="Choose tasks per project">
<svg class="dhx_ic" viewBox="0 0 12 12"><polyline points="2 4 6 8 10 4"/></svg>
</button>
</div>
<div class="dhx_menu dhx_number_menu" id="menu_proj">
<div class="dhx_menu__item" data-value="5" onclick="pickProj(5)">5</div>
<div class="dhx_menu__item" data-value="10" onclick="pickProj(10)">10</div>
<div class="dhx_menu__item" data-value="20" onclick="pickProj(20)">20</div>
<div class="dhx_menu__item active checked" data-value="50" onclick="pickProj(50)">50</div>
<div class="dhx_menu__item" data-value="100" onclick="pickProj(100)">100</div>
<div class="dhx_menu__item" data-value="200" onclick="pickProj(200)">200</div>
</div>
</div>
<button class="dhx_btn dhx_btn--primary" onclick="applyLoad()">Apply</button>
<span class="dhx_form_error" id="load_error"></span>
</div>
<div id="gantt_here"></div>
<div class="dhx_perf_toast" id="perf_toast" role="status" aria-live="polite"></div>
</body>
</html>