Hello @Surbhi_Agarwal,
Yes — you can try to achieve the desired behavior by implementing placeholder links from the source task to a provisional position until the target task is fully loaded. Once the target is rendered, Gantt’s native link rendering will take over automatically.
This can be done using the addTaskLayer() method. Below is an outline of this approach (full working example at the end):
- Attach the
addTaskLayer
method that processes tasks with outgoing links. In the render(task)
function:
- Skip tasks with no outgoing links (
if (!task.$source.length) return false;
)
- Create a container element (
mainEl
) for the placeholder lines.
- Call
drawLinesForTask(task, mainEl)
to render SVG lines when a link’s target is not present in the chart.
gantt.addTaskLayer({
renderer: {
render: function (task) {
if (!task.$source.length) return false;
const mainEl = document.createElement("div");
mainEl.className = "custom_link_render";
mainEl.setAttribute("task-id", task.id);
drawLinesForTask(task, mainEl);
return mainEl;
},
// ...
}
});
- The
drawLinesForTask()
function iterates over each source link, and if the target doesn’t exist, calculates coordinates reaching down to the last visible task row:
if (!gantt.isTaskExists(link.target)) {
const visibleTasks = gantt.getVisibleTaskCount();
const lastVisibleTask = gantt.getTaskByIndex(visibleTasks - 1);
const x = [gantt.posFromDate(task.end_date), gantt.posFromDate(task.end_date)];
const y = [
gantt.getTaskPosition(task).top + 16,
gantt.getTaskPosition(lastVisibleTask).top + gantt.config.row_height / 2
];
// ... build SVG line from (x[0], y[0]) to (x[1], y[1])
}
Use link.type
to determine correct positioning:
switch (link.type) {
case "0": // Finish-to-Start
x[1] -= 5;
break;
case "1": // Start-to-Start
x[0] = gantt.posFromDate(task.start_date);
x[1] -= 5;
break;
case "2": // Finish-to-Finish
x[1] = gantt.posFromDate(last.end_date) + 5;
break;
case "3": // Start-to-Finish
x[0] = gantt.posFromDate(task.start_date);
x[1] = gantt.posFromDate(last.end_date) + 5;
break;
}
And then render SVG path:
const line = document.createElementNS("http://www.w3.org/2000/svg", "svg");
line.style.left = x[0] + "px";
line.style.top = y[0] + "px";
line.setAttribute("width", x[1] - x[0] + 2);
line.setAttribute("height", y[1] - y[0] + 10);
const path = document.createElementNS(line.namespaceURI, "path");
path.setAttribute("d", `M ${p1x} ${p1y} L ${p2x} ${p2y}`);
path.setAttribute("stroke-width", "5");
path.setAttribute("fill", "none");
line.appendChild(path);
mainEl.appendChild(line);
- Define
getRectangle(task)
so that Gantt knows where the custom element might appear, even off-screen:
getRectangle: function (task) {
const visibleTasks = gantt.getVisibleTaskCount();
const lastVisibleTask = gantt.getTaskByIndex(visibleTasks - 1);
const pos = gantt.getTaskPosition(task);
return {
left: pos.left,
top: pos.top,
width: gantt.$task.offsetWidth,
height: lastVisibleTask.top - pos.top
};
}
Once the target is parsed into Gantt, your custom layer does not render the line for that link—Gantt’s built-in rendering displays it. This keeps visual consistency across all links.
Please check the full working example: DHTMLX Snippet Tool.
Best regards,
Valeria Ivashkevich
DHTMLX Support Engineer