Tooltip always displaying the same value - javascript

I'm trying to create a map of coordinates from some data I got in a csv file. The converting of the X/Y axes works perfectly, the circles (or rather dots) get drawn but the mouseover tooltip always displays the last values (or rather the last values +1 which is in my array out of bounds even though the tooltip should be set with the current values of the array.
Longitude and altitude are my two array names
var svgContainer = d3.select("body").append("svg")
.attr("width", 700)
.attr("height", 250)
.style("border", "1px solid black");
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
for (i = 0; i < longitude.length; i++) {
var circleSelection = svgContainer.append("circle")
.attr("cx", longitude[i])
.attr("cy", altitude[i])
.attr("r", 2)
.style("fill", "purple")
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div .html("X: " + longitude[i] + " Y: " + altitude[i])
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
}
and here's the css but I doubt the problem's to be found in here
<style>
div.tooltip {
position: absolute;
text-align: center;
width: 60px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
Every clue is much appreciated

As a general rule: do not use loops for appending elements in a D3 code. Not only this is not the idiomatic D3 but, more importantly, things will break (as you're seeing right now).
Before anything, here is an explanation of why all the values are the same: JavaScript closure inside loops – simple practical example
Let's see this, hover over any circle:
var data = ["foo", "bar", "baz"];
var svg = d3.select("svg");
for (var i = 0; i < data.length; i++) {
svg.append("circle")
.attr("cy", 75)
.attr("cx", 50 + i * 100)
.attr("r", 20)
.attr("fill", "teal")
.on("mouseover", function() {
console.log(data[i - 1])
})
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>
Things get better using let:
var data = ["foo", "bar", "baz"];
var svg = d3.select("svg");
for (let i = 0; i < data.length; i++) {
svg.append("circle")
.attr("cy", 75)
.attr("cx", 50 + i * 100)
.attr("r", 20)
.attr("fill", "teal")
.on("mouseover", function() {
console.log(data[i])
})
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>
However, even if using let gives the correct result, it is not a good solution, because you are not binding any data.
The best solution is: use a D3 "enter" selection, binding data to the elements:
var data = ["foo", "bar", "baz"];
var svg = d3.select("svg");
svg.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("cy", 75)
.attr("cx", function(d, i) {
return 50 + i * 100
})
.attr("r", 20)
.attr("fill", "teal")
.on("mouseover", function(d) {
console.log(d)
})
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>

Related

problem with D3 lasso circle element with text

I basically copied the example https://bl.ocks.org/skokenes/a85800be6d89c76c1ca98493ae777572
Then I got the code to work with my data. So, I can now get the lasso to work.
But when I try to add back my old code for the circles to display a text-type tool tip, the lasso breaks. The code then puts the class variables such as not_possible or selected on the "text" elements rather than on the "circle" elements where they need to be.
I found that is the issue by using Chrome developer tools.
When the tool tips code is commented out, the lasso code works and the DOM looks like this:
<circle cx="854" cy="37" fill="red" r="7" class="selected"></circle>
When the tool tips code is live, the tool tips work but the lasso code doesn't work and the DOM looks like this:
<circle cx="854" cy="37" fill="red" r="4.9">
<title r="3.5" class> ==$0
"curr = 89.7, prev = 89.5, geo = Alaska, measure = Percent Citizen, Born in the US"
</title>
</circle>
I've tried changing the styles for the classes, for example, from ".possible" to "circle.possible" but that doesn't help. I've googled for suggestions but haven't found anything that I could make work. I've tried passing the circle selection thru lasso.items(circles) but that doesn't work.
This is the lasso code that does work: the troublesome ".append title" and "text" lines are commented out.
var margin = {top: 20, right: 15, bottom: 60, left: 60}
, width = 960 - margin.left - margin.right
, height = 960 - margin.top - margin.bottom;
var xScale = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return d[1]; })])
.range([0, width]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return d[0]; })])
.range([height, 0]);
var svgArea = d3.select('.content')
.append('svg')
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom)
.attr('class', 'chart');
var main = svgArea.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.attr('width', width)
.attr('height', height)
.attr('class', 'main');
main.append('g')
.attr('transform', 'translate(0,' + height + ')')
.attr('class', 'main axis date')
.call(d3.axisBottom(xScale));
main.append('g')
.append("text")
.attr("x", width / 2)
.attr("y", height + margin.bottom - 10)
.style("text-anchor", "middle")
.style("font", "14px times")
.text("Current X");
main.append('g')
.attr('transform', 'translate(0,0)')
.attr('class', 'main axis date')
.call(d3.axisLeft(yScale));
main.append('g')
.append("text")
.attr("transform", "rotate(-90)")
.attr("x", 0 - (height / 2))
.attr("y", 0 - margin.left / 2)
.style("text-anchor", "middle")
.style("font", "14px times")
.text("Previous Y");
var rScale = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return d[1]; })])
.range([ 4, 5 ]);
var lasso_start = function() {
lasso.items()
.attr("r",7)
.classed("not_possible",true)
.classed("selected",false)
;
};
var lasso_draw = function() {
lasso.possibleItems()
.classed("not_possible",false)
.classed("possible",true)
;
lasso.notPossibleItems()
.classed("not_possible",true)
.classed("possible",false)
;
};
var lasso_end = function() {
lasso.items()
.classed("not_possible",false)
.classed("possible",false)
;
lasso.selectedItems()
.classed("selected",true)
.attr("r", 7)
;
lasso.notSelectedItems()
.attr("r", 3.5)
;
};
var circles = main.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("cx", function (d,i) { return xScale(d[1]); } )
.attr("cy", function (d) { return yScale(d[0]); } )
.attr("fill", function (d) { if (d[1] > 75) { return "red"; } else { return "black"; } })
.attr("r", function (d) { return rScale(d[1]); })
//.append("title")
//.text(function(d) {
// return "curr = " + d[1] +
// ", prev = " + d[0] +
// ", geo = " + d[2] +
// ", measure = " + d[3];
// })
;
var lasso = d3.lasso()
.items(circles)
.closePathDistance(75) // max distance for the lasso loop to be closed
.closePathSelect(true) // can items be selected by closing the path?
.targetArea(svgArea) // area where the lasso can be started
.on("start",lasso_start) // lasso start function
.on("draw",lasso_draw) // lasso draw function
.on("end",lasso_end); // lasso end function
svgArea.call(lasso);
Why does including ".title" and ".text" cause a problem?
And how do I solve it?
I don't think the problem is with the CSS, but here it is:
<style>
// styling for D3 chart
.chart {
background: #fdfefe;
}
.main text {
font: 10px sans-serif;
}
// styling for D3-lasso
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
circle {
fill-opacity: 0.4;
}
.dot {
stroke: #000;
}
.lasso path {
stroke: rgb(80,80,80);
stroke-width: 2px;
}
.lasso .drawn {
fill-opacity: 0.05 ;
}
.lasso .loop_close {
fill: none;
stroke-dasharray: 4,4;
}
.lasso .origin {
fill: #3399FF;
fill-opacity: 0.5;
}
.not_possible {
fill: rgb(200,200,200);
}
.possible {
fill: #EC888C;
}
.selected {
fill: steelblue;
}
</style>
The problem appears to be that lasso is adding a radius attribute to the title elements here:
lasso.notSelectedItems()
.attr("r", 3.5)
;
resulting in all your not-selected elements, i.e., circles, and titles, having the attribute assigned, as your example suggests:
<title r="3.5" class>
Rather than calling lasso's selected and notSelected to change the radius and css class of the desired items, use a filter on the items array itself:
// Style the selected dots
lasso.items().filter(function(d) {return d.selected===true})
.classed(...)
.attr("r",7);
// Reset the style of the not selected dots
lasso.items().filter(function(d) {return d.selected===false})
.classed(...)
.attr("r",3.5);
You can get as specific as you want with the return value, i.e., omit any nodes (like title nodes) you don't want affected by the rules you apply to the selection.
The problem was that I couldn't get D3 lasso and my approach to tool tips to work together. I was appending a title element to each circle (point) on a scatter plot. This does NOT work:
var circles = main.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("cx", function (d,i) { return xScale(d[1]); } )
.attr("cy", function (d) { return yScale(d[0]); } )
.attr("fill", function (d) { if (d[1] > 75) { return "red"; } else { return "black"; } })
.attr("r", function (d) { return rScale(d[1]); })
.append("title")
.text(function(d) {
return "curr = " + d[1] +
", prev = " + d[0] +
", geo = " + d[2] +
", measure = " + d[3];
})
;
I found a coding example by Mikhail Shabrikov that solved the issue by avoiding .append("title") altogether. This works:
A new CSS element:
.tooltip {
position: absolute;
z-index: 10;
visibility: hidden;
background-color: lightblue;
text-align: center;
padding: 4px;
border-radius: 4px;
font-weight: bold;
color: black;
}
A new DIV element:
var tooltip = d3.select("body")
.append("div")
.attr('class', 'tooltip');
And mainly a modified circles element:
var circles = main.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("cx", function (d,i) { return xScale(d[1]); } )
.attr("cy", function (d) { return yScale(d[0]); } )
.attr("fill", function (d) { if (d[1] > 75) { return "red"; } else { return "black"; } })
.attr("r", 5)
.on("mouseover", function(d) {return tooltip.style("visibility", "visible")
.text(
"curr = " + d[1] +
", prev = " + d[0] +
", geo = " + d[2] +
", measure = " + d[3]
)
})
.on("mousemove", function() {
return tooltip.style("top", (event.pageY - 30) + "px")
.style("left", event.pageX + "px");
})
.on("mouseout", function() {
return tooltip.style("visibility", "hidden");
})
;
Shabrikov's code is near the very bottom of this item: circles, tool tips, mouse events

