Split gantt horizontally and fix tasks like Excel

Hello there… i want to know if its possible to “split” horizontally the gantt and “frozen” the tasks from above to only scroll the tasks of the bottom side? just like we do it in Excel.

Hello,

Unfortunately, there is no built-in solution at the moment. But you can do it manually.
This can be done with two Gantt instances Multiple Charts on a Page Gantt Docs. Top instance for fixed tasks, bottom instance for all other tasks.

To filter the tasks that should be loaded into the top/bottom Gantt instances, you need to add a marker/property for the task, for example rowNum:

...
{ id: "6d2661c6-b941", rowNum: 3, text: "Task #3", start_date: "03-03-2023", duration: 2 },
...

Here is an example for automatic row numbering: https://snippet.dhtmlx.com/pbp87aut

You also need to add the isSplitGantt flag to filter tasks when they are loaded (using onTaskLoading handler) and the ability to disable the “Split Gantt” mode. For example, when the “Split Gantt” mode is off, all tasks are loaded:

let isSplitGantt = false;

gantt.attachEvent("onTaskLoading", (task) => {
    if (isSplitGantt) {
        if (task.rowNum > selectedTaskRow) {

            return false
        }
    }

    return true;
});

To enable/disable “Split Gantt” mode, you can add a button:

<div class="gantt_control">
    Select task and click button
    <input type='button' value='Split Gantt' onclick='splitGantt()'>
</div>

When selecting (using onTaskSelected handler) a task in the Gantt, you can get its rowNum property as well as the top position of the task’s DOM element (using getTaskTop method):

let taskTop = null;
let selectedTaskRow = null;

gantt.attachEvent("onTaskSelected", function(id) {
    const task = gantt.getTask(id);
    taskTop = gantt.getTaskTop(id);
    selectedTaskRow = task.rowNum;
});

When you click on the “Split Gantt” button, the Gantt will change height and display only fixed tasks.
With fixed tasks, vertical scroll will be disabled, and in order for the width of the top and bottom Gantts to be the same, you can insert an HTML element instead of vertical scroll (using gantt.config.layout configuration) (splitGantt() function, part 1):

function splitGantt() {
    const splitButton = document.querySelector("input");
    const ganttContainer = gantt.$root;

    if (!isSplitGantt) {
        isSplitGantt = !isSplitGantt;
        const scaleHeight = gantt.config.scale_height;
        const rowHeight = gantt.config.row_height;
        const fixedHeight = scaleHeight + taskTop + rowHeight + 2;
        ganttContainer.style.height = `${fixedHeight}px`;
        gantt.clearAll();
        gantt.config.layout = splitGanttLayoutConfig;
        gantt.resetLayout();
        gantt.parse(data);
        ...

splitGanttLayoutConfig layout:

const splitGanttLayoutConfig = {
    css: "gantt_container",
    rows:[
        {
            cols: [
                {
                    view: "grid",
                    scrollX: "scrollHor",
                },
                { resizer: true, width: 1 },
                {
                    view: "timeline",
                    scrollX: "scrollHor"
                },
                { html:"<div></div>", width: 16 },
            ]
        },
        {
            view: "scrollbar",
            id:"scrollHor"
        }
    ]
};

Under the Gantt with fixed tasks, add the Gantt with all other tasks (continuation of the splitGantt() function, part 2):

...
const lowerGanttContainer = document.createElement("div");
lowerGanttContainer.id = 'gantt_here_2';
lowerGanttContainer.style.width = '100%';
lowerGanttContainer.style.height = `calc(100vh - ${fixedHeight}px - 21px)`;
ganttContainer.parentNode.appendChild(lowerGanttContainer);

gantt_2 = Gantt.getGanttInstance();

gantt_2.attachEvent("onTaskLoading", function(task) {
    if (isSplitGantt) {
        if (task.rowNum < (selectedTaskRow + 1)) {
            return false
        }
    }

    return true;
});

gantt_2.config.scale_height = 0;

gantt_2.init("gantt_here_2");
gantt_2.parse(data);

splitButton.value = "Unsplit Gantt";
...

To disable the “Split Gantt” mode, you need to dispose unnecessary bottom instance of the Gantt (using gantt.destructor() method), change the height of the container and the layout of the top Gantt and reload the tasks (the end of the splitGantt() function, part 3):

    ...
    } else {
        isSplitGantt = !isSplitGantt;
        ganttContainer.style.height = "100%";
        gantt.clearAll();
        gantt.config.layout = defaultGanttLayoutConfig;
        gantt.resetLayout();
        gantt.parse(data);
        gantt_2.$root.style.height = "0%";
        gantt_2.destructor();

        splitButton.value = "Split Gantt";
    }
}

defaultGanttLayoutConfig layout:

const defaultGanttLayoutConfig = {
    css: "gantt_container",
    rows:[
        {
            cols: [
                {
                    view: "grid",
                    scrollX: "scrollHor",
                    scrollY: "scrollVer"
                },
                { resizer: true, width: 1 },
                {
                    view: "timeline",
                    scrollX: "scrollHor",
                    scrollY: "scrollVer"
                },
                {
                    view: "scrollbar",
                    id: "scrollVer"
                }
            ]
        },
        {
            view: "scrollbar",
            id:"scrollHor"
        }
    ]
};

Please see an example: https://snippet.dhtmlx.com/oj0rdfph. This is not a complete example, but based on it, you can modify it to suit your needs.
You can also contact the development department contact@dhtmlx.com if the proposed solution does not meet your expectations.

1 Like