D3.js removing data on rect click - d3.js

With a lot of help I've been able to click on rectangles to remove them.
But how can I also remove the data? Currently there is some strange behaviour, such as previously deleted rectangles reappearing and creation of a rectangle being in a different position to the click. I think all these are caused by the fact that the data is not being deleted.
I thought perhaps .exit() might work, but it doesn't.
Here's a working snippet.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.active {
stroke: #000;
stroke-width: 2px;
}
</style>
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
radius = 32;
var data = [{
x: 100,
y: 200
},
{
x: 200,
y: 300
},
{
x: 300,
y: 200
},
{
x: 400,
y: 300
}
];
var xScale = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.x_pos
})]).range([0, width]);
svg.append("rect")
.attr("x", 100)
.attr("y", 250)
.attr("rx", 2)
.attr("ry", 2)
.attr("height", 6)
.attr("width", 800)
.style("fill", "grey");
svg.append("rect")
.attr("x", 102)
.attr("y", 252)
.attr("rx", 2)
.attr("ry", 2)
.attr("height", 2)
.attr("width", 796)
.style("fill", "black");
svg.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d) {
return d.x;
})
.attr("y", 200)
.attr("height", 100)
.attr("width", 15)
.style("fill", "lightblue")
.attr('id', function(d, i) {
return 'rect_' + i;
})
.attr("rx", 6)
.attr("ry", 6)
.attr("stroke-width", 2)
.attr("stroke", "black")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("click", removeElement);
svg.on("click", function() {
var coords = d3.mouse(this);
var newData = {
x: d3.event.x,
y: d3.event.y
};
data.push(newData);
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) {
return d.x;
})
.attr("y", 200)
.attr("height", 100)
.attr("width", 15)
.style("fill", "steelblue")
.attr('id', function(d, i) {
return 'circle_' + i;
})
.attr("rx", 6)
.attr("ry", 6)
.attr("stroke-width", 2)
.attr("stroke", "black")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("click", removeElement);
})
function dragstarted(d) {
d3.select(this).raise().classed("active", true);
}
function dragged(d) {
d3.select(this)
.attr("x", d.x = d3.event.x)
// .attr("cy", d.y = d3.event.y);
}
function dragended(d) {
d3.select(this)
.classed("active", false);
}
function removeElement(d) {
d3.event.stopPropagation();
d3.select(this)
.remove();
}
</script>
This question is a follow on from here.

First of all, your data array has 4 elements, but you are appending only 2 rectangles. That happens because you are selecting previously existent rectangles in that SVG. Therefore, instead of:
svg.selectAll("rect")
.data(data)
.enter().append("rect")
It should be:
svg.selectAll(null)
.data(data)
.enter().append("rect")
Back to your question:
D3 has methods to manipulate elements based on data, but not to manipulate data based on elements.
Thus, you have to change the data array yourself. For instance, inside removeElement:
function removeElement(d) {
data = data.filter(function(e){
return e != d;
});
};
Here is your code with those changes:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.active {
stroke: #000;
stroke-width: 2px;
}
</style>
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
radius = 32;
var data = [{
x: 100,
y: 200
},
{
x: 200,
y: 300
},
{
x: 300,
y: 200
},
{
x: 400,
y: 300
}
];
var xScale = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.x_pos
})]).range([0, width]);
svg.append("rect")
.attr("x", 100)
.attr("y", 250)
.attr("rx", 2)
.attr("ry", 2)
.attr("height", 6)
.attr("width", 800)
.style("fill", "grey");
svg.append("rect")
.attr("x", 102)
.attr("y", 252)
.attr("rx", 2)
.attr("ry", 2)
.attr("height", 2)
.attr("width", 796)
.style("fill", "black");
svg.selectAll(null)
.data(data)
.enter().append("rect")
.attr("x", function(d) {
return d.x;
})
.attr("y", 200)
.attr("height", 100)
.attr("width", 15)
.style("fill", "lightblue")
.attr('id', function(d, i) {
return 'rect_' + i;
})
.attr("rx", 6)
.attr("ry", 6)
.attr("stroke-width", 2)
.attr("stroke", "black")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("click", removeElement);
svg.on("click", function() {
var coords = d3.mouse(this);
var newData = {
x: d3.event.x,
y: d3.event.y
};
data.push(newData);
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) {
return d.x;
})
.attr("y", 200)
.attr("height", 100)
.attr("width", 15)
.style("fill", "steelblue")
.attr('id', function(d, i) {
return 'circle_' + i;
})
.attr("rx", 6)
.attr("ry", 6)
.attr("stroke-width", 2)
.attr("stroke", "black")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("click", removeElement);
})
function dragstarted(d) {
d3.select(this).raise().classed("active", true);
}
function dragged(d) {
d3.select(this)
.attr("x", d.x = d3.event.x)
// .attr("cy", d.y = d3.event.y);
}
function dragended(d) {
d3.select(this)
.classed("active", false);
}
function removeElement(d) {
d3.event.stopPropagation();
data = data.filter(function(e){
return e != d;
});
d3.select(this)
.remove();
}
</script>