how to plot the image inside the polygon in d3

hi all i am using d3 chart with polygon i have one map structure d3 chart and plot one circle for the purpose of show tooltip now my need is i need to show one image 'https://i.stack.imgur.com/O9xB5.png' to replace the circle so when mouse over the image i shown tooltip and another need show 'State Abbr' inside polygon like Ak,TD,PD...
.help ow to do this here i attached my code files
code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="http://d3js.org/d3.v3.min.js"></script>
<style type="text/css">
/* On mouse hover, lighten state color */
path:hover {
fill-opacity: .7;
}
/* Style for Custom Tooltip */
div.tooltip {
position: absolute;
text-align: center;
width: 60px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: white;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
/* Legend Font Style */
body {
font: 11px sans-serif;
}
/* Legend Position Style */
.legend {
position:absolute;
left:800px;
top:350px;
}
</style>
</head>
<body>
<script type="text/javascript">
//Width and height of map
var width = 960;
var height = 500;
// D3 Projection
var projection = d3.geo.albersUsa()
.translate([width/2, height/2])
.scale([1000]);
// Define path generator
var path = d3.geo.path()
.projection(projection);
// Define linear scale for output
var color = d3.scale.linear()
.range(["green","red"]);
var legendText = ["Population Present", "Population Absent"];
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var div = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Load in my states data!
d3.csv("Population_education.csv", function(data) {
// Load GeoJSON data and merge with states data
d3.json("us-states.json", function(json) {
// Loop through each state data value in the .csv file
for (var i = 0; i < data.length; i++) {
// Grab State Name
var dataState = data[i].SiteState;
// Get Population
var dataPop = data[i].Population;
// Grab data value
if(data[i].Members > 0) {
var dataValue = 1;
}
else { var dataValue = 0;}
// Find the corresponding state inside the GeoJSON
for (var j = 0; j < json.features.length; j++) {
var jsonState = json.features[j].properties.name;
if (dataState == jsonState) {
// Copy the data value into the JSON
json.features[j].properties.MembersPresent = dataValue;
json.features[j].properties.pop = +dataPop;
// Stop looking through the JSON
break;
}
}
}
// Get Max and Min Population and update colorscale
var max = d3.max(json.features, function(d) { return d.properties.pop });
var min = d3.min(json.features, function(d) { return d.properties.pop })
color.domain([min, max]); // setting the range of the input data
// Bind the data to the SVG and create one path per GeoJSON feature
svg.selectAll("path")
.data(json.features)
.enter().append("path")
.attr("d", path)
.style("stroke", "#fff")
.style("stroke-width", "1")
.style("fill", function(d) {
return color(d.properties.pop)
});
// Map the cities I have lived in!
d3.csv("Population_education.csv", function(data) {
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
if (d.AvgLng != 0 && d.AvgLat != 0)
return projection([d.AvgLng, d.AvgLat])[0];
})
.attr("cy", function(d) {
if (d.AvgLng != 0 && d.AvgLat != 0)
return projection([d.AvgLng, d.AvgLat])[1];
})
.attr("r", function(d) {
return 3;
})
.style("fill", "rgb(217,91,67)")
.style("opacity", 0.45)
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html("State:" + d['State Abbr'] + "<br/>" + "Pop:" + d.Population)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
});
var legend = d3.select("body").append("svg")
.attr("class", "legend")
.attr("width", 140)
.attr("height", 200)
.selectAll("g")
.data(color.domain().slice().reverse())
.enter()
.append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.data(legendText)
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) { return d; });
});
});
</script>
</body>
</html>
Data
Population_education.csv
RowID,SiteState,State Abbr,AvgLat,AvgLng,Population
1,Alabama,AL,32.806671,-86.79113,28
2,Arizona,AZ,33.729759,-111.431221,11704
3,California,CA,36.116203,-119.681564,4356448
4,Colorado,CO,39.059811,-105.311104,374435
5,Connecticut,CT,41.597782,-72.755371,455966
6,Florida,FL,27.766279,-81.68678300000001,442537
7,Georgia,GA,33.040619,-83.643074,1339081
8,Illinois,IL,40.349457,-88.986137,29
9,Indiana,IN,39.849426,-86.258278,1525124
10,Iowa,IA,42.011539,-93.210526,185146
11,Kansas,KS,38.5266,-96.72648599999999,129301
12,Kentucky,KY,37.66814,-84.670067,621047
13,Louisiana,LA,31.169546,-91.867805,170568
14,Maine,ME,44.693947,-69.381927,222966
15,Maryland,MD,39.063946,-76.80210099999999,256966
16,Massachusetts,MA,42.230171,-71.530106,27
17,Michigan,MI,43.326618,-84.536095,27
18,Minnesota,MN,45.694454,-93.900192,11
19,Missouri,MO,38.456085,-92.28836800000001,420415
20,Nevada,NV,38.313515,-117.055374,309799
21,New Hampshire,NH,43.452492,-71.563896,195948
22,New Jersey,NJ,40.298904,-74.521011,241039
23,New Mexico,NM,34.840515,-106.248482,1945
24,New York,NY,42.165726,-74.94805100000001,1075153
25,North Carolina,NC,35.630066,-79.80641900000001,14
26,Ohio,OH,40.388783,-82.764915,1526404
27,Oregon,OR,44.572021,-122.070938,11
28,Pennsylvania,PA,40.590752,-77.209755,197
29,South Carolina,SC,33.856892,-80.945007,45
30,Tennessee,TN,35.747845,-86.692345,446667
31,Texas,TX,31.054487,-97.563461,736672
32,Vermont,VA,37.769337,-78.169968,2324640
33,Washington,WA,47.400902,-121.490494,141319
34,West Virginia,WV,38.491226,-80.954453,128275
35,Wisconsin,WI,44.268543,-89.616508,405942
36,Alaska,AK,0,0,0
37,Arkansas,AR,0,0,0
38,Delaware,DE,0,0,0
39,District of Columbia,DC,0,0,0
40,Hawaii,HI,0,0,0
41,Idaho,ID,0,0,0
42,Mississippi,MS,0,0,0
43,Montana,MT,0,0,0
44,Nebraska,NE,0,0,0
45,North Dakota,ND,0,0,0
46,South Dakota,SD,0,0,0
47,Utah,UT,0,0,0
48,Virginia,VT,0,0,0
49,Wyoming,WY,0,0,0
50,Oklahoma,OK,0,0,0
51,Rhode Island,RI,0,0,0
My us-states.json is as in the following link https://raw.githubusercontent.com/alignedleft/d3-book/master/chapter_12/us-states.json
Code to add image and tooltip to each polygon.
paths.each(function(d) {
if (this.getTotalLength() > 0) {
var midPoint = path.centroid(d);
svg.append("svg:image")
.attr("height", "15px")
.attr("width", "15px")
.attr("xlink:href", "https://i.stack.imgur.com/O9xB5.png")
.attr("transform", "translate(" + midPoint[0] + ", " + midPoint[1] + ")")
.append("title")
.text(d.properties.abbr);
svg.append("svg:text")
.attr("x", midPoint[0])
.attr("y", midPoint[1])
.text(d.properties.abbr);
}
});
JSFiddle
To include abbr details to the data, code as shown below.
// Find the corresponding state inside the GeoJSON
for (var j = 0; j < json.features.length; j++) {
var jsonState = json.features[j].properties.name;
if (dataState == jsonState) {
// Copy the data value into the JSON
json.features[j].properties.MembersPresent = dataValue;
json.features[j].properties.pop = +dataPop;
json.features[j].properties.abbr = data[i]["State Abbr"];
// Stop looking through the JSON
break;
}
}

