Skip to main content
Back to examples
Popular features/Grid controls
Loading live demo…from dhtmlxcode.com
Popular features

Grid controls

Render edit, add-child, duplicate, delete, and star-flag buttons in every grid row, each wired straight to the Gantt API.

gantt.config.date_format  = "%Y-%m-%d";
gantt.config.row_height    = 38;
gantt.config.bar_height    = 24;
gantt.config.scale_height  = 52;

gantt.config.scales = [
	{ unit: "month", step: 1, format: "%F %Y" },
	{ unit: "week",  step: 1, format: "Week %W" }
];

let ICON = {
	above: '<svg viewBox="0 0 16 16"><polyline points="4 10 8 6 12 10"/><polyline points="4 6 8 2 12 6"/></svg>',
	below: '<svg viewBox="0 0 16 16"><polyline points="4 6 8 10 12 6"/><polyline points="4 10 8 14 12 10"/></svg>',
	edit: '<svg viewBox="0 0 16 16"><path d="M2 11.5 10 3.5l2.5 2.5-8 8H2z"/><path d="M9 4.5 11.5 7"/></svg>',
	add:  '<svg viewBox="0 0 16 16"><line x1="8" y1="3" x2="8" y2="13"/><line x1="3" y1="8" x2="13" y2="8"/></svg>',
	dup:  '<svg viewBox="0 0 16 16"><rect x="5" y="5" width="8" height="8" rx="1.5"/><path d="M3 11V4a1 1 0 0 1 1-1h7"/></svg>',
	del:  '<svg viewBox="0 0 16 16"><polyline points="3 5 13 5"/><path d="M6 5V3h4v2"/><path d="M5 5l1 8h4l1-8"/></svg>',
	star: '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"><path d="M8 2l1.8 3.9 4.2.5-3.1 2.9.8 4.2L8 11.8 4.3 13.4l.8-4.2L2 6.3l4.2-.5z"/></svg>',
	starF:'<svg viewBox="0 0 16 16" fill="currentColor" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"><path d="M8 2l1.8 3.9 4.2.5-3.1 2.9.8 4.2L8 11.8 4.3 13.4l.8-4.2L2 6.3l4.2-.5z"/></svg>'
};

function flagCell(task) {
	return `<span class="flag ${task.important ? "on" : ""}" data-grid-action="toggle-flag">${task.important ? ICON.starF : ICON.star}</span>`;
}

function actionsCell() {
	return `<span class="grid_actions">
		<span class="act edit" title="Edit" data-grid-action="edit">${ICON.edit}</span>
		<span class="act add" title="Add child" data-grid-action="add">${ICON.add}</span>
		<span class="act above" title="Add task above" data-grid-action="add-sibling" data-position="above">${ICON.above}</span>
		<span class="act below" title="Add task below" data-grid-action="add-sibling" data-position="below">${ICON.below}</span>
		<span class="act dup" title="Duplicate" data-grid-action="duplicate">${ICON.dup}</span>
		<span class="act del" title="Delete" data-grid-action="delete">${ICON.del}</span>
	</span>`;
}

gantt.config.columns = [
	{ name: "text",       label: "Step", tree: true, width: 250, resize: true },
	{ name: "flag",       label: "", align: "center", width: 40, resize: true, template: flagCell },
	{ name: "duration",   label: "Days", align: "center", width: 56, resize: true },
	{ name: "actions",    label: `<div class="gantt_add"></div>`, align: "center", width: 164, resize: true, template: actionsCell }
];

gantt.templates.task_class = function (start, end, task) {
	return task.important ? "is_important" : "";
};

/* control handlers */
function insertSibling(taskId, position) {
	const referenceTask = gantt.getTask(taskId);
	const taskIndex = gantt.getTaskIndex(taskId) + (position === "below" ? 1 : 0);

	gantt.createTask({
		text: "New task",
		start_date: referenceTask.start_date,
		duration: Math.max(referenceTask.duration, 1),
		progress: 0
	}, referenceTask.parent, taskIndex);
}

function editTask(taskId) {
	gantt.showLightbox(taskId);
}

