I have a problem making a sankey chart with d3. It has to be in an html file without using frameworks so that it can be sent without further ado. The problem is that nodes 4 and 5 are overlapping and after node 7 there is a space that should be occupied by the nodes above. I don’t know if it is a problem of percentages in the values of the links, but they are fine because it is more or less the size I want them to be. If in addition to solve that you help me to put the text at the top left inside the nodes, great. This is the code:
`
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gráfico de Sankey</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-sankey/0.12.3/d3-sankey.min.js"></script>
<style>
body,
html {
margin: 5px;
padding: 10px;
overflow: hidden;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif, sans-serif;
display: flex;
flex-direction: column;
}
h1 {
color: #004B87;
margin: 20px 0;
}
#container {
width: 100vw;
height: 80vh;
}
.link {
fill: none;
stroke-opacity: 0.6;
}
.node text {
fill: #000000;
font-size: 16px;
text-anchor: middle;
}
.node rect {
cursor: default;
fill-opacity: .9;
}
.tooltip {
position: fixed;
background-color: #ccecf1;
border: 1px solid #ccc;
padding: 10px;
border-radius: 4px;
pointer-events: none;
font-size: 16px;
display: none;
max-width: 300px;
max-height: 130px;
}
.link:hover {
stroke-opacity: 1.5;
}
</style>
</head>
<body>
<h1 style="color: #004B87;">Journey de Use Case Discovery</h1>
<div id="container">
<svg></svg>
<div class="tooltip"></div>
</div>
<script>
const container = document.getElementById("container");
const width = container.offsetWidth;
const height = container.offsetHeight;
const svg = d3.select("svg")
.attr("viewBox", `0 0 ${width} ${height}`)
.attr("preserveAspectRatio", "xMidYMid meet");
const sankey = d3.sankey()
.nodeWidth(130)
.nodePadding(10)
.size([width, height]);
const data = {
"nodes": [
{ "node": 0, "name": "0", "level": 0 },
{ "node": 1, "name": "1", "level": 1 },
{ "node": 2, "name": "2", "level": 1 },
{ "node": 3, "name": "3", "level": 1 },
{ "node": 4, "name": "4", "level": 2 },
{ "node": 5, "name": "5", "level": 2 },
{ "node": 6, "name": "6", "level": 2 },
{ "node": 7, "name": "7", "level": 2 },
{ "node": 8, "name": "8", "level": 2 },
{ "node": 9, "name": "9", "level": 2 },
{ "node": 10, "name": "10", "level": 2 },
{ "node": 11, "name": "11", "level": 3 },
{ "node": 12, "name": "12", "level": 3 },
{ "node": 13, "name": "13", "level": 3 },
{ "node": 14, "name": "14", "level": 4 }
],
"links": [
{ "source": 0, "target": 1, "value": 0.57, "message": "hola" },
{ "source": 0, "target": 2, "value": 0.350, "message": "hola" },
{ "source": 0, "target": 3, "value": 0.14, "message": "hola" },
{ "source": 1, "target": 4, "value": 0.19195, "message": "hola" },
{ "source": 1, "target": 5, "value": 0.15735, "message": "hola" },
{ "source": 1, "target": 6, "value": 0.12365, "message": "hola" },
{ "source": 1, "target": 7, "value": 0.09705, "message": "hola" },
{ "source": 2, "target": 4, "value": 0.1527, "message": "hola" },
{ "source": 2, "target": 8, "value": 0.1164, "message": "hola" },
{ "source": 2, "target": 9, "value": 0.0809, "message": "hola" },
{ "source": 3, "target": 10, "value": 0.14, "message": "hola" },
{ "source": 5, "target": 11, "value": 0.1446, "message": "hola" },
{ "source": 6, "target": 12, "value": 0.133, "message": "hola" },
{ "source": 7, "target": 12, "value": 0.09705, "message": "hola" },
{ "source": 8, "target": 11, "value": 0.1164, "message": "hola" },
{ "source": 9, "target": 13, "value": 0.0809, "message": "hola" },
{ "source": 13, "target": 14, "value": 0.0809, "message": "hola" }
]
}
const graph = sankey(data);
graph.nodes.forEach(node => {
node.x0 = node.level * (width / 5);
node.x1 = node.x0 + sankey.nodeWidth();
});
const tooltip = d3.select(".tooltip");
svg.append("g")
.selectAll(".link")
.data(graph.links)
.join("path")
.attr("class", "link")
.attr("d", d3.sankeyLinkHorizontal())
.style("stroke-width", d => Math.max(1, d.width))
.style("stroke", d => d.source.index === 0 ? "#9EC2E5" : "#FFCC80")
.on("mouseover", (event, d) => {
tooltip.style("display", "block")
.html(d.message)
.style("left", `${event.pageX + 5}px`)
.style("top", `${event.pageY - 20}px`);
})
.on("mouseout", () => tooltip.style("display", "none"));
const node = svg.append("g")
.selectAll(".node")
.data(graph.nodes)
.join("g")
.attr("class", "node")
.attr("transform", d => `translate(${d.x0},${d.y0})`)
node.append("rect")
.attr("height", d => d.y1 - d.y0)
.attr("width", sankey.nodeWidth())
.style("fill", d => d.level === 0 ? "#4E79A6" : d.level === 1 ? "#9FCBE8" : "#F28E2C")
.append("title")
.text(d => d.name);
node.append("text")
.attr("x", sankey.nodeWidth() / 2)
.attr("y", d => (d.y1 - d.y0) / 2)
.attr("dy", "0.35em")
.text(d => d.name);
</script>
</body>
</html>
`
I have a problem making a sankey chart with d3. It has to be in an html file without using frameworks so that it can be sent without further ado. The problem is that nodes 4 and 5 are overlapping and after node 7 there is a space that should be occupied by the nodes above. If in addition to solve that you help me to put the text at the top left inside the nodes, great. This is the code: