D3.js no value update in a tooltip - d3.js

I´m back with another d3.js problem.
I have an choropleth map and i want a tooltip which shows me the correct value of the community on an mouseover function.
It works nearly perfect, there is only one problem, there is no "update" of the value. on Every county it is the same.
I hope someone has an answer for me. Thank you.
Here is my code snippet:
var feature = group.selectAll("path")
.data(collection.features)
.enter()
.append("path")
//.transition()
//.duration(9000)
.attr("fill",function(d) {
//Get data value
var value = d.properties.EINWOHNER;
//console.log(value);
if (value) {
//If value exists…
return color(value);
}
else {
//If value is undefined…
return "none";
}
})
.on("mouseover", function(d) {
d3.select("#tooltip")
.data(collection.features)
.select("#value")
.text(function(d){
return d.properties.EINWOHNER;
});
//Show the tooltip
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
//Hide the tooltip
d3.select("#tooltip").classed("hidden", true);
});

To set the text, use
.text(d.properties.EINWOHNER);
You're currently getting the value from the data bound to the #value DOM element for each of them. You also don't need to pass in data there -- you already have the current data in d. So the complete code would look like this.
.on("mouseover", function(d) {
d3.select("#tooltip")
.text(d.properties.EINWOHNER);
d3.select("#tooltip").classed("hidden", false);
})

Related

d3js not removing previous element when select a new JSON file

I have a problem with my first attempt at an Exit event. The code below draws one of three circles based on three different JSON files. The default is the medium sized circle. The drop-down menu allows you to select either a Small, Medium, or Large circle to be drawn next.
My problem is that I have not written the Exit mode correctly. If I comment out the exit() then each selection appears on the screen without deleting any of the previous elements, as you would expect. If the exit mode is not commented out then only the default (medium) circle is displayed.
This is likely very obvious to someone with D3JS experience. Please help me to learn where I am going wrong. Thanks so much!
- Tim
<body>
<select id = "opts">
<option value="circle1">Small</option>
<option value="circle2" selected="selected">Medium</option>
<option value="circle3">Large</option>
</select>
<script>
// data sources for small, med, large circles
var circle1 = "/MenuSelections/circle1.JSON";
var circle2 = "/MenuSelections/circle2.JSON";
var circle3 = "/MenuSelections/circle3.JSON";
function drawCirc(newData){
// Read in the JSON data
console.log(newData); // Confirms data source
d3.json(newData, function(dataset) {
var svgContainer = d3.select("body").append("svg")
.attr("width", 200)
.attr("height", 200);
//ENTER
var circles = svgContainer.selectAll("circle")
.data(dataset.circle)
.enter()
.append("circle");
var circleAttributes = circles
.attr("cx", function (d) {
console.log(d)
return d.x_axis; })
.attr("cy", function (d) { return d.y_axis; })
.attr("r", function (d) { return d.radius; })
.style("fill", function(d) { return d.color; });
//EXIT - NOT WORKING. Original remains, no new graph.
// if this section commented out: new circles draw in without remove of prev.
d3.select("body").selectAll("circle")
.data(dataset.circle)
.exit()
.remove();
});
}
drawCirc(circle2); // Initiate with default selection
// handle on click event for the ID (opts) of the menu item
d3.select('#opts')
.on('change', function() {
var newData = eval(d3.select(this).property('value'));
// console.log(newData);
drawCirc(newData); // Call with new selection.
});
The advice from both Lars and Ben got me on the right track, with two key parts to the solution. The first was understanding that exit() acts on the elements, not their data values, and I needed to add a key as suggested above. Once I fixed that I still had a problem because I was defining my svg container WITHIN the drawCirc function, so each new selection resulted in a new SVG 200x200 container.
Good information on Enter, Update, Exit can be found here
http://bost.ocks.org/mike/circles/ and here http://bl.ocks.org/mbostock/3808218
The book "Data Visualization with D3 and AngularJS" Chapter 1, pages 13-16 is also very helpful.
Here is my revised code.
<body>
<select id = "opts">
<option value="circle1">Small</option>
<option value="circle2" selected="selected">Medium</option>
<option value="circle3">Large</option>
</select>
<script>
// var names correspond to the value of each drop down item
var circle1 = "/MenuSelections/circle1.JSON";
var circle2 = "/MenuSelections/circle2.JSON";
var circle3 = "/MenuSelections/circle3.JSON";
var svg = d3.select("body").append("svg")
.attr("width", 200)
.attr("height", 200);
function drawCirc(newData){
console.log(newData); // Confirms data source
// Read in the JSON data
d3.json(newData, function(dataset) {
//ENTER
var circle = svg.selectAll("circle")
.data(dataset.circle, function(d) {return d;});
circle.enter().append("circle");
circle.attr("cx", function (d) {
console.log(d)
return d.x_axis; })
.attr("cy", function (d) { return d.y_axis; })
.attr("r", function (d) { return d.radius; })
.style("fill", function(d) { return d.color; });
//EXIT
circle.exit().remove();
});
}
drawCirc(circle2); // Initiate with default selection
// handle on click event for the ID (opts) of the menu item
d3.select('#opts')
.on('change', function() {
var newData = eval(d3.select(this).property('value'));
drawCirc(newData); // Call with new selection.
});
</script>
</body>

