Skip to main content
Back to examples
Professional demos/PERT chart
Loading live demo…from dhtmlxcode.com
Professional demos

PERT chart

Visualize project dependencies as a PERT chart using the DHTMLX Diagram component alongside the Gantt chart.

gantt.config.date_format = "%d-%m-%Y"; // Set the date format for the Gantt chart
gantt.init("gantt");
gantt.parse(dataset);

const diagram = new dhx.Diagram("diagram", {
    type: "pert", // Set diagram type to PERT chart
    scale: 0.6, // Set initial zoom scale to 60%
    typeConfig: {
        dateFormat: gantt.config.date_format, // Use the same date format as the Gantt chart
    }
});

diagram.events.on("itemClick", id => selectGanttTask(id));
gantt.attachEvent("onTaskSelected", id => selectDiagramTask(id));

gantt.attachEvent("onAfterLinkAdd", (id, link) => diagram.data.add({ id, ...link }));
gantt.attachEvent("onAfterLinkDelete", id => diagram.data.remove("$link:" + id));
gantt.attachEvent("onAfterLinkUpdate", (id, link) => diagram.data.update("$link:" + id, link));

gantt.attachEvent("onAfterTaskAdd", (id, task) => diagram.data.add({ id, ...task, parent: getTopProject(id) }));
gantt.attachEvent("onAfterTaskDelete", id => diagram.data.remove(id));
gantt.attachEvent("onAfterTaskUpdate", (id, task) => diagram.data.update(id, task));

gantt.attachEvent("onTaskOpened", (id) => diagram.expandItem(id));
gantt.attachEvent("onTaskClosed", (id) => diagram.collapseItem(id));

diagram.events.on("afterCollapse", (id) => gantt.getTask(id).$open && gantt.close(id));
diagram.events.on("afterExpand", (id) => !gantt.getTask(id).$open && gantt.open(id));

// Load serialized Gantt data into the diagram to keep them in sync initially
diagram.data.parse(gantt.serialize());

// Initialize the scale panel when the DOM is ready
document.addEventListener('DOMContentLoaded', createScalePanel(diagram));
<section style="height: 100%">
    <div id="gantt" style="height: 50%; width: 100%;"></div>
    <div id="diagram" style="height: 50%; width: 100%;"></div>
</section>

<style>
.dhx_diagram_item--selected {
        --dhx-selected-border-dashed: 2px solid var(--dhx-color-primary);
    }

	.scale-panel {
		position: fixed;
		bottom: 50px;
		right: 50px;
		height: 40px;

		display: flex;
		align-items: center;

		background-color: var(--dhx-background-primary);
        box-shadow: var(--dhx-border-shadow-normal);

		font-family: var(--dhx-font-family);
		font-size: var(--dhx-font-size-large);
		color: var(--dhx-font-color-primary);
		border-radius: 4px;
	}

	.scale-panel button {
		border: none;
		cursor: pointer;
		width: 64px;
		height: 100%;
		background-color: inherit;
	}

	.scale-panel button:first-child {
		border-bottom-left-radius: 4px;
		border-top-left-radius: 4px;
	}

	.scale-panel button:last-child {
		border-bottom-right-radius: 4px;
		border-top-right-radius: 4px;
	}

	.scale-panel button:hover {
		background-color: var(--dhx-color-secondary-light-hover);
	}

	.scale-panel__text {
		width: 60px;
		text-align: center;
		font-weight: var(--dhx-font-weight-medium);
	}
</style>