Related

d3.js - add text above bar chart not show [duplicate]

This question already has answers here:
D3 Appending Text to a SVG Rectangle
(2 answers)
SVG: text inside rect
(5 answers)
Closed 1 year ago.
After study this excelent d3.js tutorial, I like to add the bar value on top of each bar! below example code the text not show up!
XYZ.csv
year,value
2011,45
2012,47
2013,52
2014,70
2015,75
2016,78
html file:
<!doctype html>
<html>
<head>
<style>
.bar {
fill: steelblue;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<svg width="600" height="500"></svg>
<script>
var svg = d3.select("svg"),
margin = 200,
width = svg.attr("width") - margin,
height = svg.attr("height") - margin
var xScale = d3.scaleBand().range([0, width]).padding(0.4),
yScale = d3.scaleLinear().range([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + 100 + "," + 100 + ")");
d3.csv("XYZ.csv", function(error, data) {
if (error) {
throw error;
}
xScale.domain(data.map(function(d) { return d.year; }));
yScale.domain([0, d3.max(data, function(d) { return d.value; })]);
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
g.append("g")
.call(d3.axisLeft(yScale).tickFormat(function(d){
return "$" + d;
}).ticks(10));
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return xScale(d.year); })
.attr("y", function(d) { return yScale(d.value); })
.attr("width", xScale.bandwidth())
.attr("height", function(d) { return height - yScale(d.value); })
.append("text")
.attr("x", function(d) { return xScale(d.year); })
.attr("y", function(d) { return yScale(d.value); })
.attr("text", function(d) { return d.value; })
});
</script>
</body>
</html>
Below code is added for text on top of each bar:
.append("text")
.attr("x", function(d) { return xScale(d.year); })
.attr("y", function(d) { return yScale(d.value); })
.attr("text", function(d) { return d.value; })
It's impossible to append a <text> to a <rect>. Just add the texts separately:
g.selectAll(".bar-title")
.data(data)
.enter()
.append("text")
.classed('bar-title', true)
.attr('text-anchor', 'middle')
.attr("x", d => xScale(d.year) + xScale.bandwidth()/2)
.attr("y", d => yScale(d.value))
.text(d => `$${d.value}`);
... or, you can append a <g> element on enter() and then append <text> and <rect> under <g>.
const data = [
{year: 2011, value: 45},
{year: 2012, value: 47},
{year: 2013, value: 52},
{year: 2014, value: 70},
{year: 2015, value: 75},
{year: 2016, value: 78}
];
var svg = d3.select("svg"),
margin = 200,
width = svg.attr("width") - margin,
height = svg.attr("height") - margin
var xScale = d3.scaleBand().range([0, width]).padding(0.4),
yScale = d3.scaleLinear().range([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + 100 + "," + 100 + ")");
xScale.domain(data.map(function(d) { return d.year; }));
yScale.domain([0, d3.max(data, function(d) { return d.value; })]);
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
g.append("g")
.call(d3.axisLeft(yScale).tickFormat(function(d){
return "$" + d;
}).ticks(10));
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return xScale(d.year); })
.attr("y", function(d) { return yScale(d.value); })
.attr("width", xScale.bandwidth())
.attr("height", function(d) { return height - yScale(d.value); })
g.selectAll(".bar-title")
.data(data)
.enter()
.append("text")
.classed('bar-title', true)
.attr('text-anchor', 'middle')
.attr("x", d => xScale(d.year) + xScale.bandwidth()/2)
.attr("y", d => yScale(d.value) - 5)
.text(d => `$${d.value}`);
.bar {
fill: steelblue;
}
.bar-title {
font-family: 'Ubuntu';
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<svg width="600" height="500"></svg>

d3.js Grouped Bar Chart: text is not showing on bars

I am trying to add text on each bar in grouped bar chart. but it is not showing & no error is there on console.
I am using the sample code from https://bl.ocks.org/bricedev/0d95074b6d83a77dc3ad
I tried below code:
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
//display: none; //to show x axis
}
</style>
</head>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 40, right: 60, bottom: 40, left: 40},
width = 560 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x0)
.tickSize(0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var color = d3.scale.ordinal()
.range(["#ca0020","#f4a582","#d5d5d5","#92c5de","#0571b0"]);
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 + ")");
d3.json("data.json", function(error, data) {
var categoriesNames = data.map(function(d) { return d.categorie; });
var rateNames = data[0].values.map(function(d) { return d.rate; });
x0.domain(categoriesNames);
x1.domain(rateNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(categorie) { return d3.max(categorie.values, function(d) { return d.value; }); })]);
//Graph Title
svg.append("text")
.attr("transform", "translate(100,0)")
.attr("x", 100)
.attr("y", -20)
.attr("font-size", "24px")
.text("Grouped Bar Chart");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.style('font-weight','bold')
.attr("transform", "translate(" + width + ",10)")
.text("Months");
svg.append("g")
.attr("class", "y axis")
.style('opacity','0')
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.style('font-weight','bold')
.text("Incidents");
svg.select('.y').transition().duration(500).delay(1300).style('opacity','1');
var slice = svg.selectAll(".slice")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform",function(d) { return "translate(" + x0(d.categorie) + ",0)"; });
//draw bars
slice.selectAll("rect")
.data(function(d) { return d.values; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.rate); })
.style("fill", function(d) { return color(d.rate) })
.attr("y", function(d) { return y(0); })
.attr("height", function(d) { return height - y(0); })
.on("mouseover", function(d) {
d3.select(this).style("fill", d3.rgb(color(d.rate)).darker(2));
})
.on("mouseout", function(d) {
d3.select(this).style("fill", color(d.rate));
});
slice.selectAll("text")
.data(function(d) {return d.values ;})
.enter()
.append("text")
.attr("x", function(d) { return x1(d.rate); })
.attr("y", function(d) { return ( y(0) ) ; })
.text(function(d) {
return (d.value); // Value of the text
});
slice.selectAll("rect")
.transition()
.delay(function (d) {return Math.random()*1000;})
.duration(1000)
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
//Legend
var legend = svg.selectAll(".legend")
.data(data[0].values.map(function(d) { return d.rate; }).reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d,i) { return "translate(60," + i * 20 + ")"; })
.style("opacity","0");
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d) { return color(d); });
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {return d; });
legend.transition().duration(500).delay(function(d,i){ return 1300 + 100 * i; }).style("opacity","1");
});
</script>
</body>
</html>
But the output is output after adding block
It is showing object object in output.
Please help.
output of code is here
You're appending to slice, which is a selection of all the bar groups, not the bars themselves. Try something like this to get started:
slice.selectAll("text")
.data(function(d) { return d.values; })
.enter().append("text")
.attr("x", function(d) { return x1(d.rate); })
.attr("y", function(d) { return y(d.value); })
.text(function(d) { return d.value })

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>

