Hide and show svg element with transition - d3.js

How can I hide and show an svg element using transition?
I try this code:
<div id="bubble"></div>
<div id="buttonHide"><button>Hide</button></div>
<div id="buttonShow"><button>Show</button></div>
d3.select("#bubble")
.append("svg")
.append("g")
.append("circle")
.attr("class", "bubble")
.attr("transform", "translate(100, 100)")
.attr("r", 50)
.attr("fill", "black");
d3.select("#buttonHide").on("click", function() {
d3.select(".bubble").transition().attr('visibility', 'hidden').duration(1000);
});
d3.select("#buttonShow").on("click", function() {
d3.select(".bubble").transition().attr('visibility', 'visible').duration(1000);
});
d3.select("#bubble")
.append("svg")
.append("g")
.append("circle")
.attr("class", "bubble")
.attr("transform", "translate(100, 100)")
.attr("r", 50)
.attr("fill", "black");
d3.select("#buttonHide").on("click", function() {
d3.select(".bubble").transition().duration(1000).attr('visibility', 'hidden');
});
d3.select("#buttonShow").on("click", function() {
d3.select(".bubble").transition().duration(1000).attr('visibility', 'visible');
});
<html lang='en'>
<head>
<meta charset='utf-8'>
<script src='https://d3js.org/d3.v5.js' charset='utf-8'></script>
</head>
<body>
<div id="bubble"></div>
<div id="buttonHide"><button>Hide</button></div>
<div id="buttonShow"><button>Show</button></div>
</body>
</html>
But transition doesn't work.

You could play with the opacity style instead of switching the visibility attribute:
d3.select("#bubble")
.append("svg")
.append("g")
.append("circle")
.attr("class", "bubble")
.attr("transform", "translate(100, 100)")
.attr("r", 50)
.attr("fill", "black")
.style("opacity", 1);
d3.select("#buttonHide").on("click", function() {
d3.select(".bubble").transition().duration(1000).style("opacity", 0);
});
d3.select("#buttonShow").on("click", function() {
d3.select(".bubble").transition().duration(1000).style("opacity", 1);
});
<html lang='en'>
<head>
<meta charset='utf-8'>
<script src='https://d3js.org/d3.v5.js' charset='utf-8'></script>
</head>
<body>
<div id="bubble"></div>
<div id="buttonHide"><button>Hide</button></div>
<div id="buttonShow"><button>Show</button></div>
</body>
</html>
This way, the transition will be applied on the opacity, which will transition from 0 to 1 (unhide) or from 1 to 0 (hide).
To set the opacity:
d3.select(".bubble").transition().duration(1000).style("opacity", 1);
The transition is in fact expecting a change which can be interpolated between two values. This is the case for the opacity (all the way between 0 and 1), whereas the visibility attribute is a simple switch (on/off).

Related

Why can't I get a d3.js map to render using topojson?

I'm having some difficulty getting d3 to render a geoAlbersUsa projection from topoJson data. I'm showing a blank screen, but no errors returned. The geoJson data seems to be coming through fine, but it's not rendering the path for some reason. Any help would be greatly appreciated!
Here's the relevant code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script src="https://d3js.org/d3.v6.min.js"></script>
<script src="https://unpkg.com/topojson-client#3"></script>
</head>
<body>
<script>
const width = 1000;
const height = 600;
const projection = d3.geoAlbersUsa()
.translate([width / 2, height / 2])
.scale(800);
const path = d3.geoPath(projection);
const svg = d3.select("body")
.append("svg")
.attr("height", height)
.attr("width", width)
.style("display", "block")
.style("margin", "auto");
d3.json("https://d3js.org/us-10m.v1.json").then(data => {
svg.selectAll(".states")
.data(topojson.feature(data, data.objects.states).features)
.attr("d", path)
.style("fill", "none")
.attr("class", "states")
.style("stroke", "black")
.style("stroke-width", "2px")
});
</script>
</body>
</html>
You need to join with an element:
.data(topojson.feature(data, data.objects.states).features)
.join("path") // here
.attr("d", path)
.style("fill", "none")
.attr("class", "states")
.style("stroke", "black")
.style("stroke-width", "2px")

D3.js - X-axis not appearing on Scatterplot

