Hi) Can I make one task sticky (for example, the very last one), that is, when scrolling vertically, it is always in the visibility zone of the entire line (left and right parts)?
Hello,
If I understood you correctly, you want to make a specific task (like the last one in your project) always remain visible at the top of the Gantt chart when scrolling vertically, so both its grid (left) and timeline (right) parts stay in view. If it’s so, then you can implement this with a custom solution that creates a separate sticky row that becomes visible when scrolling.
Here’s a possible custom solution for how it could be implemented:
- Create a custom data store and layout that displays a copy of your target task at the top of the Gantt chart when scrolling occurs:
const staticStore = gantt.createDatastore({
name: 'staticValue',
type: "treeDatastore",
initItem: function (item) {
item.id = item.key || gantt.uid();
return item;
},
});
staticStore.parse([{ text: 'any text', id: 100, start_date: new Date(), duration: 1, static: true }]);
- Then configure a custom layout that includes a separate row for the sticky task:
gantt.config.layout = {
css: "gantt_container",
rows: [
{
height: 35,
cols: [
{ view: "grid", id: "staticGrid", bind: "staticValue", group: "grids", css: "custom-scale" },
{ view: "timeline", id: "staticTimeline", bind: "staticValue", scrollX: "scrollHor", css: "custom-scale", layers: staticLayers },
{ view: "scrollbar", id: "staticscrollVer", group: "vertical" }
]
},
// ... rest of your main Gantt layout
]
};
- Draw a clone of the task inside the fixed timeline area using a custom layer. Here, we define a
renderStaticTasklayer function that calculates bar position and returns a DOM element that will be placed inside the static timeline area:
const renderStaticTask = function (task, timeline) {
let end_date = gantt.calculateEndDate(task.start_date, task.duration);
const sizes = timeline.getItemPosition(task, task.start_date, end_date);
const row = document.createElement("div");
row.innerHTML = task.text;
row.classList.add("gantt_task_line");
row.style.cssText = [
`left: ${sizes.left}px`,
`width: ${sizes.width}px`,
`position:absolute`,
`height: ${gantt.config.row_height - 5}px`,
`top: ${sizes.top + 2}px`,
`text-align:center`,
`font-size: 12px`
].join(";");
return row;
};
const staticLayers = [ renderStaticTask, "taskBg" ];
Also, keep the static datastore updated when the sticky task changes. So when the task you want to show as sticky changes (dates, progress, text), copy its important fields into the staticStore so the fixed timeline re-renders the clone:
function updateStaticRow(taskId) {
let task = gantt.getTask(taskId);
staticStore.clearAll();
staticStore.addItem({
text: task.text,
id: task.id,
start_date: task.start_date,
duration: task.duration,
static: true
});
}
- Use the onGanttScroll event to show/hide the sticky row and update its content:
gantt.attachEvent("onGanttScroll", function (left, top) {
const staticlayout = document.querySelector(".gantt_layout_x");
let scrollState = gantt.getScrollState();
if (scrollState.y > gantt.config.row_height) {
staticlayout.classList.add("scrolling");
// find topmost visible task element (smart rendering may mean only some DOM rows exist)
let ganttRowTask = document.querySelectorAll(".gantt_layout_x:nth-child(2) .gantt_row_task");
let highestTaskId = ganttRowTask[0].getAttribute("data-task-id");
// find the topmost one by bounding rect (example iterates to find smallest top)
// then pick its parent (or use other logic) to choose which project/task to make static
let parentId = gantt.getParent(highestTaskId);
if (parentId) updateStaticRow(parentId);
} else {
staticlayout.classList.remove("scrolling");
}
});
Apply some CSS rules to ensure the sticky row is properly positioned and visible:
.gantt_layout_x:first-child {
position: absolute;
z-index: 100;
display: none;
}
.gantt_layout_x.scrolling {
margin-top: 35px;
display: block;
}
.custom-scale .gantt_grid_scale,
.custom-scale .gantt_task_scale {
display: none;
height: 0px;
}
Please check a full working example: DHTMLX - Gantt. Fix first task row in the grid and timeline (custom datastore, layout view and layer).
Best regards,
Valeria Ivashkevich
DHTMLX Support Engineer