Redrawing the stack bar chat on click on the toggle legend using D3.js

I want to implement stack bar with toggle legend using D3.js ,on click on the legend, stack bar should get redrawn.If the legend was active,rectangle slab corresponding to the legend should get disappear and vise versa.
On click on the legend, I am not able to update the data binded with the group element and rect element present inside the group element properly.
In the DOM tree,on click on the legend,rect element is getting appended and added to first group element, rect element should actually get updated only.
You can view the source code in Jsfiddle here
I want something similar to stack bar with legend selection as implemented here in nvd3
function redraw() {
var legendselector = d3.selectAll("g.rect");
var legendData = legendselector.data();
var columnObj = legendData.filter(function(d, i) {
if (d.active == true)
return d;
});
var remapped = columnObj.map(function(cause) {
return dataArch.map(function(d, i) {
return {
x : d.timeStamp,
y : d[cause.errorType]
};
});
});
var stacked = d3.layout.stack()(remapped);
valgroup = stackBarGroup.selectAll("g.valgroup").data(stacked, function(d) {
return d;
}).attr("class", "valgroup");
valgroup.enter().append("svg:g").attr("class", "valgroup").style("fill",
function(d, i) {
return columnObj[i].color;
}).style("stroke", function(d, i) {
return d3.rgb(columnObj[i].color).darker();
});
valgroup.exit().remove();
rect = valgroup.selectAll("rectangle");
// Add a rect for each date.
rect = valgroup.selectAll("rectangle").data(function(d, i) {
return d;
}).enter().append('rect');
valgroup.exit().remove();
rect.attr("x", function(d) {
return x(d.x);
}).attr("y", function(d) {
return y(d.y0 + d.y);
}).attr("height", function(d) {
return y(d.y0) - y(d.y0 + d.y);
}).attr("width", 6);
}
function redraw() did not use transition inside it
You need to get more understanding about object constancy. (Three state described by the author)
I wrote an example of group chart in d3, the legend is interactable and works well, because i am new to d3, maybe the pattern or standard used is not very formal.
Listed it below only for you reference, hope it helps, good luck :-p
fiddle

on ("click") not working with d3 circle

I am unable to get the click to work on the circle. I have read various tips about how to attach clicks etc. to circles but I cannot get them to work so I must not understand them.
Any tips would be appreciated.
d3.json("d3_files/json/points-category-xyz.json",function(error,data){
svg.select("#points")
.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("r",10)
.style("fill-opacity",.7)
.style("display",function(data){
return"block";
}) // end display style
.style("fill",function (data) {
if (data.category=="abc") {
return "blue"
} // endif
else {
return "red"
} // endelse
}) // end style fill
.style("stroke","#fff")
.on("click", function (){ // this doesn't appear to work
console.log("Hmmm");
alert("Hello");
}) // end click
.attr("transform",function(data){
return"translate("+projection([data.lng,data.lat])+")"
}) // end transform attr
.on("mouseout",function(d){
d3.select("body")
.select(".datamaps-hoverover")
.style("display","none");
d3.select(this)
.style("stroke-width","1px")
}) // end mouseout
.on("mouseover",function(d){
d3.select(this)
.style("stroke-width","4px")
.html(function() {
return node.attributes.name.value + "Popup here"
}) // end .html
}) // end mouseover
.on("mousemove",function(data){
var data=d3.mouse(this);
var n=this;
d3.select(this)
.style("stroke-width","4px")
d3.select("body")
.select(".datamaps-hoverover")
.style("display","block")
.style("top",data[1]+10+"px")
.html(function() {
return node.attributes.name.value + "Popup here"
}) // end html
.style("left",data[0]+"px")
}) // end mouseover
}); // end points json
The problem is that your circles are obscured by other elements and never receive pointer events. One solution to fix this is to append the circles last.
This worked for me.
link.on("click", function(d){
$scope.$apply(function() {
$scope.show(d);
});
d3.event.stopPropagation();
});