I am trying to build a scatterplot by loading data from a .csv file. My scatterplot is trying to show Poverty (x-axis) vs. Healthcare (y-axis) for each state.
Here's a snippet of the CSV file:
id state healthcare poverty
1 Alabama 13.9 19.3
2 Alaska 11.2 11.4
3 Arizona 14.4 18.2
4 Arkansas 16.3 18.9
5 California 14.8 16.4
6 Colorado 12.8 12
7 Connecticut 8.7 10.8
8 Delaware 8.7 12.5
Here's my javascript code:
var svgWidth = 960;
var svgHeight = 500;
var margin = {
top: 20,
right: 40,
bottom: 60,
left: 100
};
var width = svgWidth - margin.left - margin.right;
var height = svgHeight - margin.top - margin.bottom;
// Create an SVG wrapper, append an SVG group that will hold our chart and shift the latter by left and top margins
var svg = d3.select("#scatter")
.append("svg")
.attr("width", width)
.attr("height", height);
var chartGroup = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
// Import Data
d3.csv("data.csv").then(function(censusData) {
// Parse Data & Cast as numbers
censusData.forEach(function(data) {
data.healthcare = +data.healthcare;
data.poverty = +data.poverty;
});
// Create scale functions
var xLinearScale = d3.scaleLinear()
.domain(d3.extent(censusData, d => d.poverty))
.range([0, width]);
var yLinearScale = d3.scaleLinear()
.domain([0, d3.max(censusData, d => d.healthcare)])
.range([height, 0]);
// Create axis functions
var bottomAxis = d3.axisBottom(xLinearScale);
var leftAxis = d3.axisLeft(yLinearScale);
// Append axes to the chart
chartGroup.append("g")
.attr("transform", `translate(0, ${height})`)
.call(bottomAxis);
chartGroup.append("g")
.call(leftAxis);
// Create circles
var circlesGroup = chartGroup.selectAll("Circle")
.data(censusData)
.enter()
.append("circle")
.attr("cx", d => xLinearScale(d.poverty))
.attr("cy", d => yLinearScale(d.healthcare))
.attr("r", "15")
.attr("fill", "blue")
.attr("opacity", "0.5");
// Create axes labels
chartGroup.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left + 40)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.attr("class", "axisText")
.text("Lacks Healthcare (%)");
chartGroup.append("text")
.attr("transform", `translate(${width / 2}, ${height + margin.top + 30})`)
.attr("class", "axisText")
.text("In Poverty (%)");
});
And here's the accompanying HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>D3Times</title>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy"
crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO"
crossorigin="anonymous">
<link rel="stylesheet" href="assets/css/style.css">
<link rel="stylesheet" href="assets/css/d3Style.css">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-xs-12 col-md-12">
<h1>D3Times</h1>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-md-9">
<div id="scatter">
<!-- We append our chart here. -->
</div>
</div>
</div>
</div>
<!-- Footer-->
<div id="footer">
<p>The Coding Boot CampĀ©2016</p>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.5.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.9.1/d3-tip.js"></script>
<script type="text/javascript" src="assets/js/app.js"></script>
</body>
</html>
When I run the code, here's the scatterplot I get:
Why isn't the X-axis displaying? Can anyone help with this?
Any help is greatly appreciated! Thanks in advance.
P.S: To run the code, I have to go into my project folder and run
python -m http.server
This hosts the page at localhost:8000 in the web browser.
You're using the famous margin convention to create your dataviz:
However, for this to work, you have to set the SVG size using the total width and height...
var svgWidth = 960;
var svgHeight = 500;
...not the ones computed after removing the margins:
var width = svgWidth - margin.left - margin.right;
var height = svgHeight - margin.top - margin.bottom;
Therefore, it should be:
var svg = d3.select("#scatter")
.append("svg")
.attr("width", svgWidth)
.attr("height", svgHeight);
Here is your updated code:
var csv = `id,state,healthcare,poverty
1,Alabama,13.9,19.3
2,Alaska,15,11.2,11.4
3,Arizona,14.4,18.2
4,Arkansas,16.3,18.9
5,California,14.8,16.4
6,Colorado,12.8,12
7,Connecticut,8.7,10.8
8,Delaware,8.7,12.5`;
const censusData = d3.csvParse(csv)
var svgWidth = 960;
var svgHeight = 500;
var margin = {
top: 20,
right: 40,
bottom: 60,
left: 100
};
var width = svgWidth - margin.left - margin.right;
var height = svgHeight - margin.top - margin.bottom;
// Create an SVG wrapper, append an SVG group that will hold our chart and shift the latter by left and top margins
var svg = d3.select("#scatter")
.append("svg")
.attr("width", svgWidth)
.attr("height", svgHeight);
var chartGroup = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
// Parse Data & Cast as numbers
censusData.forEach(function(data) {
data.healthcare = +data.healthcare;
data.poverty = +data.poverty;
});
// Create scale functions
var xLinearScale = d3.scaleLinear()
.domain(d3.extent(censusData, d => d.poverty))
.range([0, width]);
var yLinearScale = d3.scaleLinear()
.domain([0, d3.max(censusData, d => d.healthcare)])
.range([height, 0]);
// Create axis functions
var bottomAxis = d3.axisBottom(xLinearScale);
var leftAxis = d3.axisLeft(yLinearScale);
// Append axes to the chart
chartGroup.append("g")
.attr("transform", `translate(0, ${height})`)
.call(bottomAxis);
chartGroup.append("g")
.call(leftAxis);
// Create circles
var circlesGroup = chartGroup.selectAll("Circle")
.data(censusData)
.enter()
.append("circle")
.attr("cx", d => xLinearScale(d.poverty))
.attr("cy", d => yLinearScale(d.healthcare))
.attr("r", "15")
.attr("fill", "blue")
.attr("opacity", "0.5");
// Create axes labels
chartGroup.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left + 40)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.attr("class", "axisText")
.text("Lacks Healthcare (%)");
chartGroup.append("text")
.attr("transform", `translate(${width / 2}, ${height + margin.top + 30})`)
.attr("class", "axisText")
.text("In Poverty (%)");
<head>
<meta charset="UTF-8">
<title>D3Times</title>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link rel="stylesheet" href="assets/css/style.css">
<link rel="stylesheet" href="assets/css/d3Style.css">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-xs-12 col-md-12">
<h1>D3Times</h1>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-md-9">
<div id="scatter">
<!-- We append our chart here. -->
</div>
</div>
</div>
</div>
<!-- Footer-->
<div id="footer">
<p>The Coding Boot CampĀ©2016</p>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.5.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.9.1/d3-tip.js"></script>
<script type="text/javascript" src="assets/js/app.js"></script>
</body>