D3 js tooltip issue for Choropleth Map

i am new to D3.js. I have been trying to add tooltip to the existing d3 chloropleth Map by Michelle Chandra. However i am unable to make any progress, the tooltip doesnt seem to appear. Where am i doing wrong? Any Help will be appreciated. http://bl.ocks.org/michellechandra/0b2ce4923dc9b5809922.
Thanks
<style type="text/css">
/* On mouse hover, lighten state color */
path:hover {
fill-opacity: .7;
}
/* Style for Custom Tooltip */
div.tooltip {
position: absolute;
text-align: center;
width: 60px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: white;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
/* Legend Font Style */
body {
font: 11px sans-serif;
}
/* Legend Position Style */
.legend {
position:absolute;
left:800px;
top:350px;
}
</style>
</head>
<body>
<script type="text/javascript">
/* This visualization was made possible by modifying code provided by:
Scott Murray, Choropleth example from "Interactive Data Visualization for the Web"
https://github.com/alignedleft/d3-book/blob/master/chapter_12/05_choropleth.html
Malcolm Maclean, tooltips example tutorial
http://www.d3noob.org/2013/01/adding-tooltips-to-d3js-graph.html
Mike Bostock, Pie Chart Legend
http://bl.ocks.org/mbostock/3888852 */
//Width and height of map
var w = 900;
var h = 600;
// D3 Projection
var projection = d3.geo.albersUsa()
.translate([w/2, h/2]) // translate to center of screen
.scale([1000]); // scale things down so see entire US
// Define path generator
var path = d3.geo.path() // path generator that will convert GeoJSON to SVG paths
.projection(projection); // tell path generator to use albersUsa projection
// Define linear scale for output
var color = d3.scale.linear()
.range(["rgb(24,143,95)","rgb(51,188,196)","rgb(155,226,183)","rgb(217,91,67)"]);
var legendText = ["Cities Lived", "States Lived", "States Visited", "States Not Visited Yet"];
//Create SVG element and append map to the SVG
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
// Append Div for tooltip to SVG
var div = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Load in my states data!
d3.csv("stateslived.csv", function(data) {
color.domain([0,1,2,3]); // setting the range of the input data
// Load GeoJSON data and merge with states data
d3.json("us-states.json", function(json) {
// Loop through each state data value in the .csv file
for (var i = 0; i < data.length; i++) {
// Grab State Name
var dataState = data[i].state;
// Grab data value
var dataValue = data[i].visited;
// Find the corresponding state inside the GeoJSON
for (var j = 0; j < json.features.length; j++) {
var jsonState = json.features[j].properties.name;
if (dataState == jsonState) {
// Copy the data value into the JSON
json.features[j].properties.visited = dataValue;
// Stop looking through the JSON
break;
}
}
}
// Bind the data to the SVG and create one path per GeoJSON feature
svg.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path)
.style("stroke", "#fff")
.style("stroke-width", "1")
.style("fill", function(d) {
// Get data value
var value = d.properties.visited;
if (value) {
//If value exists…
return color(value);
} else {
//If value is undefined…
return "rgb(213,222,217)";
}
});
// Map the cities I have lived in!
d3.csv("cities-lived.csv", function(data) {
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("cy", function(d) {
return projection([d.lon, d.lat])[1];
})
.attr("r", function(d) {
return Math.sqrt(d.years) * 4;
})
.style("fill", "rgb(217,91,67)")
.style("opacity", 0.85)
// add browser tooltip of city name
//.append("title")
//.text(function(d) {
// return d.place;
//});
// Modification of custom tooltip code provided by Malcolm Maclean, "D3 Tips and Tricks"
// http://www.d3noob.org/2013/01/adding-tooltips-to-d3js-graph.html
.on("mouseover", function(d) {
d3.select(this).transition().duration(300).style("opacity", 1);
div.transition().duration(200)
.style("opacity", .9);
div.text(d.properties.visited)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
// fade out tooltip on mouse out
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
});
// Modified Legend Code from Mike Bostock: http://bl.ocks.org/mbostock/3888852
var legend = d3.select("body").append("svg")
.attr("class", "legend")
.attr("width", 140)
.attr("height", 200)
.selectAll("g")
.data(color.domain().slice().reverse())
.enter()
.append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.data(legendText)
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) { return d; });
});
});
/* This code generate paths without mapping to other data
// Load JSON file and generate path for each state
d3.json("us-states.json", function(json) { // file path, callback function called when data loaded
svg.selectAll("path") // creates empty references to all the paths
.data(json.features) // loop through our data (the states in the array) and bind to paths
.enter() // create placeholder to reference the new elements
.append("path") // add to the DOM!
.attr("d", path) // generate paths for each state
.style("fill", "steelblue"); // make the states blue!
}); */
</script>
I'm not sure what this statement is supposed to do:
d3.select(this).transition().duration(300).style("opacity", 1);
It appears to select the window object.
One would need to look at your json file to see if you're getting the right data into the div.
Shameless plug. Take a look at foxToolTip.js. I think its easier and more flexible than d3tip.
https://github.com/MichaelRFox/foxToolTip.js
Once div is a div, you have to use html, not text:
div.transition().duration(200)
.style("opacity", .9);
div.html(d.properties.visited)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");

