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>