How to add D3 tooltips - d3.js

Currently I am looking in to D3 sample trying to alter it to add tooltips https://plnkr.co/edit/stHnntCknDUs0Xw6MlQ2?p=preview but cant manage it myself.
I want to add tooltip and pointers to this the way like it is done here http://c3js.org/samples/timeseries.html
//Add tooltips
// Define the div for the tooltip
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Add the scatterplot
countryEnter.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 5)
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.stat); })
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div .html(formatTime(d.date) + "<br/>" + d.date)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
Just cant get it working

You are putting the hover on the wrong element. You are putting them on the dots (which don't exist as in your data you as you have no date attribute).
What I have done is place them on the path like so :
countryEnter.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); })
.on("mouseover", function(d) {
console.log(d)
divTooltip.transition()
.duration(200)
.style("opacity", .9)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px")
.innerHTML(formatTime(d.date) + "<br/>" + d.close)
})
.on("mouseout", function(d) {
divTooltip.transition()
.duration(500)
.style("opacity", 0);
});
But I still get the error formatTime is not defined. But the problem is solved on getting the tooltip to be viewed. So solving the rest shouldn't be difficult :)
Updated plunkr : https://plnkr.co/edit/zQY0Wen1plIMuwOMq3PE?p=preview

Related

d3 grouped bar chart highlight group on mouseover