d3: repeat transition for series of elements?

I'm having trouble getting a transition to repeat, for a series of elements, in this case a set of three lines. The animation runs just fine once, but when it is repeated (with the same data), all three lines merge into a single line (the last array in data). What am I doing wrong?
(function() {
var w = 100, h = 100
var div = d3.select('#sketches').append('div')
var svg = div.append("svg")
.attr("width", w)
.attr("height", h)
var x = 0, y = 55
var data = [
[x, y, x+20, y-40],
[x+10, y, x+30, y-40],
[x+20, y, x+40, y-40]
];
(function lines() {
svg.selectAll('line')
.data(data).enter().append('line')
.attr("stroke", "black")
.attr('x1', function(d) {return d[0]})
.attr('y1', function(d) {return d[1]})
.attr('x2', function(d) {return d[2]})
.attr('y2', function(d) {return d[3]})
.transition()
.duration(3000)
.ease('linear')
.attr('x1', function(d) {return d[0] + 60})
.attr('y1', function(d) {return d[1]})
.attr('x2', function(d) {return d[2] + 60})
.attr('y2', function(d) {return d[3]})
.each('end', function(d) {
d3.select(this).remove()
lines()
})
})()
})()
body {
padding: 1rem;
}
svg {
background-color: silver;
stroke: black;
stroke-width: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="sketches"></div>
The issue is the each function will initiate for each line you have. So actually what you are doing is calling lines() three times every time. Why it's yieling the output of one line I'm not entirely sure (still looking into it) but for some reason, it seems like data defaults to the last array so its only setting the drawing to be based on data[3].
To fix it, you want to make sure lines() only gets called after it has finished going through removing all the lines so it only runs once. I'm pretty sure there is better way (i.e. a promise of some kind so after all of each has ran, it'll run a function, but what you can do is set a count and then just run lines() every N times where N is the number of lines you want removed. Because you go through array data and append a line for each index, N is data.length.
(I'm gonna see if there's a cleaner way to do this and I'll edit my answer if I find a way but hopefully this helps you understand the issue at the very least)
(function() {
var w = 100, h = 100
var div = d3.select('#sketches').append('div')
var svg = div.append("svg")
.attr("width", w)
.attr("height", h)
var x = 0, y = 55
var data = [
[x, y, x+20, y-40],
[x+10, y, x+30, y-40],
[x+20, y, x+40, y-40]
];
var count = 0;
(function lines() {
svg.selectAll('line')
.data(data).enter().append('line')
.attr("stroke", "black")
.attr('x1', function(d) {return d[0]})
.attr('y1', function(d) {return d[1]})
.attr('x2', function(d) {return d[2]})
.attr('y2', function(d) {return d[3]})
.transition()
.duration(3000)
.ease('linear')
.attr('x1', function(d) {return d[0] + 60})
.attr('y1', function(d) {return d[1]})
.attr('x2', function(d) {return d[2] + 60})
.attr('y2', function(d) {return d[3]})
.each('end', function(d) {
d3.select(this).remove()
count++;
if (count == data.length) {
count = 0;
lines();
}
})
})()
})()
body {
padding: 1rem;
}
svg {
background-color: silver;
stroke: black;
stroke-width: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="sketches"></div>

Binding Data from tutorial example

I'm pretty new to d3 and have been following this tutorial: http://christopheviau.com/d3_tutorial/
I'm stuck on the 'Binding Data' example - it's pretty simple but the code just won't produce anything. I've poked around here and haven't found the question listed so I thought I'd ask away.
Here's the code:
var dataset = [],
i = 0;
for(i = 0; i < 5; i++) {
dataset.push(Math.round(Math.random() * 100));
}
var sampleSVG = d3.select("#viz")
.append("svg")
.attr("width", 400)
.attr("height", 75);
sampleSVG.selectAll("circle")
.data(dataset)
.enter().append("circle")
.style("stroke", "gray")
.style("fill", "white")
.attr("height", 40)
.attr("width", 75)
.attr("x", function (d, i) {
return i * 80
})
.attr("y", 20);
Other examples on the site work fine.
Thanks in advance - any ideas would be appreciated.
Unfortunately the code listed in the tutorial is incorrect. The svg element "circle" is specified by three attributes, "cx", x-axis coordinate of the center of the circle, "cy", y-axis coordinate of the center of the circle, and "r", the radius of the circle. I got this information from the w3 specification for an SVG circle.
I would recommend inspecting the JavaScript in the tutorial page to help iron out any other inconsistencies. Here it is:
<script type="text/javascript">
var dataset = [],
i = 0;
for(i=0; i<5; i++){
dataset.push(Math.round(Math.random()*100));
}
var sampleSVG = d3.select("#viz5")
.append("svg")
.attr("width", 400)
.attr("height", 100);
sampleSVG.selectAll("circle")
.data(dataset)
.enter().append("circle")
.style("stroke", "gray")
.style("fill", "white")
.attr("r", 40)
.attr("cx", function(d, i){return i*80+40})
.attr("cy", 50)
.on("mouseover", function(){d3.select(this).style("fill", "aliceblue");})
.on("mouseout", function(){d3.select(this).style("fill", "white");})
.on("mousedown", animateFirstStep);
function animateFirstStep(){
d3.select(this)
.transition()
.delay(0)
.duration(1000)
.attr("r", 10)
.each("end", animateSecondStep);
};
function animateSecondStep(){
d3.select(this)
.transition()
.duration(1000)
.attr("r", 40);
};
</script>
I also created a JSFiddle which you can utilize to get the basic idea that the author of the tutorial is trying to convey, with respect to utilizing d3.js data, here.
svg circles use cx, cy, and r - not x, y, height, and width. I've correct the example code below:
var dataset = [];
for(var i = 0; i < 5; i++) {
dataset.push(Math.round(Math.random() * 100));
}
var sampleSVG = d3.select("#viz")
.append("svg")
.attr("width", 400)
.attr("height", 400);
sampleSVG.selectAll("circle")
.data(dataset)
.enter().append("circle")
.style("stroke", "black")
.attr("r", 10)
.attr("cx", function (d, i) {
return i * 80 + 10;
})
.attr("cy", function (d, i) {
return d;
});
http://jsfiddle.net/q3P4v/7/
MDN on svg circles: https://developer.mozilla.org/en-US/docs/SVG/Element/circle

Categories

Resources