<script>
function buildDateFromYMD(y, m, d){const params = new URLSearchParams(typeof location !== 'undefined' ? location.search : '');const fixed = params.get('fixedYear') || (typeof window !== 'undefined' && window.FIXED_YEAR);const anchor = fixed ? Number(fixed) : new Date().getFullYear();return new Date(anchor + (y - 2026), m, d);}

	const dataset = {
        "data": [
            { "id": "start", "text": "Product Development", "type": "project", "parent": null, "start_date": buildDateFromYMD(2026, 0, 1), "duration": 60, "progress": 0, "open": true },
            { "id": "2", "text": "Design", "type": "project", "parent": "start", "start_date": buildDateFromYMD(2026, 0, 1), "duration": 20, "progress": 0, "open": true },
            { "id": "3", "text": "Development", "type": "project", "parent": "start", "start_date": buildDateFromYMD(2026, 0, 21), "duration": 25, "progress": 0, "open": true },
            { "id": "4", "text": "Testing", "type": "project", "parent": "start", "start_date": buildDateFromYMD(2026, 1, 15), "duration": 10, "progress": 0, "open": true },
            { "id": "5", "text": "Launch", "type": "project", "parent": "start", "start_date": buildDateFromYMD(2026, 1, 25), "duration": 5, "progress": 0, "open": true },
            { "id": "2.1", "text": "Desk Research", "type": "project", "parent": "2", "start_date": buildDateFromYMD(2026, 0, 1), "duration": 5, "progress": 0, "open": true },
            { "id": "2.1.1", "text": "Market Research", "type": "task", "parent": "2.1", "start_date": buildDateFromYMD(2026, 0, 1), "duration": 2, "progress": 0, "open": true },
            { "id": "2.1.2", "text": "Competitive Analysis", "type": "task", "parent": "2.1", "start_date": buildDateFromYMD(2026, 0, 3), "duration": 3, "progress": 0, "open": true },
            { "id": "2.2", "text": "User Research", "type": "project", "parent": "2", "start_date": buildDateFromYMD(2026, 0, 6), "duration": 5, "progress": 0, "open": true },
            { "id": "2.2.1", "text": "Define User Personas", "type": "task", "parent": "2.2", "start_date": buildDateFromYMD(2026, 0, 6), "duration": 2, "progress": 0, "open": true },
            { "id": "2.2.2", "text": "User Interviews", "type": "task", "parent": "2.2", "start_date": buildDateFromYMD(2026, 0, 8), "duration": 3, "progress": 0, "open": true },
            { "id": "2.3", "text": "UX/UI Design", "type": "project", "parent": "2", "start_date": buildDateFromYMD(2026, 0, 11), "duration": 7, "progress": 0, "open": true },
            { "id": "2.3.1", "text": "Create UX Wireframes", "type": "task", "parent": "2.3", "start_date": buildDateFromYMD(2026, 0, 11), "duration": 3, "progress": 0, "open": true },
            { "id": "2.3.2", "text": "UI Visual Design", "type": "task", "parent": "2.3", "start_date": buildDateFromYMD(2026, 0, 14), "duration": 4, "progress": 0, "open": true },
            { "id": "2.4", "text": "Design Review", "type": "project", "parent": "2", "start_date": buildDateFromYMD(2026, 0, 18), "duration": 3, "progress": 0, "open": true },
            { "id": "2.4.1", "text": "Stakeholder Review", "type": "task", "parent": "2.4", "start_date": buildDateFromYMD(2026, 0, 18), "duration": 2, "progress": 0, "open": true },
            { "id": "2.4.2", "text": "Design Approval", "type": "milestone", "parent": "2.4", "start_date": buildDateFromYMD(2026, 0, 20), "duration": 1, "progress": 0, "open": true },
            { "id": "3.1", "text": "Environment Setup", "type": "project", "parent": "3", "start_date": buildDateFromYMD(2026, 0, 21), "duration": 3, "progress": 0, "open": true },
            { "id": "3.1.1", "text": "Setup Dev Environment", "type": "task", "parent": "3.1", "start_date": buildDateFromYMD(2026, 0, 21), "duration": 1, "progress": 0, "open": true },
            { "id": "3.1.2", "text": "Install Dependencies", "type": "task", "parent": "3.1", "start_date": buildDateFromYMD(2026, 0, 22), "duration": 2, "progress": 0, "open": true },
            { "id": "3.2", "text": "Frontend Development", "type": "project", "parent": "3", "start_date": buildDateFromYMD(2026, 0, 24), "duration": 8, "progress": 0, "open": true },
            { "id": "3.2.1", "text": "Basic Layout & Structure", "type": "task", "parent": "3.2", "start_date": buildDateFromYMD(2026, 0, 24), "duration": 2, "progress": 0, "open": true },
            { "id": "3.2.2", "text": "Implement Core Features", "type": "task", "parent": "3.2", "start_date": buildDateFromYMD(2026, 0, 26), "duration": 2, "progress": 0, "open": true },
            { "id": "3.2.3", "text": "Implement Core Features", "type": "task", "parent": "3.2", "start_date": buildDateFromYMD(2026, 0, 28), "duration": 2, "progress": 0, "open": true },
            { "id": "3.2.4", "text": "UI Integration", "type": "task", "parent": "3.2", "start_date": buildDateFromYMD(2026, 0, 30), "duration": 2, "progress": 0, "open": true },
            { "id": "3.3", "text": "Backend Development", "type": "project", "parent": "3", "start_date": buildDateFromYMD(2026, 1, 1), "duration": 7, "progress": 0, "open": true },
            { "id": "3.3.1", "text": "API Design", "type": "task", "parent": "3.3", "start_date": buildDateFromYMD(2026, 1, 1), "duration": 2, "progress": 0, "open": true },
            { "id": "3.3.2", "text": "Database Setup", "type": "task", "parent": "3.3", "start_date": buildDateFromYMD(2026, 1, 3), "duration": 2, "progress": 0, "open": true },
            { "id": "3.3.3", "text": "Implement Backend Logic", "type": "task", "parent": "3.3", "start_date": buildDateFromYMD(2026, 1, 5), "duration": 3, "progress": 0, "open": true },
            { "id": "3.4", "text": "Integration", "type": "project", "parent": "3", "start_date": buildDateFromYMD(2026, 1, 8), "duration": 4, "progress": 0, "open": true },
            { "id": "3.4.1", "text": "Frontend-Backend Integration", "type": "task", "parent": "3.4", "start_date": buildDateFromYMD(2026, 1, 8), "duration": 2, "progress": 0, "open": true },
            { "id": "3.4.2", "text": "Testing Integration", "type": "task", "parent": "3.4", "start_date": buildDateFromYMD(2026, 1, 10), "duration": 2, "progress": 0, "open": true },
            { "id": "3.5", "text": "Development Completion", "type": "project", "parent": "3", "start_date": buildDateFromYMD(2026, 1, 12), "duration": 3, "progress": 0, "open": true },
            { "id": "3.5.1", "text": "Final Development Review", "type": "task", "parent": "3.5", "start_date": buildDateFromYMD(2026, 1, 12), "duration": 2, "progress": 0, "open": true },
            { "id": "3.5.2", "text": "Development Approval", "type": "milestone", "parent": "3.5", "start_date": buildDateFromYMD(2026, 1, 14), "duration": 1, "progress": 0, "open": true },
            { "id": "4.1", "text": "Unit Testing", "type": "project", "parent": "4", "start_date": buildDateFromYMD(2026, 1, 15), "duration": 3, "progress": 0, "open": true },
            { "id": "4.1.1", "text": "Development Approval", "type": "task", "parent": "4.1", "start_date": buildDateFromYMD(2026, 1, 15), "duration": 1, "progress": 0, "open": true },
            { "id": "4.1.2", "text": "Execute Unit Tests", "type": "task", "parent": "4.1", "start_date": buildDateFromYMD(2026, 1, 16), "duration": 2, "progress": 0, "open": true },
            { "id": "4.2", "text": "QA Testing", "type": "project", "parent": "4", "start_date": buildDateFromYMD(2026, 1, 18), "duration": 3, "progress": 0, "open": true },
            { "id": "4.2.1", "text": "Functional Testing", "type": "task", "parent": "4.2", "start_date": buildDateFromYMD(2026, 1, 18), "duration": 2, "progress": 0, "open": true },
            { "id": "4.2.2", "text": "Usability Testing", "type": "task", "parent": "4.2", "start_date": buildDateFromYMD(2026, 1, 20), "duration": 1, "progress": 0, "open": true },
            { "id": "4.3", "text": "Bug Fixing", "type": "project", "parent": "4", "start_date": buildDateFromYMD(2026, 1, 21), "duration": 2, "progress": 0, "open": true },
            { "id": "4.3.1", "text": "Report Bugs", "type": "task", "parent": "4.3", "start_date": buildDateFromYMD(2026, 1, 21), "duration": 1, "progress": 0, "open": true },
            { "id": "4.3.2", "text": "Fix Critical Bugs", "type": "task", "parent": "4.3", "start_date": buildDateFromYMD(2026, 1, 22), "duration": 1, "progress": 0, "open": true },
            { "id": "4.4", "text": "Final Testing", "type": "project", "parent": "4", "start_date": buildDateFromYMD(2026, 1, 23), "duration": 2, "progress": 0, "open": true },
            { "id": "4.4.1", "text": "Final QA Review", "type": "task", "parent": "4.4", "start_date": buildDateFromYMD(2026, 1, 23), "duration": 1, "progress": 0, "open": true },
            { "id": "4.4.2", "text": "Final Sign-off", "type": "milestone", "parent": "4.4", "start_date": buildDateFromYMD(2026, 1, 24), "duration": 1, "progress": 0, "open": true },
            { "id": "5.1", "text": "Marketing & Preparation", "type": "project", "parent": "5", "start_date": buildDateFromYMD(2026, 1, 25), "duration": 4, "progress": 0, "open": true },
            { "id": "5.1.1", "text": "Create Marketing Strategy", "type": "task", "parent": "5.1", "start_date": buildDateFromYMD(2026, 1, 25), "duration": 2, "progress": 0, "open": true },
            { "id": "5.1.2", "text": "Social Media Plan", "type": "task", "parent": "5.1", "start_date": buildDateFromYMD(2026, 1, 27), "duration": 2, "progress": 0, "open": true },
            { "id": "5.2", "text": "Product Launch", "type": "milestone", "parent": "5", "start_date": buildDateFromYMD(2026, 2, 1), "duration": 1, "progress": 0, "open": true }
        ],
        "links": [
            { "source": "2.1", "target": "2.2", "type": "0" },
            { "source": "2.1.1", "target": "2.1.2", "type": "0" },
            { "source": "2.2", "target": "2.3", "type": "0" },
            { "source": "2.2.1", "target": "2.2.2", "type": "0" },
            { "source": "2.3", "target": "2.4", "type": "0" },
            { "source": "2.3.1", "target": "2.3.2", "type": "0" },
            { "source": "2.4.1", "target": "2.4.2", "type": "0" },
            { "source": "3.1", "target": "3.2", "type": "0" },
            { "source": "3.1.1", "target": "3.1.2", "type": "0" },
            { "source": "3.2", "target": "3.3", "type": "0" },
            { "source": "3.2.1", "target": "3.2.2", "type": "0" },
            { "source": "3.2.2", "target": "3.2.3", "type": "0" },
            { "source": "3.2.3", "target": "3.2.4", "type": "0" },
            { "source": "3.3", "target": "3.4", "type": "0" },
            { "source": "3.3.1", "target": "3.3.2", "type": "0" },
            { "source": "3.3.2", "target": "3.3.3", "type": "0" },
            { "source": "3.4", "target": "3.5", "type": "0" },
            { "source": "3.4.1", "target": "3.4.2", "type": "0" },
            { "source": "3.5.1", "target": "3.5.2", "type": "0" },
            { "source": "4.1", "target": "4.2", "type": "0" },
            { "source": "4.1.1", "target": "4.1.2", "type": "0" },
            { "source": "4.2", "target": "4.3", "type": "0" },
            { "source": "4.2.1", "target": "4.2.2", "type": "0" },
            { "source": "4.3", "target": "4.4", "type": "0" },
            { "source": "4.3.1", "target": "4.3.2", "type": "0" },
            { "source": "4.4.1", "target": "4.4.2", "type": "0" },
            { "source": "5.1", "target": "5.2", "type": "0" },
            { "source": "5.1.1", "target": "5.1.2", "type": "0" }
        ]
    };

    // Recursive function to find the top-level project of a given task by its ID
    function getTopProject(id) {
        const item = gantt.getTask(id);
        if (!item) {
            return null; // Return null if task doesn't exist
        }
        if (item.type === "project") return item.id; // If task is a project, return its ID
        return getTopProject(item.parent); // Otherwise, recurse upwards to find top project
    }

    // Function to select a task in the Gantt chart and sync with diagram
    function selectGanttTask(id) {
        if (!gantt.isTaskExists(id)) {
            // return false;
        }
        if (diagram.data.exists(id)) {
            diagram.selection.add({ id });
        }
        gantt.showTask(id);
        gantt.selectTask(id);
        return true;
    }

    // Function to select a task in the diagram and sync with Gantt chart
    function selectDiagramTask(id) {
        if (!diagram.data.exists(id)) {
            return;
        }
        if (gantt.isTaskExists(id)) {
            gantt.selectTask(id);
        }
        diagram.showItem(id);
        diagram.selection.add({ id });     
    }

    // Function to update the diagram's scale (zoom level)
    function updateScaleValue(diagram, delta) {
        if (diagram.config.scale <= 0.2 && delta > 0) return;
        if (delta > 0) {
            diagram.config.scale -= 0.05; 
        } else {
            diagram.config.scale += 0.05; 
        }
        diagram.paint();
    
        const textElement = document.querySelector(".scale-panel__text");
        textElement.textContent = `${(diagram.config.scale * 100).toFixed(0)}%`;
    }

    // Function to create a scale control panel with zoom in/out buttons
    function createScalePanel(diagram) {
        // Create the container for the scale control panel
        const panel = document.createElement("div");
        panel.className = "scale-panel";

        // Create the zoom-in button
        const plusButton = document.createElement("button");
        plusButton.innerHTML = "<i class='dxi dxi-plus'></i>";
        plusButton.addEventListener("click", () => {
            updateScaleValue(diagram, -1);
        });

        // Create the text element to display current scale percentage
        const textElement = document.createElement("span");
        textElement.className = "scale-panel__text";
        textElement.textContent = `${(diagram.config.scale * 100).toFixed(0)}%`;

        // Create the zoom-out button
        const minusButton = document.createElement("button");
        minusButton.innerHTML = "<i class='dxi dxi-minus'></i>";
        minusButton.addEventListener("click", () => {
            updateScaleValue(diagram, 1);
        });

        // Append buttons and text element to the panel
        panel.appendChild(minusButton);
        panel.appendChild(textElement);
        panel.appendChild(plusButton);

        // Append panel to the body
        document.body.appendChild(panel);
    }

    // Add event listener for mouse wheel to control zoom with Ctrl/Meta key
    document.getElementById("diagram").addEventListener("wheel", event => {
        if (event.ctrlKey || event.metaKey) {
            event.preventDefault();
            updateScaleValue(diagram, event.deltaY || event.detail || event.wheelDelta);
        }
    });
</script>