How to animate areas of a D3 area chart?

How can d3 areas have their transitions animated? I've seen examples for lines but can't find anything on animating an area.
Eg area:
var area = d3.svg.area()
.x(function(d) { return x(d.x); })
.y0(height)
.y1(function(d) { return y(d.y); });
Update: I've found an example for an area chart but I don't understand it. How is this function creating the area transition?
function transition() {
d3.selectAll("path")
.data(function() {
var d = layers1;
layers1 = layers0;
return layers0 = d;
})
.transition()
.duration(2500)
.attr("d", area);
}
The transition of areas works just like for other attributes. Only in case of areas, we are interpolating strings instead of interpolating numbers. When you call the area function with some data, then it produces a string which looks like M0,213L4,214L9,215 ... L130,255.7, which is a DSL used for the d attribute. When you change the data you pass to the area function, this string changes and D3 interpolates them.
Regarding the example you have found, the interesting bit which causes the transition is only this:
.transition()
.duration(2500)
.attr("d", area);
The other part merely is a fancy way of alternatively returning layers1 and layers0 as the data for the area function on consecutive calls.
d3.selectAll("path")
.data(function() {
var d = layers1;
layers1 = layers0;
return layers0 = d;
})
Thanks #neptunemo for your suggestion. However, your code is too specific for your problem. I would like to take a general case for better illustration of your idea:
Please see the full code from an example of d3noob: https://bl.ocks.org/d3noob/119a138ef9bd1d8f0a8d57ea72355252
Original code of area generator:
var area = d3.area()
.x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.close); });
Modified code of area generator:
var area = function(datum, boolean) {
return d3.area()
.y0(height)
.y1(function (d) { return y(d.close); })
.x(function (d) { return boolean ? x(d.date) : 0; })
(datum);
}
datum is to take the data,
boolean is to control the:
.x() (in case you want the animation along x-axis)
.y1() (in case you want the animation along y-axis)
By setting boolean to false, we're able to set .x() or .y1() to 0.
This will help us to set the initial state of area before triggering the transition process.
Modified code of area transition:
svg.append("path")
.data([data])
.attr("class", "area")
.attr("d", d => area(d, false))
.attr("fill", "lightsteelblue")
.transition()
.duration(2000)
.attr("d", d => area(d,true));
Effects?
case of controlling .x()
case of controlling .y1()
Note: The issue I met is that I cannot synchronize the animation of line and area :(
Bit late to the party, but:
I solved the problem by modifying the original 'area' function, passing two variables: the data, and the field I wish to chart:
var area = function(datum, field) {
return d3.svg.area()
.x(function(d) {
return xScale(d.period_end);
})
.y0(height)
.y1(function(d) {
return yScale(d[field] || 0);
})(datum);
};
Then when you draw the path, just use basic transition. First time, passing no 'field', resulting in drawing zero values, and then - after transition() - passing the field I wanted:
areaChart.append('path')
.attr('class', 'area')
.attr('d', area(chartData))
.attr('fill', function() {
return chartColor;
})
.attr('opacity', 0.15)
.transition().duration(chartSettings.duration)
.attr('d', area(chartData, 'value'));
Works nicely without the need for sub functions. Exactly the same can of course be done for line charts.

Update Selection in D3 is empty

In my code, my update selection is always empty, as is my exit selection, so the transitions never run. Every time I refresh, I end up redrawing the entire DOM fragment as if it never existed before (i.e. I can remove everything but .enter and the behavior doesn't change).
I'm making use of a key function in data() to ensure the join is being made on a unique value instead of by position.
The entire code is at http://jsfiddle.net/colin_young/xRQjX/23/, but I've extracted what I think is the relevant section here (basically, I'm just trying to follow the General Update Pattern):
var key = function (d) {
return d.index;
}
var filterDistance = function () {
var names = list.selectAll("div")
.data(byDistance.bottom(40), key);
// Update
names.attr("class", "update");
// Add
names.enter()
.append("div")
.attr("class", "enter")
.style("opacity", "0")
.transition()
.duration(500)
.style("opacity", "1")
.text(function (d) {
return displayText(d);
});
// Remove
names.exit()
.transition()
.duration(750)
.style("opacity", "0")
.remove();
};
bewest at https://groups.google.com/forum/?fromgroups=#!topic/d3-js/9mrspdWqkiU was able to find the problem. It turned out that in my "working" summary display, I was using $('results').text('insert summary here') which of course wipes out anything else that I might have stuck in there (like my D3 generated divs for instance).

Resources