Draw a line between points

Another trivial question. I am trying to draw a line between points, here, starting from lineData[0] to lineData[1], and so on. I am getting a very funny looking area rather than a line! Can you please help me.
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title> Icon </title>
<script type="text/javascript" src="lib/d3/d3.v3.js"></script>
</head>
<body>
<p id="drawing">
<script>
// data is not same as here, just to explain the requirement created it.
var lineData = [{"x": 55, "y": 65},
{"x": 63, "y": 57},
{"x": 157, "y": 57},
{"x": 165, "y": 65}];
var svg = d3.select("#drawing")
.append("svg")
.attr("height", 200)
.attr("width", 200)
.attr("transform", "translate(20, 20)");
var lineFunction = d3.svg.line()
.x(function (d) {
return d.x;
})
.y(function (d) {
return d.y;
})
.interpolate("linear");
svg.append("path")
.attr("d", lineFunction(lineData))
.style("stroke-width", 0.5)
.style("stroke", "rgb(6,120,155)")
.on("mouseover", function () {
d3.select(this)
.style("stroke", "orange");
})
.on("mouseout", function () {
d3.select(this)
.style("stroke", "rgb(6,120,155)");
});
</script>
</body>
</html>
Your issue is that you're drawing a <path>, and you haven't set the path's fill. By default it's black, so you're drawing an object instead. Try removing the fill after appending your <path>:
svg.append("path")
.attr("d", lineFunction(lineData))
.style("stroke-width", 0.5)
.style("stroke", "rgb(6,120,155)")
.style("fill", "none") // <------ add this line
and you get this:

How to align two svg's side by side in d3.js

