How to make mouseover functionality permanent - d3.js

I want to make a force graph with permanently visible text on the nodes and text on the links.
This snippet provides labels on the nodes.
The 2nd snippet from an answer to Show tool-tip on links of force directed graph in d3js provides mouseover labels on links and nodes.
I'm currently trying to extend the second to make the mouseover node labels permanent.
var width = 400;
var height = 125;
var margin = 20;
var pad = margin / 2;
var graph = { "nodes":[ { "name": "A"}, { "name": "B"}] };
drawGraph(graph);
function drawGraph(graph) {
var svg = d3.select("#force").append("svg")
.attr("width", width)
.attr("height", height);
// create an area within svg for plotting graph
var plot = svg.append("g")
.attr("id", "plot")
.attr("transform", "translate(" + pad + ", " + pad + ")");
var layout = d3.layout.force()
.size([width - margin, height - margin])
.charge(-120)
.nodes(graph.nodes)
.start();
drawNodes(graph.nodes);
// add ability to drag and update layout
d3.selectAll(".node").call(layout.drag);
layout.on("tick", function() {
d3.selectAll(".node")
.attr("cx", d => { return d.x; })
.attr("cy", d => { return d.y; });
});
}
// Draws nodes on plot
function drawNodes(nodes) {
d3.select("#plot").selectAll(".node")
.data(nodes)
.enter()
.append("circle")
.attr("class", "node")
.attr("id", (d, i) => { return d.name; })
.attr("cx", (d, i) => { return d.x; })
.attr("cy", (d, i) => { return d.y; })
.attr("r", 4)
.style("fill", "#EE77b4")
.on("mouseover", function(d, i) {
var x = d3.mouse(this)[0];
var y = d3.mouse(this)[1];
var tooltip = d3.select("#plot")
.append("text")
.text(d.name)
.attr("x", x)
.attr("y", y)
.attr("id", "tooltip");
})
.on("mouseout", function(d, i) {
d3.select("#tooltip").remove();
});
}
body {
font-family: 'Source Sans Pro', sans-serif;
font-weight: 300;
}
b {
font-weight: 900;
}
.outline {
fill: none;
stroke: #888888;
stroke-width: 1px;
}
#tooltip {
font-size: 10pt;
font-weight: 900;
fill: #000000;
stroke: #ffffff;
stroke-width: 0.25px;
}
.node {
stroke: #ffffff;
stroke-weight: 1px;
}
.highlight {
stroke: red;
stroke-weight: 4px;
stroke-opacity: 1.0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div align="center" id="force"></div>
I've tried replacing the mouseover function with:
.each( function(d, i) {
var x = d.x; //d3.mouse(this)[0];
var y = d.y; //d3.mouse(this)[1];
var tooltip = d3.select("#plot")
.append("text")
.text(d.name)
.attr("x", x)
.attr("y", y)
.attr("id", "tooltip");
})
but now the labels don't move so I added
d3.selectAll("text").attr( "x", d => { return d.x; })
.attr( "y", d => { return d.y; });
in layout.on("tick", function() ...
But now it's all in one place doesn't move and I get TypeError: d is undefined

Rewrite your code this way (pay attention on the comments):
layout.on("tick", function() {
tooltips // here we set new position for tooltips on every tick
.attr("x", (d, i) => { return d.x; })
.attr("y", (d, i) => { return d.y; });
d3.selectAll(".node")
.attr("cx", d => { return d.x; })
.attr("cy", d => { return d.y; });
});
...
function drawNodes(nodes) {
tooltips = d3.select("#plot").selectAll(".node")
.data(nodes)
.enter()
.append("circle")
.attr("class", "node")
.attr("id", (d, i) => { return d.name; })
.attr("cx", (d, i) => { return d.x; })
.attr("cy", (d, i) => { return d.y; })
.attr("r", 4)
.style("fill", "#EE77b4")
.select(function() { return this.parentNode }) // returns to parent node
.append('text') // append svg-text elements for tooltip
.data(nodes)
.text(function(d) { return d.name; }) // set text
.attr("x", (d, i) => { return d.x; }) // set initial x position
.attr("y", (d, i) => { return d.y; }) // set initial y position
.attr("id", function(d,i) { return "tooltip-" + i; }) // set unique id
.attr("class", "d3-tooltip");
}
Working demo:
var width = 400;
var height = 125;
var margin = 20;
var pad = margin / 2;
var tooltips = null;
var graph = { "nodes":[ { "name": "A"}, { "name": "B"}] };
drawGraph(graph);
function drawGraph(graph) {
var svg = d3.select("#force").append("svg")
.attr("width", width)
.attr("height", height);
// create an area within svg for plotting graph
var plot = svg.append("g")
.attr("id", "plot")
.attr("transform", "translate(" + pad + ", " + pad + ")");
var layout = d3.layout.force()
.size([width - margin, height - margin])
.charge(-120)
.nodes(graph.nodes)
.start();
drawNodes(graph.nodes);
// add ability to drag and update layout
d3.selectAll(".node").call(layout.drag);
layout.on("tick", function() {
tooltips
.attr("x", (d, i) => { return d.x; })
.attr("y", (d, i) => { return d.y; });
d3.selectAll(".node")
.attr("cx", d => { return d.x; })
.attr("cy", d => { return d.y; });
});
}
// Draws nodes on plot
function drawNodes(nodes) {
tooltips = d3.select("#plot").selectAll(".node")
.data(nodes)
.enter()
.append("circle")
.attr("class", "node")
.attr("id", (d, i) => { return d.name; })
.attr("cx", (d, i) => { return d.x; })
.attr("cy", (d, i) => { return d.y; })
.attr("r", 4)
.style("fill", "#EE77b4")
.select(function() { return this.parentNode })
.append('text')
.data(nodes)
.text(function(d) { return d.name; })
.attr("x", (d, i) => { return d.x; })
.attr("y", (d, i) => { return d.y; })
.attr("class", "d3-tooltip")
.attr("id", function(d,i) { return "tooltip-" + i; });
}
body {
font-family: 'Source Sans Pro', sans-serif;
font-weight: 300;
}
b {
font-weight: 900;
}
.outline {
fill: none;
stroke: #888888;
stroke-width: 1px;
}
.d3-tooltip {
font-size: 20pt;
font-family: 'Comic Sans MS';
font-weight: 900;
fill: #000000;
stroke: #ffffff;
stroke-width: 0.25px;
}
.node {
stroke: #ffffff;
stroke-weight: 1px;
}
.highlight {
stroke: red;
stroke-weight: 4px;
stroke-opacity: 1.0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div align="center" id="force"></div>

Related

D3 force-directed graph( v4) is not accepting img as nodes

I am trying to generate a force directed graph. I am able to achieve it if I use 'circle/rect' to draw nodes. But I want to use an image instead. What am I doing wrong?
Here is how I create and transform nodes (I am using d3 v4):
var node = svg.append("g")
.attr("class", "nodes f32")
.selectAll("img")
.data(json.nodes)
.enter().append("img")
.attr("class","flag ar")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
function ticked() {
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node
.style("left", function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); })
.style("top", function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); });
}
And here is a demo of what I have so far:
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
radius=5;
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d,index) { return d.id; }).distance(10))
.force("charge", d3.forceManyBody().distanceMin(10).distanceMax(120))
.force("center", d3.forceCenter(width / 2, height / 2));
d3.json("https://raw.githubusercontent.com/DealPete/forceDirected/master/countries.json",function(json ) {
json.nodes.forEach(function(d,i){
d.id = i;
})
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(json.links)
.enter().append("line")
.attr("stroke-width","1");
var node = svg.append("g")
.attr("class", "nodes f32")
.selectAll("img")
.data(json.nodes)
.enter().append("img")
.attr("class","flag ar")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
function ticked() {
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node
.style("left", function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); })
.style("top", function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); });
}
simulation
.nodes(json.nodes)
.on("tick", ticked);
simulation.force("link")
.links(json.links);
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
h1{
font-family: arial;
}
body{
display:flex;
justify-content:center;
align-items:center;
flex-direction: column;
background:#d64d4d;
}
.fdd{
width:1000px;
height:500px;
background: white;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<h1>Force Directed Graph of State Contiguity</h1>
<div class="fdd">
<svg width="1000" height="500"></svg>
</div>
To include an image element, you should use .append("image") instead of .append("img").
In addition, the image itself needs to be specified with the xlink:href attribute. You can provide a link to an image for instance.
These 2 points combined gives the following snippet:
.append("image")
.attr("class","flag ar")
.attr("xlink:href", "https://cdn.sstatic.net/Sites/stackoverflow/img/favicon.ico")
In addition, the location of nodes seems to be off; you can set their position this way (by translating them):
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
Here is a demo:
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
radius=5;
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d,index) { return d.id; }).distance(10))
.force("charge", d3.forceManyBody().distanceMin(10).distanceMax(120))
.force("center", d3.forceCenter(width / 2, height / 2));
d3.json("https://raw.githubusercontent.com/DealPete/forceDirected/master/countries.json",function(json ) {
json.nodes.forEach(function(d,i){
d.id = i;
})
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(json.links)
.enter().append("line")
.attr("stroke-width","1");
var node = svg.append("g")
.attr("class", "nodes f32")
.selectAll("image")
.data(json.nodes)
.enter().append("image")
.attr("class","flag ar")
.attr("xlink:href", "https://cdn.sstatic.net/Sites/stackoverflow/img/favicon.ico")
.attr("height", "32") // width/height (are necessary in Firefox to make the image appear)
.attr("width", "32")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
function ticked() {
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
// -16 is half the width/height of the image I used:
node
.attr("transform", function(d) { return "translate(" + (d.x - 16) + "," + (d.y - 16) + ")"; })
}
simulation
.nodes(json.nodes)
.on("tick", ticked);
simulation.force("link")
.links(json.links);
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
h1{
font-family: arial;
}
body{
display:flex;
justify-content:center;
align-items:center;
flex-direction: column;
background:#d64d4d;
}
.fdd{
width:1000px;
height:500px;
background: white;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<h1>Force Directed Graph of State Contiguity</h1>
<div class="fdd">
<svg width="1000" height="500"></svg>
</div>
I was confused between img and svg:image .
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
radius=5;
var graph = d3.select(".fdd")
.append("div")
.style("width", width + "px")
.style("height", height + "px")
.attr('class',"countries f16")
var node = graph
.selectAll("img")
.data(json.nodes)
.enter().append("img")
.attr("class",function(d){return "flag " +d.code} )
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
function ticked() {
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node
.style("top", function(d) { return d.y + "px"; } )
.style("left", function(d) { return d.x + "px"; });
}
Having img inside svg wasn't working.
Complete solution can be found here:
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
radius=5;
var graph = d3.select(".fdd")
.append("div")
.style("width", width + "px")
.style("height", height + "px")
.attr('class',"countries f16")
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d,index) { return d.id; }).distance(10))
.force("charge", d3.forceManyBody().distanceMin(10).distanceMax(120))
.force("center", d3.forceCenter(width / 2, height / 2));
d3.json("https://raw.githubusercontent.com/DealPete/forceDirected/master/countries.json",function(json ) {
json.nodes.forEach(function(d,i){
d.id = i;
})
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(json.links)
.enter().append("line")
.attr("stroke-width","1");
var node = graph
.selectAll("img")
.data(json.nodes)
.enter().append("img")
.attr("class",function(d){return "flag " +d.code} )
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("mouseover", function(data,a){
return tooltip
.style("visibility", "visible")
.attr('class', 'd3-tip')
.html("<div class='data'>"+data.country+"</div>")
.style("top", (event.pageY-10)+"px").style("left",(event.pageX+10)+"px");})
.on("mouseout", function(){
return tooltip.style("visibility", "hidden");});
var tooltip = d3.select("body")
.append("div")
.style("position", "absolute")
.style("z-index", "100000")
.style("visibility", "hidden")
.text("a simple tooltip");
function ticked() {
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node
.style("top", function(d) { return d.y + "px"; } )
.style("left", function(d) { return d.x + "px"; });
}
simulation
.nodes(json.nodes)
.on("tick", ticked);
simulation.force("link")
.links(json.links);
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
svg{
position:absolute;
}
.countries{
width: 100%;
height:100%;
position: relative;
margin: 0 auto;
}
h1{
font-family: arial;
}
body{
display:flex;
justify-content:center;
align-items:center;
flex-direction: column;
background:#d64d4d;
}
.fdd{
width:1000px;
height:500px;
background: white;
}
.flag{
position:absolute;
border-radius: 50%;
border: 0;
transform: translate(-8px,-8px);
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<link href="https://github.com/downloads/lafeber/world-flags-sprite/flags16.css" rel="stylesheet"/>
<h1>Force Directed Graph of State Contiguity</h1>
<div class="fdd">
<svg width="1000" height="500"></svg>
</div>

Displaying correct X-coordinate value on the scatter point tooltip

In my scatterplot, I am hovering on the points, to generate a tooltip. But, my X-cordinate values are not right, corresponding to the graph axis. Can a clue be provided for the same??
One thing to note- I have 2 dataset arrays- 1 corresponds to Y value, the other corresponds to Y-Error values.
var width = 700;
var height = 700;
var padding = 90;
var myData = [12, 18, 20, 9, 17, 25, 30];
var errData = [6, 9, 10, 4.5, 8.5, 12.5, 15];
var svg = d3.select("body").
append("svg")
.attr("width", width)
.attr("height", height);
var Mydiv = d3.select("body")
.append("div")
//.append("span")
//.text('X')
.data(myData)
.attr("class", "toolDiv")
//.attr("span", "close")
.style("opacity", "0");
var MyLine = d3.select("body")
.append("div")
.data(myData)
.attr("class", "error-line");
//.style("opacity", 1);
//var m1 = d3.max(myData + errData)
var yScale = d3.scaleLinear().domain([0, d3.max(myData)]).range([height / 2, 50]);
var xScale = d3.scaleLinear().domain([0, d3.max(myData)]).range([0, width - 100]);
var y_ErScale = d3.scaleLinear().domain([0, d3.max(errData)]).range([height / 2, 50]);
var x_ErScale = d3.scaleLinear().domain([0, d3.max(errData)]).range([0, width - 100]);
var valueline = d3.selectAll("scatter-dots")
.append("line")
.attr("x", function(d, i) {
return xScale(myData[i]);
})
.attr("y", function(d) {
return yScale(d);
});
//.curve();
svg.append("g")
.selectAll("scatter-dots")
.data(myData)
.enter()
.append("line")
.data(myData)
.attr("class", "line")
.attr("d", valueline);
// .style("opacity", 0);
var eBar = d3.select("body").append("svg");
//var x_min =
var x_axis = d3.axisBottom()
.scale(xScale);
var y_axis = d3.axisLeft()
.scale(yScale);
svg.append("g")
.attr("transform", "translate(50, 10)")
.call(y_axis);
var xAxisTranslate = height / 2 + 10;
svg.append("g")
.attr("transform", "translate(50, " + xAxisTranslate + ")")
.call(x_axis);
svg.append("text")
.attr("transform",
"translate(" + (width / 1.2) + " ," + (height - 280) + ")")
.style("text-anchor", "middle")
.style("left", "70px")
.text("Points");
svg.append("text")
.attr("transform",
"translate(" + (width / 2) + " ," + (height - 280) + ")")
.style("text-anchor", "middle")
.style("left", "70px")
.text("X-Axis");
svg.append("text")
.attr("transform",
"translate(" + (height / 40) + " ," + (width - 500) + ")rotate(-90)")
.style("text-anchor", "middle")
.style("left", "70px")
.text("Y-Axis");
// svg.append("text")
// .attr("transform",
// "translate(" + (height/10) + " ," + (width - 690) + ")rotate(-90)")
// .style("text-anchor", "middle")
// .style("left", "70px")
// .text("Y");
svg.append("g")
.selectAll("scatter-dots")
.data(myData)
.enter().append("svg:circle")
.attr("cx", function(d, i) {
return xScale(myData[i]);
})
.attr("cy", function(d) {
return yScale(d);
})
.attr("r", 3)
.style("opacity", 0.8)
.style("cursor", "help")
// .on("click", function(d, i){
// var active = Mydiv.active ? false : true ,
// newOpacity = active ? 0 : 0.9;
// Mydiv.transition()
// .duration(200)
// .style("opacity", newOpacity);
// Mydiv.html("X" + "-" + errData[i] + "<br/>" + "Y" + "-" + myData[i] )
// .style("left", (d3.event.pageX + 10) + "px")
// .style("top", (d3.event.pageY - 18) + "px");
// Mydiv.active = active;
// });
.on("mouseover", function(d, i) {
//console.log(this);
//console.log(d3.select(this));
//d3.select(this);
//console.log(d3.select(this));
Mydiv.transition()
.duration(200)
.style("opacity", 0.9);
Mydiv.html("X" + "-" + errData[i] + "<br/>" + "Y" + "-" + myData[i])
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
//div.html(yScale(d));
})
.on("mouseout", function(d) {
Mydiv.transition()
.duration(500)
.style("opacity", 0);
});
// var errorBar = eBar.append("path")
// .attr("d", yScale(errData))
// .attr("stroke", "red")
// .attr("stroke-width", 1.5);
// svg.append("g")
// .selectAll("error-bars")
// .data(errData)
// .enter().append("svg:path")
// .attr("cx", function (d,i) { return x_ErScale(errData[i]); } )
// .attr("cy", function (d) { return y_ErScale(d); } )
// .attr("stroke", "red")
// .attr("stroke-width", 1.5);
svg.append("g")
.selectAll("line")
.data(errData)
.enter()
.append("line")
.attr("class", "error-line")
.attr("x1", function(d) {
return x_ErScale(d);
})
.attr("y1", function(d) {
return y_ErScale(d) + 30;
})
.attr("x2", function(d) {
return x_ErScale(d);
})
.attr("y2", function(d) {
return y_ErScale(d) + 2;
})
.on("mouseover", function(d) {
MyLine.transition()
//.duration(200)
.style("opacity", "1");
//.style("fill-opacity", 0);
})
.on("mouseout", function(d) {
MyLine.transition()
//.duration(200)
.style("opacity", 0.0);
//.style("fill-opacity", 1);
});
svg.append("g").selectAll("line")
.data(errData).enter()
.append("line")
.attr("class", "error-cap")
.attr("x1", function(d) {
return x_ErScale(d) - 8;
})
.attr("y1", function(d) {
return y_ErScale(d) - 30;
})
.attr("x2", function(d) {
return x_ErScale(d) + 8;
})
.attr("y2", function(d) {
return y_ErScale(d) - 30;
});
svg.append("g")
.selectAll("line")
.data(errData)
.enter()
.append("line")
.attr("class", "error-line")
.attr("x1", function(d) {
return x_ErScale(d);
})
.attr("y1", function(d) {
return y_ErScale(d) - 30;
})
.attr("x2", function(d) {
return x_ErScale(d);
})
.attr("y2", function(d) {
return y_ErScale(d) - 2;
})
.on("mouseover", function(d) {
MyLine.transition()
//.duration(200)
.style("opacity", "1");
//.style("fill-opacity", 0);
})
.on("mouseout", function(d) {
MyLine.transition()
//.duration(200)
.style("opacity", "0.6");
//.style("fill-opacity", 1);
});
// .on("mouseover", function(d){
// MyLine.transition()
// .duration(200)
// .style("opacity", 0);
// })
// .on("mouseout", function(d){
// MyLine.transition()
// .duration(200)
// .style("opacity", 1);
// });
// Add Error Bottom Cap
svg.append("g").selectAll("line")
.data(errData).enter()
.append("line")
.attr("class", "error-cap")
.attr("x1", function(d) {
return x_ErScale(d) - 8;
})
.attr("y1", function(d) {
return y_ErScale(d) + 30;
})
.attr("x2", function(d) {
return x_ErScale(d) + 8;
})
.attr("y2", function(d) {
return y_ErScale(d) + 30;
});
//svg.selectAll("scatter-dots")
// .data(myData)
// .enter()
// .on("mouseover", function()
// {
// //console.log(this);
// //console.log(d3.select(this));
// //d3.select(this);
// //console.log(d3.select(this));
// //div.transition()
// //.duration(200)
// //.style("opacity", 0.8);
// //div.html(myData);
// });
//// .on("mouseout", function(d)
//// {
//// div.transition()
//// .duration(500)
//// .style("opacity", 0);
//// });
/*body {
font: 10px sans-serif;
}
*/
#d1 {
position: relative;
top: 100px;
}
#svg1 {
position: relative;
bottom: 80px;
}
.axis_path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis_line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.dot {
stroke: #000;
cursor: pointer;
}
.error-line {
stroke: #b30059;
stroke-width: 2px;
opacity: 0.6;
stroke-dasharray: 2;
/*fill-opacity: 1;*/
/*opacity: 1;*/
}
.error-cap {
stroke: #b30059;
stroke-width: 2px;
stroke-type: solid;
}
.toolDiv {
position: absolute;
text-align: center;
width: 120px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
/*.toolDiv.image
{
position: static;
content: url(http://wfarm1.dataknet.com/static/resources/icons/set28/7f8535d7.png);
}*/
/*.close {
width: 10px;
height: 10px;
background: #fff;
position: absolute;
top: 0;
right: 0;
background: url('http://i.imgur.com/Idy9R0n.png') no-repeat 0 0;
cursor: pointer;
}
.close:hover {
background-position: -13px 0;
}*/
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
<head>
<title>Scatter Plot Example</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<!-- <script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script> -->
<link rel="stylesheet" type="text/css" href="scatter.css">
</head>
<body>
<script src="scatterplot.js" type="text/javascript"></script>
<div id="d1">
<svg id="svg1">
<div id="d2"></div>
</svg>
</div>
</body>
I've included a fiddle for reference.

Layout with hierarchical grouping Cola.js a.type not a function?

Heading ##Trying to get cola.js "Layout with hierarchical grouping" Set up.
Trying to create the following in my browser:
http://marvl.infotech.monash.edu/webcola/examples/gridifiedSmallGroups.html
I get the error:
d[a.type] is not a function
cola.min.js:2023 Uncaught TypeError: d[a.type] is not a function
return a.d3adaptor = function() {
var d = d3.dispatch("start", "tick", "end"),
e = a.adaptor({
trigger: function(a) {
d[a.type](a)
},
on: function(a, b) {
return d.on(a, b), e
},
kick: function(a) {
d3.timer(a)
},
drag: function() {
var a = d3.behavior.drag().origin(function(a) {
return a
}).on("dragstart.d3adaptor", b).on("drag.d3adaptor", function(a) {
a.px = d3.event.x, a.py = d3.event.y, e.resume()
}).on("dragend.d3adaptor", c);
return arguments.length ? void this.call(a) : a
}
});
return e
},
HTML
<html lang="en">
<head>
<meta charset="utf-8" />
<style>
.node {
stroke: #fff;
stroke-width: 1.5px;
cursor: move;
}
.group {
stroke: #fff;
stroke-width: 1.5px;
cursor: move;
opacity: 0.7;
}
.link {
stroke: #7a4e4e;
stroke-width: 3px;
stroke-opacity: 1;
}
.label {
fill: white;
font-family: Verdana;
font-size: 25px;
text-anchor: middle;
cursor: move;
}
</style>
</head>
<body>
<script src="./d3.min.js" charset="utf-8" type="text/javascript"></script>
<script src="./cola.min.js"></script>
<script>
var width = 960,
height = 500;
var color = d3.scaleOrdinal(d3.schemeCategory20);
var cola = cola.d3adaptor(d3)
.linkDistance(80)
.avoidOverlaps(true)
.handleDisconnected(false)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("smallgrouped.json", function (error, graph) {
graph.nodes.forEach(function (v) {
v.width = v.height = 95;
})
graph.groups.forEach(function (g) { g.padding = 0.01; });
cola
.nodes(graph.nodes)
.links(graph.links)
.groups(graph.groups)
.start(100, 0, 50, 50);
var group = svg.selectAll(".group")
.data(graph.groups)
.enter().append("rect")
.attr("rx", 8).attr("ry", 8)
.attr("class", "group")
.style("fill", function (d, i) { return color(i); });
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link");
var pad = 20;
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("rect")
.attr("class", "node")
.attr("width", function (d) { return d.width - 2 * pad; })
.attr("height", function (d) { return d.height - 2 * pad; })
.attr("rx", 5).attr("ry", 5)
.style("fill", function (d) { return color(graph.groups.length); })
.call(cola.drag)
.on('mouseup', function (d) {
d.fixed = 0;
cola.alpha(1); // fire it off again to satify gridify
});
var label = svg.selectAll(".label")
.data(graph.nodes)
.enter().append("text")
.attr("class", "label")
.text(function (d) { return d.name; })
.call(cola.drag);
node.append("title")
.text(function (d) { return d.name; });
cola.on("tick", function () {
link.attr("x1", function (d) { return d.source.x; })
.attr("y1", function (d) { return d.source.y; })
.attr("x2", function (d) { return d.target.x; })
.attr("y2", function (d) { return d.target.y; });
node.attr("x", function (d) { return d.x - d.width / 2 + pad; })
.attr("y", function (d) { return d.y - d.height / 2 + pad; });
group.attr("x", function (d) { return d.bounds.x; })
.attr("y", function (d) { return d.bounds.y; })
.attr("width", function (d) { return d.bounds.width(); })
.attr("height", function (d) { return d.bounds.height(); });
label.attr("x", function (d) { return d.x; })
.attr("y", function (d) {
var h = this.getBBox().height;
return d.y + h/4;
});
});
});
</script>
</body>
</html>
JSON
{
"nodes":[
{"name":"a","width":60,"height":40},
{"name":"b","width":60,"height":40},
{"name":"c","width":60,"height":40},
{"name":"d","width":60,"height":40},
{"name":"e","width":60,"height":40},
{"name":"f","width":60,"height":40},
{"name":"g","width":60,"height":40}
],
"links":[
{"source":1,"target":2},
{"source":2,"target":3},
{"source":3,"target":4},
{"source":0,"target":1},
{"source":2,"target":0},
{"source":3,"target":5},
{"source":0,"target":5}
],
"groups":[
{"leaves":[0], "groups":[1]},
{"leaves":[1,2]},
{"leaves":[3,4]}
]
}
I meet the same issue. If you use d3v4 or later version, you will get this error. If you use d3v3, it works well.

Filter table in D3 from selection in parallel coordinates visualization

I have a parallel coordinates visualization and I've added a table below that represents all the data in the visualization.
I want to filter the table based on the selection of the parallel coordinates visualization. So, just showing the data highlighted in the parallel coordinates visualization.
Any ideas on how to do that?
I've tried in the brush section to highlight the cells in the table in red adding a style in for the tags, but it doesn't seem to work. See code below:
// Add and store a brush for each axis.
g.append("g")
.attr("class", "brush")
.each(function(d) {
d3.select(this).call(y[d].brush = d3.svg.brush().y(y[d]).on("brushstart", brushstart).on("brush", brush));
d3.selectAll("td").style('bgcolor', 'red');
})
.selectAll("rect")
.attr("x", -8)
.attr("width", 16);
I've tried to add the highlight of the cells in the brush function. Here I achieve for all the cells to turn red, but I don't know how to highlight only the ones selected in the selection.
function brush() {
var actives = dimensions.filter(function(p) {
return !y[p].brush.empty();
}),
extents = actives.map(function(p) {
return y[p].brush.extent();
});
foreground.style("display", function(d) {
return actives.every(function(p, i) {
return extents[i][0] <= d[p] && d[p] <= extents[i][1];
}) ? null : "none";
});
d3.selectAll("td").style('background-color', 'red');
}
See code below, which you can find also in the following link GitHub:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg {
font: 10px sans-serif;
}
.background path {
fill: none;
stroke: #ddd;
shape-rendering: crispEdges;
}
.foreground path {
fill: none;
stroke: steelblue;
stroke-width: 2;
}
.brush .extent {
fill-opacity: .3;
stroke: #fff;
shape-rendering: crispEdges;
}
.axis line,
.axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis text {
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
cursor: move;
}
.tooltip {
background-color: rgba(220,220,220,0.5);
color: #333;
margin: 10px;
height: 25px;
padding-right: 10px;
padding-left: 10px;
padding-top: 10px;
-webkit-border-radius:10px;
-moz-border-radius:10px;
border-radius:10px;
}
td, th {
padding: 1px 4px;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// Fuente original: https://bl.ocks.org/jasondavies/1341281
var margin = {
top: 30,
right: 10,
bottom: 10,
left: 10
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal().rangePoints([0, width], 1),
y = {},
dragging = {};
var line = d3.svg.line(),
axis = d3.svg.axis().orient("left"),
background,
foreground;
var svg = d3.select("body").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 + ")");
var notas = [
{
"Nombre": "Emily",
"Matematicas": "10",
"Ciencias": "10",
"Historia": "8",
"Geografia": "8",
"Lengua": "10"
},
{
"Nombre": "Cooper",
"Matematicas": "10",
"Ciencias": "7",
"Historia": "2",
"Geografia": "8",
"Lengua": "10"
}];
var tooltip = d3.select("body")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.text("a simple tooltip")
.attr("class","tooltip");
// Extract the list of dimensions and create a scale for each.
x.domain(dimensions = d3.keys(notas[0]).filter(function(d) {
return d != "Nombre" && (y[d] = d3.scale.linear()
.domain(d3.extent(notas, function(p) {
return +p[d];
}))
.range([height, 0]));
}));
// Add grey background lines for context.
background = svg.append("g")
.attr("class", "background")
.selectAll("path")
.data(notas)
.enter().append("path")
.attr("d", path);
// Add blue foreground lines for focus.
foreground = svg.append("g")
.attr("class", "foreground")
.selectAll("path")
.data(notas)
.enter().append("path")
.attr("d", path)
.on("mouseover", function(n){
d3.select(this)
.transition().duration(100)
.style({'stroke' : '#F00'});
tooltip.text(n.Nombre);
return tooltip.style("visibility", "visible");
})
.on("mousemove", function(){
return tooltip
.style("top", (event.pageY-10)+"px")
.style("left",(event.pageX+10)+"px");
})
.on("mouseout", function(d){
d3.select(this)
.transition().duration(100)
.style({'stroke': 'steelblue' })
.style({'stroke-width' : '2'});
return tooltip.style("visibility", "hidden");
});
// Add a group element for each dimension.
var g = svg.selectAll(".dimension")
.data(dimensions)
.enter().append("g")
.attr("class", "dimension")
.attr("transform", function(d) {
return "translate(" + x(d) + ")";
})
.call(d3.behavior.drag()
.origin(function(d) {
return {
x: x(d)
};
})
.on("dragstart", function(d) {
dragging[d] = x(d);
background.attr("visibility", "hidden");
})
.on("drag", function(d) {
dragging[d] = Math.min(width, Math.max(0, d3.event.x));
foreground.attr("d", path);
dimensions.sort(function(a, b) {
return position(a) - position(b);
});
x.domain(dimensions);
g.attr("transform", function(d) {
return "translate(" + position(d) + ")";
})
})
.on("dragend", function(d) {
delete dragging[d];
transition(d3.select(this)).attr("transform", "translate(" + x(d) + ")");
transition(foreground).attr("d", path);
background
.attr("d", path)
.transition()
.delay(500)
.duration(0)
.attr("visibility", null);
}));
// Add an axis and title.
g.append("g")
.attr("class", "axis")
.each(function(d) {
d3.select(this).call(axis.scale(y[d]));
})
.append("text")
.style("text-anchor", "middle")
.attr("y", -9)
.text(function(d) {
return d;
});
// Add and store a brush for each axis.
g.append("g")
.attr("class", "brush")
.each(function(d) {
d3.select(this).call(y[d].brush = d3.svg.brush().y(y[d]).on("brushstart", brushstart).on("brush", brush));
})
.selectAll("rect")
.attr("x", -8)
.attr("width", 16);
function position(d) {
var v = dragging[d];
return v == null ? x(d) : v;
}
function transition(g) {
return g.transition().duration(500);
}
// Returns the path for a given data point.
function path(d) {
return line(dimensions.map(function(p) {
return [position(p), y[p](d[p])];
}));
}
function brushstart() {
d3.event.sourceEvent.stopPropagation();
}
// Handles a brush event, toggling the display of foreground lines.
function brush() {
var actives = dimensions.filter(function(p) {
return !y[p].brush.empty();
}),
extents = actives.map(function(p) {
return y[p].brush.extent();
});
foreground.style("display", function(d) {
return actives.every(function(p, i) {
return extents[i][0] <= d[p] && d[p] <= extents[i][1];
}) ? null : "none";
});
}
// The table generation function
function tabulate(data, columns) {
var table = d3.select("body").append("table")
.attr("style", "margin-left: 250px"),
thead = table.append("thead"),
tbody = table.append("tbody");
// append the header row
thead.append("tr")
.selectAll("th")
.data(columns)
.enter()
.append("th")
.text(function(column) { return column; });
// create a row for each object in the data
var rows = tbody.selectAll("tr")
.data(notas)
.enter()
.append("tr");
// create a cell in each row for each column
var cells = rows.selectAll("td")
.data(function(row) {
return columns.map(function(column) {
return {column: column, value: row[column]};
});
})
.enter()
.append("td")
.attr("style", "font-family: Courier") // sets the font style
.html(function(d) { return d.value; });
return table;
}
// render the table
var peopleTable = tabulate(notas, ["Nombre", "Matematicas","Ciencias", "Historia","Geografia", "Lengua"]);
</script>
</body>
</html>
I've added also the code in JSFiddle but it doesn't display anything. I'm not sure what I'm doing wrong. I'm not familiar with JSFiddle, so I might be doing something wrong.
I've tried also to use divgrid (DivGrid) and with the following code I can see how when I attempt to brush the table gets updated. The problem is that it doesn't get updated with the data I need. I just have it to show 5 records.
<script src="divgrid.js"></script>
...
// Handles a brush event, toggling the display of foreground lines.
function brush() {
var actives = dimensions.filter(function(p) {
return !y[p].brush.empty();
}),
extents = actives.map(function(p) {
return y[p].brush.extent();
});
foreground.style("display", function(d) {
return actives.every(function(p, i) {
return extents[i][0] <= d[p] && d[p] <= extents[i][1];
}) ? null : "none";
});
//d3.selectAll("td").style('background-color', 'red');
var grid = d3.divgrid();
d3.select('body')
.datum(notas.slice(0,5))
.call(grid);
}
The problems I have (I think) is that (1) the update of the table is not really in the right place as it is only updating when starting to brush, not when brushing over the lines and (2) I need to find a way to pickup the data from the lines that are selected. I've added to code in github as a reference (GitHub)
Any ideas?
Thanks

Tooltips/Markers not appearing after transition

I'm creating a population pyramid with D3.js using with a template taken from the visualisation group at Stanford. I can't figure out why initially I get tooltips and markers on the graph but once I transition to a different years these are no longer available. Anyone know how I could fix this? Apologies for the length of the code, I'm pretty new to D3/JavaScript so just figured I'd put everything up rather than trying to pinpoint where the error is.
Thanks
Northern Ireland Teacher Population Pyramid
body {
font: 12px sans-serif;
margin: 0;
padding: 5px;
color: #888;
}
h1 {
padding-left: 10px;
margin-bottom: 2px;
color: #333;
}
.source {
padding-left: 10px;
}
.source a, .source a:hover {
color: #888;
}
.label {
position: absolute;
top: 60px;
left: 15px;
font-size: 48px;
font-weight: bold;
color: #dedede;
}
.break {
border-bottom: solid 1px #dedede;
margin: 10px 15px 2px 15px;
width: 545px;
}
.years, .controls {
padding-top: 10px;
padding-left: 15;
width: 540;
text-align: center;
font-size: 12px;
}
.years span, .controls span {
padding-left: 2px;
padding-right: 2px;
}
.years .title {
font-size: 13px;
font-variant: small-caps;
letter-spacing: 1;
}
.years a, .controls a {
color: #888;
text-decoration: none;
}
.years a:hover, .controls a:hover {
color: #000;
text-decoration: underline;
}
.years a.y1890 {
color: #bbb;
}
.years a.active {
color: #000;
}
.controls a {
font-variant: small-caps;
letter-spacing: 1;
text-decoration: none;
}
svg {
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<h1>Northern Ireland Teachers Population Pyramid, 2010-2014</h1>
<div class="source">
Source: Teachers' Payroll and Pensions Administration System.
</div><script type="text/javascript">
document.onkeydown = function(event) {
var y = year;
switch (event.keyCode) {
case 37: // left arrow
y = Math.max(2010, year-1);
break;
case 39: // right arrow
y = Math.min(2014, year+1);
break;
case 32: // space bar
toggle();
return;
}
if (y != year) goto(y);
};
function isYear(d) { return d.year == year; }
function linkClass(y) { return "y"+y.toFixed(0) + (y==year?" active":""); }
function tooltipText(d) {
return d3.format(",")(d.people)
+ " " + (d.sex==1?"men":"women")
+ " aged " + (d.age==60?"60+":d.age+"-"+(d.age+4))
+ " in " + d.year;
}
function barWidth(d) { return x1(d.people); }
function goto(yr, dur) {
dur = dur || 300;
var old = year;
year = yr;
label.text(year);
div.selectAll("span.link a")
.attr("class", linkClass);
var fb = vis.selectAll("rect.female").data(fdat.filter(isYear), {
nodeKey: function(node) { return node.getAttribute("id"); },
dataKey: function(data) { return "f"+(data.year - data.age); }
});
fb.enter("svg:rect")
.attr("id", function(d) { return "f"+(d.year - d.age); })
.attr("class", "female")
.attr("fill", "pink")
.attr("transform", lTransform)
.attr("width", function(d) { return x1(d.people); })
.attr("y", yr>old ? 20 : -20)
.attr("height", y.rangeBand())
.attr("opacity", 0.0001)
.transition()
.duration(dur)
.attr("y", 0)
.attr("opacity", 1);
fb.exit().transition()
.duration(dur)
.attr("y", yr>old ? -20 : 30)
.attr("opacity", 0.0001)
.each("end", function() { d3.select(this).remove(); });
fb.transition()
.duration(dur)
.attr("transform", lTransform)
.attr("y", 0)
.attr("width", function(d) { return x1(d.people); })
.attr("opacity", 1);
fb.selectAll("title").text(tooltipText);
var mb = vis.selectAll("rect.male").data(mdat.filter(isYear), {
nodeKey: function(node) { return node.getAttribute("id"); },
dataKey: function(data) { return "m"+(data.year - data.age); }
});
mb.enter("svg:rect")
.attr("id", function(d) { return "m"+(d.year - d.age); })
.attr("class", "male")
.attr("fill", "steelblue")
.attr("transform", rTransform)
.attr("width", function(d) { return x1(d.people); })
.attr("y", yr>old ? 20 : -20)
.attr("height", y.rangeBand())
.attr("opacity", 0.0001)
.transition()
.duration(dur)
.attr("y", 0)
.attr("opacity", 1);
mb.exit().transition()
.duration(dur)
.attr("y", yr>old ? -20 : 30)
.attr("opacity",0.0001)
.each("end", function() { d3.select(this).remove(); });
mb.transition()
.duration(dur)
.attr("transform", rTransform)
.attr("y", 0)
.attr("width", function(d) { return x1(d.people); })
.attr("opacity", 1);
mb.select("title").text(tooltipText);
}
var timer = undefined;
function stop() {
clearInterval(timer);
timer = undefined;
ctrls.select("a.toggle").text("play");
}
function toggle() {
if (!timer) {
play();
} else {
stop();
}
}
function play(rev) {
rev = rev || false;
if (timer) { stop(); }
ctrls.select("a.toggle").text("stop");
var advance = function() {
var y = year + (rev?-1:1)*1;
if (y < 2010 || y > 2013) {
// stop at end points
stop();
return;
} else {
// else advance
goto(y, 800);
}
};
advance();
timer = setInterval(advance, 850);
}
var data = census,
maxp = data.reduce(function(a,b) { return Math.max(a,b.people); }, 0),
mdat = data.filter(function(d) { return d.sex==1; })
.sort(function(a,b) { return b.age - a.age; }),
fdat = data.filter(function(d) { return d.sex==2; })
.sort(function(a,b) { return b.age - a.age; });
var w = 250,
h = 9 * 40,
bins = d3.range(9),
year = 2010,
y = d3.scale.ordinal().domain(bins).rangeBands([0, h], 0.25),
x1 = d3.scale.linear().domain([0, maxp]).range([0, w]),
x2 = d3.scale.linear().domain([0, maxp]).range([w, 0]),
rf = "javascript:return false;";
var label = d3.select("body")
.append("div")
.attr("class", "label")
.text(year.toFixed(0));
var vis = d3.select("body")
.append("svg:svg")
.attr("width", 2*w + 40)
.attr("height", h + 40)
.append("svg:g")
.attr("transform", "translate(20,15)");
// pyramid bar chart
vis.append("svg:g")
.selectAll("text.ages")
.data(bins)
.enter("svg:text")
.filter(function(d) { return d%2==0; })
.attr("class", "ages")
.attr("x", w+15)
.attr("y", function(d) { return y(d) + y.rangeBand() + 7; })
.attr("fill", "#888")
.attr("text-anchor", "middle")
.attr("font-size", "12px")
.text(function(d) { return (60-d*5).toFixed(0); });
var rTransform = function(d,i) {
return "translate("+(w+30)+","+y(i).toFixed(2)+")";
}
var lTransform = function(d,i) {
return "translate("+x2(d.people).toFixed(2)+","+y(i).toFixed(2)+")";
}
var lEnter = function(d,i) {
return "translate("+w+","+y(i).toFixed(2)+")";
}
var mbars = vis.selectAll("rect.male")
.data(mdat.filter(isYear))
.enter("svg:rect")
.attr("id", function(d) { return "m"+(d.year - d.age); })
.attr("class", "male")
.attr("fill", "steelblue")
.attr("transform", rTransform)
.attr("width", barWidth)
.attr("height", y.rangeBand())
.attr("y", 0)
.attr("opacity", 1);
mbars.append("svg:title").text(tooltipText);
var fbars = vis.selectAll("rect.female")
.data(fdat.filter(isYear))
.enter("svg:rect")
.attr("id", function(d) { return "f"+(d.year - d.age); })
.attr("class", "female")
.attr("fill", "pink")
.attr("opacity", 1)
.attr("transform", lTransform)
.attr("width", barWidth)
.attr("height", y.rangeBand())
.attr("y", 0)
.attr("opacity", 1);
fbars.append("svg:title").text(tooltipText);
// animated intro for bars
mbars.attr("width", 0)
.transition()
.duration(100)
.delay(function(d,i) { return 30*i; })
.attr("width", barWidth);
fbars.attr("width", 0)
.attr("transform", lEnter)
.transition()
.duration(100)
.delay(function(d, i) { return 30*i; })
.attr("width", barWidth)
.attr("transform", lTransform);
// age label
vis.append("svg:text")
.attr("x", w+15)
.attr("y", h+8)
.attr("dy", ".71em")
.attr("fill", "#888")
.attr("text-anchor", "middle")
.attr("font-size", "13px")
.attr("font-variant", "small-caps")
.attr("letter-spacing", 1)
.text("age");
// gridlines and labels for right pyramid
var rules1 = vis.selectAll("g.rule1")
.data(x1.ticks(5))
.enter("svg:g")
.filter(function(d) { return d > 0; })
.attr("class", "rule1")
.attr("transform", function(d) { return "translate("+(w+30+x1(d))+",0)";});
rules1.append("svg:line")
.attr("y1", h - 2)
.attr("y2", h + 4)
.attr("stroke", "#bbb");
rules1.append("svg:line")
.attr("y1", 0)
.attr("y2", h)
.attr("stroke", "white")
.attr("stroke-opacity", .3);
rules1.append("svg:text")
.attr("y", h + 9)
.attr("dy", ".71em")
.attr("text-anchor", "middle")
.attr("font-size", "12px")
.attr("fill", "#bbb")
.text(function(d) { return (d).toFixed(0); });
// gridlines and labels for left pyramid
var rules2 = vis.selectAll("g.rule2")
.data(x2.ticks(5))
.enter("svg:g")
.filter(function(d) { return d > 0; })
.attr("class", "rule2")
.attr("transform", function(d) { return "translate("+(x2(d))+",0)";});
rules2.append("svg:line")
.attr("y1", h - 2)
.attr("y2", h + 4)
.attr("stroke", "#bbb");
rules2.append("svg:line")
.attr("y1", 0)
.attr("y2", h)
.attr("stroke", "white")
.attr("stroke-opacity", .3);
rules2.append("svg:text")
.attr("y", h + 9)
.attr("dy", ".71em")
.attr("text-anchor", "middle")
.attr("font-size", "12px")
.attr("fill", "#bbb")
.text(function(d) { return (d).toFixed(0); });
d3.select("body")
.append("div")
.attr("class", "break");
var div = d3.select("body")
.append("div")
.attr("class", "years");
div.append("span")
.attr("class", "title")
.text("year");
var ctrls = d3.select("body")
.append("div")
.attr("class", "controls");
ctrls.append("span").append("a")
.attr("class", "control back")
.attr("href", "javascript:play(true);")
.text("<< ");
ctrls.append("span").append("a")
.attr("class", "control toggle")
.attr("href", "javascript:toggle();")
.text("play");
ctrls.append("span").append("a")
.attr("class", "control forward")
.attr("href", "javascript:play();")
.text(" >>");
div.selectAll("span.link")
.data(d3.range(2010, 2014, 1))
.enter("span")
.attr("class", "link")
.append("a")
.attr("class", linkClass)
.attr("href", function(d) { return d==1890?null:"javascript:goto("+d+");"; })
.text(function(d) { return d.toFixed(0); });
</script>
The trick is to replace
var fb = vis.selectAll("rect.female").data(fdat.filter(isYear), {
nodeKey: function(node) { return node.getAttribute("id"); },
dataKey: function(data) { return "f"+(data.year - data.age); }
});
with
var fb = vis.selectAll("rect.female").data(fdat.filter(isYear));
(And the same for variable mb).
SVG elements are rendered in the order that you append them to the DOM. At the beginning, the grid lines are on top of the bars, but after you redraw the bars, they are on top of the grid lines. You need to append the lines after the bars. If you change the lines from white to another color you can see where they are -- that should help with debugging.

Resources