I have a set of D3js arc graphs that were working fine until I made them viewable only if selected using a menu. The function which wraps the text on the labels, entitled wrap() is working on the graph that is visible when you load the page, but now the rest aren't calling the wrap() function so the label text is way too wide. Do I call the wrap() function in my function that is used to select the graphs, or is my syntax incorrect?
In the interest of providing less code I have removed everything pertaining to the graphs that aren't the first two. I have tried calling the wrap() function in an onclick with one of the option buttons on the select menu, as well as including it in the if/else statement, neither of which worked. Here is the fiddle and the code, thanks in advance!
https://jsfiddle.net/arkatark/ygakxn4z/3/
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://d3js.org/d3.v4.js"></script>
<style>
text {
font-family: Gotham Narrow, sans-serif;
}
.intro {
font-family: Gotham Narrow, sans-serif;
font-size: 2em;
max-width: 700px;
text-align: center;
margin-left: auto;
margin-right: auto;
}
.container {
position: absolute;
top: 10%;
left: 50%;
margin-top: -50px;
margin-left: -50px;
width: 590px;
}
#graph1822 {
margin-left: auto;
margin-right: auto;
margin-bottom: 100px;
margin-top: 0px;
border-style: solid;
width: 580px;
}
#graph1823,
#graph1824,
#graph1825,
#graph1826,
#graph1827 {
margin-left: auto;
margin-right: auto;
margin-bottom: 100px;
margin-top: 0px;
border-style: solid;
display: none;
width: 580px;
}
#my_dataviz {
margin-left: auto;
margin-right: auto;
margin-bottom: 100px;
margin-top: 0px;
}
label {
font-family: Gotham Narrow, sans-serif;
}
select {
width: 55px;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div >
<div >
<h4 >
Arc Diagram of Colonial Secretaries' Correspondence
</h4>
</div>
<div >
<div id="my_dataviz"></div>
<label for="dropdown">Choose a year:</label>
<select id="dropdown">
<option value="0">1822</option>
<option value="1">1823</option>
<option value="2">1824</option>
<option value="3">1825</option>
<option value="4">1826</option>
<option value="5">1827</option>
</select>
<div id="graph1822"></div>
<div id="graph1823"></div>
<div id="graph1824"></div>
<div id="graph1825"></div>
<div id="graph1826"></div>
<div id="graph1827"></div>
</div>
</div>
<script>
// set the dimensions and margins of the graph
var el = document.getElementById("dropdown");
el.addEventListener("change", function() {
var elems = document.querySelectorAll('#graph1822, #graph1823 ,#graph1824 ,#graph1825, #graph1826, #graph1827')
for (var i = 0; i < elems.length; i ) {
elems[i].style.display = 'none'
}
if (this.selectedIndex === 0) {
document.querySelector('#graph1822').style.display = 'block';
document.querySelector('#dropdown').style.outlineColor = '#E9D8A6';
} else if (this.selectedIndex === 1) {
document.querySelector('#graph1823').style.display = 'block';
document.querySelector('#graph1822').style.display = 'none';
document.querySelector('#dropdown').style.outlineColor = '#E9B006';
} else if (this.selectedIndex === 2) {
document.querySelector('#graph1824').style.display = 'block';
document.querySelector('#graph1822').style.display = 'none';
document.querySelector('#dropdown').style.outlineColor = '#BB3E03';
} else if (this.selectedIndex === 3) {
document.querySelector('#graph1825').style.display = 'block';
document.querySelector('#graph1822').style.display = 'none';
document.querySelector('#dropdown').style.outlineColor = '#005F73';
} else if (this.selectedIndex === 4) {
document.querySelector('#graph1825').style.display = 'block';
document.querySelector('#graph1822').style.display = 'none';
document.querySelector('#dropdown').style.outlineColor = '#0A9396';
} else if (this.selectedIndex === 5) {
document.querySelector('#graph1825').style.display = 'block';
document.querySelector('#graph1822').style.display = 'none';
document.querySelector('#dropdown').style.outlineColor = '#BCE6E6';
}
}, false);
// DIAGRAM 1822 //
(function() {
var margin = {
top: 30,
right: 60,
bottom: 30,
left: 60
},
width = 580 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#graph1822")
.append("svg")
.attr("width", width margin.left margin.right)
.attr("height", height margin.top margin.bottom)
.append("g")
.attr("transform",
"translate(" margin.left "," margin.top ")")
// Read data
d3.json("https://raw.githubusercontent.com/arkatark/colsec/main/nodes.json", function(data) {
// List of node names
var allNodes = data.nodes1.map(function(d) {
return d.name
})
// A linear scale to position the nodes on the X axis
var x = d3.scalePoint()
.range([0, width])
.domain(allNodes)
var color = d3.scaleOrdinal().domain([1, 10])
.range(["#E9D8A6", "#E9B006", "#E9B006", "#BB3E03", "#005F73", "#0A9396", "#BCE6E6"]);
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text().split(/\s /).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
x = text.attr("x"),
y = text.attr("y"),
dy = 0, //parseFloat(text.attr("dy")),
tspan = text.text(null)
.append("tspan")
.attr("x", x)
.attr("y", y)
.attr("dy", dy "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan")
.attr("x", x)
.attr("y", y)
.attr("dy", lineNumber * lineHeight dy "em")
.text(word);
}
}
});
}
// Add the circle for the nodes
var nodes = svg
.selectAll("mynodes")
.data(data.nodes1)
.enter()
.append("circle")
.attr("cx", function(d) {
return (x(d.name))
})
.attr("cy", height - 30)
.attr("r", 10)
.style("fill", function(d) {
return color(d.grp)
})
// And give them a label
var labels = svg
.selectAll("mylabels")
.data(data.nodes1)
.enter()
.append("text")
.attr("x", function(d) {
return (x(d.name))
})
.attr("y", height - 10)
.text(function(d) {
return (d.name)
})
.style("text-anchor", "middle")
.style("font-size", "10")
.call(wrap, 100);
// Add links between nodes. Here is the tricky part.
// In my input data, links are provided between nodes -id-, NOT between node names.
// So I have to do a link between this id and the name
var idToNode = {};
data.nodes.forEach(function(n) {
idToNode[n.id] = n;
});
// Cool, now if I do idToNode["2"].name I've got the name of the node with id 2
// Add the links
var links = svg
.selectAll('mylinks')
.data(data.links1)
.enter()
.append('path')
.attr('d', function(d) {
start = x(idToNode[d.source].name) // X position of start node on the X axis
end = x(idToNode[d.target].name) // X position of end node
return ['M', start, height - 30, // the arc starts at the coordinate x=start, y=height-30 (where the starting node is)
'A', // This means we're gonna build an elliptical arc
(start - end) / 1.95, ',', // Next 2 lines are the coordinates of the inflexion point. Height of this point is proportional with start - end distance
(start - end) / 2, 0, 0, ',',
start < end ? 1 : 0, end, ',', height - 30
] // We always want the arc on top. So if end is before start, putting 0 here turn the arc upside down.
.join(' ');
})
.style("fill", "none")
.attr("stroke", "#919191")
var tooltip1 = d3.select("#graph1822")
.append("div")
.style("opacity", 0)
.attr("class", "tooltip")
.style("background-color", "#FFFFFF")
.style("border-style", "solid")
.style("border-width", "1px")
.style("position", "absolute")
.style('width', '200px')
// Add the highlighting functionality
nodes
.on('mouseover', function(d) {
// Highlight the nodes: every node is green except of him
nodes.style('fill', function(d) {
return color(d.grp)
})
d3.select(this).style('fill', function(d) {
return color(d.grp)
})
// Highlight the connections
links
.style('stroke', function(link_d) {
return link_d.source === d.id || link_d.target === d.id ? color(d.grp) : '#b8b8b8';
})
.style('stroke-width', function(link_d) {
return link_d.source === d.id || link_d.target === d.id ? 4 : 1;
})
tooltip1
.html(d.name)
.style('text-align', 'center')
.style('font-family', 'Gotham Narrow, sans-serif')
.style('font-size', '.9em')
.style('margin-left', '29%')
.style('margin-right', '30%')
.style("opacity", .8)
.style("stroke", "#EEEEEE")
.style("background-color", "#FFFFFF")
.style("z-index", 20)
.style('padding', '10px')
})
.on('mouseout', function(d) {
nodes.attr("r", '8').style("fill", function(d) {
return color(d.grp)
})
links
.style('stroke', '#919191')
.style('stroke-opacity', 1)
.style('stroke-width', '1')
tooltip1
.style("opacity", 0)
d3.select(this)
.style("stroke", "none")
.style("opacity", 1)
})
})
svg
.append("text")
.attr("text-anchor", "middle")
.style("fill", "#black")
.style("font-size", "17px")
.attr("x", 225)
.attr("y", 40)
.html(" Correspondence from 1822")
})();
// DIAGRAM 1823 //
(function() {
var margin = {
top: 40,
right: 60,
bottom: 30,
left: 60
},
width = 580 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#graph1823")
.append("svg")
.attr("width", width margin.left margin.right)
.attr("height", height margin.top margin.bottom)
.append("g")
.attr("transform",
"translate(" margin.left "," margin.top ")");
// Read data
d3.json("https://raw.githubusercontent.com/arkatark/colsec/main/nodes.json", function(data) {
// List of node names
var allNodes = data.nodes2.map(function(d) {
return d.name
})
// A linear scale to position the nodes on the X axis
var x = d3.scalePoint()
.range([0, width])
.domain(allNodes)
var color = d3.scaleOrdinal().domain([1, 10])
.range(["#E9D8A6", "#E9B006", "#E9B006", "#BB3E03", "#005F73", "#0A9396", "#BCE6E6"]);
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text().split(/\s /).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
x = text.attr("x"),
y = text.attr("y"),
dy = 0, //parseFloat(text.attr("dy")),
tspan = text.text(null)
.append("tspan")
.attr("x", x)
.attr("y", y)
.attr("dy", dy "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan")
.attr("x", x)
.attr("y", y)
.attr("dy", lineNumber * lineHeight dy "em")
.text(word);
}
}
});
}
// Add the circle for the nodes
var nodes = svg
.selectAll("mynodes")
.data(data.nodes2)
.enter()
.append("circle")
.attr("cx", function(d) {
return (x(d.name))
})
.attr("cy", height - 30)
.attr("r", 8)
.style("fill", function(d) {
return color(d.grp)
})
// And give them a label
var labels = svg
.selectAll("mylabels")
.data(data.nodes2)
.enter()
.append("text")
.attr("x", function(d) {
return (x(d.name))
})
.attr("y", height - 10)
.text(function(d) {
return (d.name)
})
.style("text-anchor", "middle")
.style("font-size", "10")
.call(wrap, 130);
// Add links between nodes. Here is the tricky part.
// In my input data, links are provided between nodes -id-, NOT between node names.
// So I have to do a link between this id and the name
var idToNode = {};
data.nodes.forEach(function(n) {
idToNode[n.id] = n;
});
// Cool, now if I do idToNode["2"].name I've got the name of the node with id 2
// Add the links
var links = svg
.selectAll('mylinks')
.data(data.links2)
.enter()
.append('path')
.attr('d', function(d) {
start = x(idToNode[d.source].name) // X position of start node on the X axis
end = x(idToNode[d.target].name) // X position of end node
return ['M', start, height - 30, // the arc starts at the coordinate x=start, y=height-30 (where the starting node is)
'A', // This means we're gonna build an elliptical arc
(start - end) / 1.95, ',', // Next 2 lines are the coordinates of the inflexion point. Height of this point is proportional with start - end distance
(start - end) / 2, 0, 0, ',',
start < end ? 1 : 0, end, ',', height - 30
] // We always want the arc on top. So if end is before start, putting 0 here turn the arc upside down.
.join(' ');
})
.style("fill", "none")
.attr("stroke", "#919191")
var tooltip1 = d3.select("#graph1823")
.append("div")
.style("opacity", 0)
.attr("class", "tooltip")
.style("background-color", "#FFFFFF")
.style("border-style", "solid")
.style("border-width", "1px")
.style("position", "absolute")
.style('width', '200px')
// Add the highlighting functionality
nodes
.on('mouseover', function(d) {
// Highlight the nodes: every node is green except of him
nodes.style('fill', function(d) {
return color(d.grp)
})
d3.select(this).style('fill', function(d) {
return color(d.grp)
})
// Highlight the connections
links
.style('stroke', function(link_d) {
return link_d.source === d.id || link_d.target === d.id ? color(d.grp) : '#b8b8b8';
})
.style('stroke-width', function(link_d) {
return link_d.source === d.id || link_d.target === d.id ? 4 : 1;
})
tooltip1
.html(d.name)
.style('text-align', 'center')
.style('font-family', 'Gotham Narrow, sans-serif')
.style('font-size', '.9em')
.style('margin-left', '30%')
.style('margin-right', '30%')
.style("opacity", .8)
.style("stroke", "#EEEEEE")
.style("background-color", "#FFFFFF")
.style("z-index", 20)
.style('padding', '10px')
})
.on('mouseout', function(d) {
nodes.attr("r", '8').style("fill", function(d) {
return color(d.grp)
})
links
.style('stroke', '#919191')
.style('stroke-opacity', 1)
.style('stroke-width', '1')
tooltip1
.style("opacity", 0)
d3.select(this)
.style("stroke", "none")
.style("opacity", 1)
})
})
svg
.append("text")
.attr("text-anchor", "middle")
.style("fill", "#black")
.style("font-size", "17px")
.attr("x", 225)
.attr("y", 40)
.html(" Correspondence from 1823")
})();
</script>
</body>
</html>
CodePudding user response:
The problem is that the getComputedTextLength()
function cannot be used to determine the length of the text on a element not rendered (display: none). So the wrap()
function does not work on the graphs not displayed initially.
Perhaps you could render a graph only when it is selected in the dropdown ? This will also avoid duplication in your code.