function addChildTask(taskId) {
	gantt.createTask(null, taskId);
}

function duplicateTask(taskId) {
	const task = gantt.getTask(taskId);

	gantt.addTask({
		text: task.text + " (copy)",
		start_date: task.start_date,
		duration: task.duration,
		progress: 0
	}, task.parent, gantt.getTaskIndex(taskId) + 1);
}

function deleteTask(taskId) {
	gantt.confirm({
		title: gantt.locale.labels.confirm_deleting_title,
		text: gantt.locale.labels.confirm_deleting,
		callback: function (isConfirmed) {
			if (isConfirmed) {
				gantt.deleteTask(taskId);
			}
		}
	});
}

function toggleFlag(taskId) {
	const task = gantt.getTask(taskId);
	task.important = !task.important;
	gantt.updateTask(taskId);
}

gantt.attachEvent("onTaskClick", function (taskId, event) {
	const eventTarget = event.target instanceof Element ? event.target : null;
	const controlElement = eventTarget ? eventTarget.closest("[data-grid-action]") : null;

	if (!controlElement) {
		return true;
	}
	event.stopPropagation();

	const gridAction = controlElement.dataset.gridAction;

	if (gridAction === "toggle-flag") {
		toggleFlag(taskId);
	} else if (gridAction === "edit") {
		editTask(taskId);
	} else if (gridAction === "add") {
		addChildTask(taskId);
	} else if (gridAction === "add-sibling") {
		insertSibling(taskId, controlElement.dataset.position);
	} else if (gridAction === "duplicate") {
		duplicateTask(taskId);
	} else if (gridAction === "delete") {
		deleteTask(taskId);
	}

	return false;
});

/* data */
/* Anchored to today (not a fixed year) so the sample never looks stale. */
function addDays(base, n) {
	let d = gantt.date.day_start(new Date(base));
	return gantt.date.add(d, n, "day");
}
let TODAY = gantt.date.day_start(new Date());
let PLAN_START = addDays(TODAY, -14);