Solved: jsfiddle
Issue #1: Have a grouped bar chart. I'd like the group to highlight if any bars in the group are moused-over.
Currently on mouseover sets all rects with class 'bar' to opacity 0.5 and then the specific rect to opacity 1. But how can I set the node or group of bars to opacity 1, so that they are highlighted against the others?
.on("mouseover", function(d, i, node) { //this is repeated should be in a function
d3.selectAll(".bar").transition()
.style("opacity", 0.3); //all groups given opacity 0
d3.select(this).transition()
.style("opacity", 1); //give opacity 1 to group on which it hovers.
return tooltip
.style("visibility", "visible")
.html("<span style=font-size:30px;> " + "name:" + d.name +
"</span>"
)
})
.on("mouseout", function(d) {
d3.selectAll(".bar").transition()
.style("opacity", 1);
return tooltip.style("visibility", "hidden");
})
Issue #2: Also I would like the bar's x axis labels to behave similarly. So that the names of all but the current bar would have opacity 0.5
I did try adding a clas of bar to the xAxis text, but doesn't work,
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.style("font", "20px times")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("class", "bar")
.attr("transform", "rotate(-65)");
I this try implementing ideas from this post
D3 Grouped Bar Chart - Selecting entire group?
but I haven't been able to get it to work.
My attempt to give a class of d.name + index to each bar in a group. But I can't select then, the return "." + d.name isn't working as I'd expect.
.attr("class", function(d, i) {
return d.name.replace(/\s/g, '') + i + " bar"
})
.on("mouseover", function(d, i, node) {
d3.selectAll(".bar").transition()
.style("opacity", 0.3);
d3.selectAll("." + d.name.replace(/\s/g) + i)
.transition()
.style("opacity", 1);
return tooltip
.style("visibility", "visible")
.html("<span style=font-size:30px;> " + "name:" + d.name +
"</span>");
})
The select should be,
d3.selectAll("." + d.name.replace(/\s/g, '') + i)
Actually each bar in each group could just be given a class of "group + index". There is no need for the regular expression.
Except for the text on the xAxis the highlighting is now working fine.
Any help would be greatly appreciated,
Thanks
you could base the opacity for all bars on its .name value (which is common attribute per group in your example), eg
.on("mouseover", function(d) {
let selectedName = d.name;
d3.selectAll(".bar")
.style("opacity", function(d) {
return d.name == selectedName ? 1 : 0.3;
})
//etc
This works jsFiddle
.on("mouseover", function(d, i, node) {
d3.selectAll(".bar").transition()
.style("opacity", 0.3);
d3.selectAll(".xAxisText").transition()
.style("fill", "lightgray")
.style("font-weight", "100"); //all groups given opacity 0
d3.selectAll(".groupText" + i)
.transition()
.style("font-weight", "900")
.style("fill", "black"); //give opacity 1 to group on which it hovers.
d3.selectAll(".group" + i)
.transition()
.style("opacity", 1);
return tooltip
.style("visibility", "visible")
.html("<span style=font-size:30px;> " + "name: " + d.name +
" (on blue bar)</span>");
})
.on("mouseout", function(d) {
d3.selectAll(".bar").transition()
.style("opacity", 1);
d3.selectAll(".xAxisText").transition()
.style("fill", "black")
.style("font-weight", "normal");
return tooltip.style("visibility", "hidden");
})

Tooltip does not appear for the overlapped circles in d3.js

I am creating a scatter Plot in which the points sometimes overlap each other. On mouse-over on any of those points, either the tooltip flickers or sometimes it does not appear. Can any one help me out with this?
dots.on("mouseenter", function(d) {
d3.select(this).attr({
r: radius * 2
});
d3.selectAll(".crosshair")
.style("display", "block");
var xCoord = d3.mouse(this)[0];
var yCoord = d3.mouse(this)[1];
addCrossHair(xCoord, yCoord);
tooltipDiv
.style("top", (d3.event.pageY + 2) + "px")
.style("left", (d3.event.pageX - 28) + "px")
.style("opacity", 0.9)
.style("display", "block")
.html(content);
});
dots.on("mouseout", function(d) {
d3.select(this).attr({
r: radius
});
d3.selectAll(".crosshair")
.style("display", "none");
tooltipDiv.transition()
.duration(100)
.style("display", "none");
});
//tooltip //
var tooltipDiv = d3.select("#scatterChart")
.append("div")
.attr("class", "d3-tip n")
.style("opacity", 0)
.style("position","fixed")
.style("display", "block")
.style("top", 100)
.style("left", 100)
.style("pointer-events","none");
//crossHair//
function addCrossHair(xCoord, yCoord) {
if(!xCoord || !yCoord){ // don't draw cross hair if no valid coordinates given
return;
}
d3.select("#h_crosshair")
.attr("x1", 0)
.attr("y1", yCoord)
.attr("x2", width)
.attr("y2", yCoord)
.style("display", "block");
d3.select("#v_crosshair")
.attr("x1", xCoord)
.attr("y1", 0)
.attr("x2", xCoord)
.attr("y2", height)
.style("display", "block");
}
I got the solution for the above question asked by me.
On mouse-out the duration given to the tooltip was causing the flickering issue.
dots.on("mouseout", function(d) {
d3.select(this).attr({
r: radius
});
d3.selectAll(".crosshair")
.style("display", "none");
tooltipDiv.transition()
.style("display", "none");
});
On removing the duration(100), the above raised issue was solved.

Can't add title to stacked bar graph on D3

I creating a stacked bar chart using this example. The chart works and renders but I can't add a mouseover label.
I tried this...
DATE.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); });
.append("svg:title")
.text(functino(d){return "foo"});
But this after adding the .append("svg:title... the graph stops rendering. If I remove the .style("fill... line, the graph renders, however it's not stacked and there's no mouseover feature.
I have also tried using the tooltip route. (Source)
.on("mouseover", function() { tooltip.style("display", null); })
.on("mouseout", function() { tooltip.style("display", "none"); })
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 15;
var yPosition = d3.mouse(this)[1] - 25;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d.y);
});
// Prep the tooltip bits, initial display is hidden
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
But still not luck. Is there a library I need to load? Not sure what's going on.
The graph stop rendering when you try to append the title because you have a typo: it's function, not functino.
Besides that, this is what you need to get the value of each stacked bar:
.append("title")
.text(function(d){
return d[1]-d[0]
});
Here is the demo: https://bl.ocks.org/anonymous/raw/886d1749c4e01e191b94df23d97dcaf7/
But I don't like <title>s. They are not very versatile. Thus, instead of creating another <text>, as the second code you linked does, I prefer creating a div:
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
Which we position and set the HTML text this way:
.on("mousemove", function(d) {
tooltip.html("Value: " + (d[1] - d[0]))
.style('top', d3.event.pageY - 10 + 'px')
.style('left', d3.event.pageX + 10 + 'px')
.style("opacity", 0.9);
}).on("mouseout", function() {
tooltip.style("opacity", 0)
});
And here is the demo: https://bl.ocks.org/anonymous/raw/f6294c4d8513dbbd8152770e0750efd9/