Why is my D3.js tooltip not working

I tried to use the following code to show a d3.js tool-tip effect but I can't get it to work, why is the number not showing up:
<style>
svg {
width: 100%;
height: 100%;
position: center;
}
#hist_sexage {
background-color: lightgrey;
}
.rect:hover {
fill: yellow;
}
.tooltip {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
position: absolute;
display: none;
width: auto;
height: auto;
background: none repeat scroll 0 0 red;
border: 0 none;
border-radius: 8px 8px 8px 8px;
box-shadow: -3px 3px 15px #888888;
color: blue;
font: 12px sans-serif;
padding: 5px;
text-align: center;
}
</style>
<svg id="hist_sexage" width="950" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var margin = {top: 30, right: 30, bottom: 40, left: 30};
var width = document.getElementById("hist_sexage").getBoundingClientRect().width-50;
var height = 400;
var g = d3.select("#hist_sexage")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.2);
var x1 = d3.scaleBand()
.padding(0.1);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range(["cornflowerblue", "orangered"]);
d3.csv("/blog/data/age_by_gender.csv",function(d, i, columns) {
for (var i = 1, n = columns.length; i < n; ++i) d[columns[i]] = +d[columns[i]];
return d;
},
function(error, data) {
if (error) throw error;
var keys = data.columns.slice(1);
x0.domain(data.map(function(d) { return d.Age_Group; }));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(d) { return d3.max(keys, function(key) { return d[key]; }); })]).nice();
g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + x0(d.Age_Group) + ",0)"; })
.selectAll("rect")
.data(function(d) { return keys.map(function(key) { return {key: key, value: d[key]}; }); })
.enter().append("rect").attr("class", "rect")
.attr("x", function(d) { return x1(d.key); })
.attr("y", function(d) { return y(d.value); })
.attr("width", x1.bandwidth())
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", function(d) { return z(d.key); })
.on("mouseover", function() { tooltip.style("display", null); })
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 5;
var yPosition = d3.mouse(this)[1] - 5;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d.value);
console.log(d.value)
})
.on("mouseout", function() { tooltip.style("display", "none"); });
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x0))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.1em")
.attr("transform", "rotate(-45)" );;
g.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y).ticks(null, ".0%"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
.text("Percentage");
var legend = g.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "end")
.selectAll("g")
.data(keys.slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 19)
.attr("width", 19)
.attr("height", 19)
.attr("fill", z);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(function(d) { return d; });
});
// Prep the tooltip bits, initial display is hidden
var tooltip = d3.select("#hist_sexage").append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("g:rect")
.attr("width", 60)
.attr("height", 20)
.attr("fill", "red")
.style("opacity", 0.5);
tooltip.append("g:text")
.attr("x", 30)
.attr("y", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
</script>
I am expecting to see it, if I move over the bar, it should show the number alongside with my cursor but it does not. Can you please help me troubleshoot this issue?
Thanks,
Well, you can't see the tooltip for a simple reason. You set its display to none...
.style("display", "none");
... but you forgot to change its style on mousemove. Therefore, its children will never be rendered.
Here is a working demo with fake data, changing the style in the mousemove:
var svg = d3.select("#hist_sexage");
var data = [30, 250, 120, 150, 90];
var rects = svg.selectAll("jamesWatson")
.data(data)
.enter()
.append("rect")
.attr("x", 0)
.attr("y", function(d, i) {
return i * 20
})
.attr("height", 15)
.attr("width", function(d) {
return d
})
.attr("fill", "teal")
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] + 10;
var yPosition = d3.mouse(this)[1] - 5;
tooltip.style("display", "inline")
.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d);
})
.on("mouseout", function() {
tooltip.style("display", "none");
});
var tooltip = d3.select("#hist_sexage").append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("g:rect")
.attr("width", 60)
.attr("height", 20)
.attr("fill", "red")
.style("opacity", 0.5);
tooltip.append("g:text")
.attr("x", 30)
.attr("y", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg id="hist_sexage"></svg>

d3 version 4 issue with rectangle and text

This method works in v3 to append rectangles with text inside but fails in v4. I getting an error message "read property 'querySelectorAll'" but it may not be relevant to this piece of code. Any suggestions would be appreciated.
group = vis.selectAll(".rectangle")
.data(data);
gEnter = group.enter()
.append("g")
.attr("class", "rectangle")
.attr("fill", function (d) { return d.colour; });
gEnter.append("rect")
.attr("class", "rectband");
group.selectAll(".rectband")
.attr("width", 18)
.attr("height", 18)
.style("opacity", .5)
.style("stroke", "black")
.style("cursor", "move");
svgEnter = group.enter()
.append("svg")
.attr("height", 18)
.attr("class", "interval")
.attr("width", 10)
.attr("x", 20)
.attr("y", 20);
svgEnter.append("text")
.attr("class", "intervalLabel")
.attr("x", 6)
.attr("y", 14)
.style("pointer-events", "none")
.text(function (d) { return (d.name); });
Your code kinda works but your text elements are on top of each other and in an svg container that's not wide enough to show their content.
Anyway, if you make your svg wider, the text shows just fine:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>
var vis = d3.select('body')
.append('svg')
.attr('width', 400)
.attr('height', 400);
group = vis.selectAll(".rectangle")
.data([
{
colour: 'red',
name: 'one'
},
{
colour: 'green',
name: 'two'
}
]);
gEnter = group.enter()
.append("g")
.attr("class", "rectangle")
.attr("fill", function(d) {
return d.colour;
});
gEnter.append("rect")
.attr("class", "rectband")
.merge(gEnter)
.attr("width", 18)
.attr("height", 18)
.style("opacity", .5)
.style("stroke", "black")
.style("cursor", "move");
svgEnter = group.enter()
.append("svg")
.attr("height", 18)
.attr("class", "interval")
.attr("width", 30)
.attr("x", 20)
.attr("y", 20);
svgEnter.append("text")
.attr("class", "intervalLabel")
.attr("x", 6)
.attr("y", 14)
.style("pointer-events", "none")
.text(function(d) {
return (d.name);
});
</script>
</body>
</html>

Resources