I have two graphs(rectangles with text the other circles with text) to be displayed but instead of displaying one below the other,I want to display side by side i.e 2 charts displaying horizontally(one at the left of div and the other at right of div). I created 2 svg's and added the charts in to that. However when ever i change the top or bottom margin it is not working.
My code goes like:
<!DOCTYPE html>
<html xmlns:xlink="http://www.w3.org/1999/xlink">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Circle legends</title>
<script type="text/javascript" src="../../js/d3/d3.v3.js"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js?1.27.2"></script>
<link rel="stylesheet" href="../../css/style.css" />
<style type="text/css">
</style>
</head>
<body>
<div id="chart"></div>
<script type="text/javascript">
var width=960,height=500;
var margin = {top: 29.5, right: 29.5, bottom: 29.5, left: 59.5};
radiusScale = d3.scale.sqrt().domain([1, 100]).range([10, 39]),
colorScale = d3.scale.category10();
//Create the SVG for legends.
var svglegend = d3.select("#chart").append("svg").attr("id","svglegend")
.attr("width", width-100)
.attr("height", height -100)
.append("g")
.attr("transform", "translate(" + margin.left + "," + (margin.top +30) + ")");
//alert("Non-empty");
d3.json("SbuLegendData.json", function(data) {
jsondata = data;
rectangle= svglegend.selectAll("rect").data(data).enter().append("rect");
var RectangleAttrb = rectangle.attr("x", function (d) { return d.x_axis; })
.attr("y", function (d) { return d.y_axis; })
.attr("width",function(d) { return d.width; } )
.attr("height",function(d) { return d.height; })
.style("fill", function(d) { return d.color; });
var textparam = svglegend.selectAll("text").data(data).enter().append("text");
var text = textparam .attr("x", function (d) { return d.x_axis + d.width +10; })
.attr("y", function (d) { return d.y_axis + d.height-5; })
.attr("width",30 )
.attr("height",20)
.text(function(d) { return d.text; });
});
// Create the SVG container and set the origin.
var svg = d3.select("#chart").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 i =0;
while (i<=50)
{
console.log("i value is " + i + " value of scale " +i+ " is " + radiusScale(i));
var g = svg.append("g");
g.append("circle")
.attr("id","circle" + i)
.attr("cx", i*12 )
.attr("cy", 30)
.attr("fill",colorScale(i))
.attr("r", radiusScale(i));
g.append("text").attr("dx",i*12).text(Math.round(radiusScale(i)));
i=i+10;
}
</script>
</body>
</html>
the json data contains:
[
{ "x_axis":40, "y_axis": 10,"width":50,"height":20,"color" : "#1f77b4","text":"F&R"},
{ "x_axis":40, "y_axis": 30,"width":50,"height":20,"color" : "#ff7f0e","text":"Legal"},
{ "x_axis":40, "y_axis": 50,"width":50,"height":20,"color" : "#2ca02c","text":"GGO"},
{ "x_axis":40, "y_axis": 70,"width":50,"height":20,"color" : "#d62728","text":"IP&S"},
{ "x_axis":40, "y_axis": 90,"width":50,"height":20,"color" : "#9467bd","text":"CORP"},
{ "x_axis":40, "y_axis": 110,"width":50,"height":20,"color": "#8c564b","text":"TAX"},
{ "x_axis":40, "y_axis": 130,"width":50,"height":20,"color" : "#e377c2","text":"REUTERS ALL"}
]
You need to use CSS for this. It's easier if you have two containers:
CSS:
.container {
float: left;
}
HTML:
<div class="container" id="chart1">
</div>
<div class="container" id="chart2">
</div>
Then use #chart1 and #chart2 in your code.

drawing on an externally loaded svg graphic in D3

I have loaded an external graphic from an svg file and I want to experiment drawing on it but cannot figure out how. my simple d3 code is here:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script>
</head>
<body>
<script type="text/javascript">
d3.xml("brussels.svg", "image/svg+xml", function(xml) {
document.body.appendChild(xml.documentElement);
});
svg.append("circle")
.style("stroke", "gray")
.style("fill", "white")
.attr("r", 40)
.attr("cx", 50)
.attr("cy", 50)
.on("mouseover", function(){d3.select(this).style("fill", "aliceblue");})
.on("mouseout", function(){d3.select(this).style("fill", "white");});
</script>
</body>
</html>
I am sure it is something simple but I am not sure how to create the actual circle.
Thanks!
The function:
d3.xml("brussels.svg", "image/svg+xml", function(xml) {
document.body.appendChild(xml.documentElement);
});
executes asynchronously. Hence, the code following it is executed before the callback is executed. The second problem is that you need to define the svg variable before you can operate on it.
Something like the following should work:
d3.xml("brussels.svg", "image/svg+xml", function(xml) {
document.body.appendChild(xml.documentElement);
var svg = d3.select('svg');
svg.append("circle")
.style("stroke", "gray")
.style("fill", "white")
.attr("r", 40)
.attr("cx", 50)
.attr("cy", 50)
.on("mouseover", function(){d3.select(this).style("fill", "aliceblue");})
.on("mouseout", function(){d3.select(this).style("fill", "white");});
});

Resources