let data = {
	tasks: [
		{ id: 1, text: "New hire onboarding", type: "project", open: true, progress: 0.5 },

		{ id: 10, text: "Before day 1", type: "project", parent: 1, open: true, progress: 1 },
		{ id: 11, text: "Provision laptop", parent: 10, start_date: addDays(PLAN_START, 0),  duration: 2, progress: 1, important: true },
		{ id: 12, text: "Create accounts",  parent: 10, start_date: addDays(PLAN_START, 2),  duration: 1, progress: 1 },
		{ id: 13, text: "Prepare workspace", parent: 10, start_date: addDays(PLAN_START, 2), duration: 1, progress: 1 },

		{ id: 20, text: "Week 1", type: "project", parent: 1, open: true, progress: 0.5 },
		{ id: 21, text: "Team intros",     parent: 20, start_date: addDays(PLAN_START, 3),  duration: 2, progress: 1 },
		{ id: 22, text: "Codebase tour",   parent: 20, start_date: addDays(PLAN_START, 7),  duration: 3, progress: 0.5, important: true },
		{ id: 23, text: "First PR",        parent: 20, start_date: addDays(PLAN_START, 10), duration: 2, progress: 0 },
		{ id: 24, text: "Product walkthrough", parent: 20, start_date: addDays(PLAN_START, 11), duration: 2, progress: 0 },

		{ id: 30, text: "Week 2-4", type: "project", parent: 1, open: true, progress: 0.1 },
		{ id: 31, text: "Own small feature", parent: 30, start_date: addDays(PLAN_START, 15), duration: 5, progress: 0.2, important: true },
		{ id: 32, text: "Support rotation shadow", parent: 30, start_date: addDays(PLAN_START, 22), duration: 4, progress: 0 },
		{ id: 33, text: "Architecture review", parent: 30, start_date: addDays(PLAN_START, 25), duration: 3, progress: 0 },
		{ id: 40, text: "Day 30 review", parent: 1, type: "milestone", start_date: addDays(PLAN_START, 32) },

		{ id: 50, text: "Weeks 6-8", type: "project", parent: 1, open: true, progress: 0.3 },
		{ id: 51, text: "Own a feature end-to-end", parent: 50, start_date: addDays(PLAN_START, 35), duration: 5, progress: 0.6, important: true },
		{ id: 52, text: "Mentor a new joiner",      parent: 50, start_date: addDays(PLAN_START, 42), duration: 3, progress: 0 },
		{ id: 53, text: "Cross-team collaboration", parent: 50, start_date: addDays(PLAN_START, 45), duration: 4, progress: 0 },
		{ id: 54, text: "Performance check-in",     parent: 50, start_date: addDays(PLAN_START, 49), duration: 1, progress: 0 },

		{ id: 60, text: "Weeks 9-10", type: "project", parent: 1, open: true, progress: 0 },
		{ id: 61, text: "Lead a small project",         parent: 60, start_date: addDays(PLAN_START, 56), duration: 6, progress: 0, important: true },
		{ id: 62, text: "Present at team demo",         parent: 60, start_date: addDays(PLAN_START, 63), duration: 1, progress: 0 },
		{ id: 63, text: "Write technical documentation", parent: 60, start_date: addDays(PLAN_START, 64), duration: 3, progress: 0 },
		{ id: 64, text: "Shadow on-call rotation",       parent: 60, start_date: addDays(PLAN_START, 67), duration: 2, progress: 0 },

		{ id: 70, text: "Weeks 11-12", type: "project", parent: 1, open: true, progress: 0 },
		{ id: 71, text: "Own a mid-size initiative",   parent: 70, start_date: addDays(PLAN_START, 70), duration: 8, progress: 0, important: true },
		{ id: 72, text: "Contribute to design review", parent: 70, start_date: addDays(PLAN_START, 78), duration: 2, progress: 0 },
		{ id: 73, text: "Onboard the next new hire",   parent: 70, start_date: addDays(PLAN_START, 80), duration: 3, progress: 0 },
		{ id: 80, text: "90-day review", parent: 1, type: "milestone", start_date: addDays(PLAN_START, 84) }
	],
	links: [
		{ id: 1, source: 11, target: 12, type: "0" },
		{ id: 2, source: 21, target: 22, type: "0" },
		{ id: 3, source: 22, target: 23, type: "0" },
		{ id: 4, source: 23, target: 31, type: "0" },
		{ id: 5, source: 31, target: 40, type: "0" },
		{ id: 6, source: 33, target: 40, type: "0" },
		{ id: 7, source: 40, target: 51, type: "0" },
		{ id: 8, source: 54, target: 61, type: "0" },
		{ id: 9, source: 64, target: 71, type: "0" },
		{ id: 10, source: 73, target: 80, 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>Controls inside the grid: 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_task_line.is_important { box-shadow: 0 0 0 2px #f59e0b, 0 1px 2px rgba(16,24,40,.18); }

		.grid_actions { display: inline-flex; gap: 2px; }
		.act {
			display: inline-flex; align-items: center; justify-content: center;
			width: 24px; height: 24px; border-radius: 6px;
			color: var(--dhx-muted); cursor: pointer;
			opacity: .75; transition: opacity .12s, background .12s, color .12s;
		}
		.act:hover { opacity: 1; background: var(--dhx-surface-2); }
		.act svg { width: 15px; height: 15px; stroke: currentColor; stroke-width: 1.7; fill: none; stroke-linecap: round; stroke-linejoin: round; }
		.act.above:hover,
		.act.below:hover,
		.act.edit:hover,
		.act.add:hover,
		.act.dup:hover    { color: var(--dhx-info-ink); }
		.act.del:hover    { color: var(--dhx-danger-solid); }

		.flag { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; cursor: pointer; color: var(--dhx-muted); }
		.flag:hover { color: #f59e0b; }
		.flag.on { color: #f59e0b; }
		.flag svg { width: 15px; height: 15px; }

		.head_add {
			display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
			color: var(--dhx-gantt-container-color); font: 600 12px var(--dhx-font);
		}
	</style>
</head>
<body>

<div id="gantt_here"></div>


</body>
</html>