Unclear about how dc.js events are working in this example

I am trying to create something similar to this: http://photogrammar.yale.edu/labs/crossfilter/california/ But I am unclear about how clicking on a county is updating the dataTablesm. My version is using a leaflet map with circles created in d3 for markers. I'd like to have it so that when a circle is clicked the dataTablesm only shows those items associated that location. Here is my plnk. In the photogrammar example I am not seeing any references to clicking or updating or rendering.
var feature = g.selectAll("circle")
.data(stateCountGroup.all())
.enter().append("circle")
.style("stroke", "black")
.style("opacity", .6)
.style("fill", "red")
.attr("r", function(d) {
//console.log(d.value);
return d.value*5; })
//.on("click", click)
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html(d.value + " Items")
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
dataTablesm
.dimension(yearDimension)
.group(function(d) { return ""
})
.size(100)
.columns([
function(d) { return '<a target="_blank" href="https://arc.lib.montana.edu /ivan-doig/item.php?id=' + d.Item+ '"><img src="https://arc.lib.montana.edu/ivan-doig/' + d.Thumb + '" /></a><div class="byline">' + d.Title + '</div>'; },
]);
map.on("viewreset", update);
update();
function update() {
feature.attr("transform", function(d){
//console.log(d.key);
var coor = map.latLngToLayerPoint(d.key);
return "translate(" +
coor.x + "," +
coor.y + ")";
});
}
//});
// register handlers
//d3.selectAll('a#all').on('click', function () {
//dc.filterAll();
//dc.renderAll();
//});
dc.renderAll();
//dc.redrawAll();

Staggered transition in multi line graph

I'm new here and to d3.js/JavaScript in general. I would like to add a transition to a multi line graph so that each line (and associated label) is 'drawn' one after the other. I've managed to get this to work for the first line (and to a lesser extent all lines at the same time) but am struggling to see how I can stagger the transitions. I've tried using a for loop and .each() to call the transition via a function but haven't really got anywhere with either approach. Any help would be gratefully received. Relevant part of the code below. Thanks.
var country = svg.selectAll(".country")
.data(countries)
.enter().append("g")
.attr("class", "country");
var path = country.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.country); })
var totalLength = path.node().getTotalLength();
d3.select(".line")
.attr("stroke-dasharray", totalLength + " " + totalLength)
.attr("stroke-dashoffset", totalLength)
.transition()
.duration(1000)
.ease("linear")
.attr("stroke-dashoffset", 0)
.each("end", function() {
d3.select(".label")
.transition()
.style("opacity", 1);
});
var labels = country.append("text")
.datum(function(d) { return {country: d.country, value: d.values[d.values.length - 1]}; })
.attr("class", "label")
.attr("transform", function(d) { return "translate(" + x(d.value.month) + "," + y(d.value.rainfall) + ")"; })
.attr("x", 3)
.attr("dy", ".35em")
.style("opacity", 0)
.text(function(d) { return d.country; });
You can use .delay() with a function to do this:
d3.select(".line")
.attr("stroke-dasharray", totalLength + " " + totalLength)
.attr("stroke-dashoffset", totalLength)
.transition()
.delay(function(d, i) { return i * 1000; })
.duration(1000)
.ease("linear")
.attr("stroke-dashoffset", 0)
.each("end", function() {
d3.select(".label")
.transition()
.style("opacity", 1);
});

Resources