D3.js, DC.js tooltips - Issue with gridlines - d3.js

I'm using the following code in a series chart renderlet to get a D3 tooltip.
lines.on('renderlet', function(chart) {
chart.selectAll('g.x text')
.attr('transform', 'translate(-29,30) rotate(315)')
chart.selectAll('circle')
.on("mouseover", function(d) {
d3.select(this)
.transition()
.duration(500)
div.transition()
.duration(200)
.style("opacity", 0.9);
div.html("<table><thead><tr><th colspan='2' class='toolHead'>" + d.data.key[1] +
'</th></tr></thead><tbody>' + '<tr style="margin-top: 100px"><td class="toolHeadCol"><td colspan="2">' +
d.data.key[0] + '</td></tr>' + '<tr style="margin-top: 100px"><td class="toolHeadCol"><b>' + 'value: ' +
'</b></td> <td>' + d.y + '</td></tr></tbody></table>')
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function() {
d3.select(this)
.transition()
.duration(500)
.style('opacity', 0)
div.transition()
.duration(500)
.style("opacity", 0)
})
When hovered on a certain data point in line, tooltip gets displayed. Data point on the line(circle) acts as expected, but there are some problems with the corresponding horizontal and vertical grid lines.
They do not disappear on mouseout and stay until another circle is hovered upon.
Since its a series chart, there are multiple lines, and mouseover on a datapoint in one line does not seem to be affected by mouseover on another line. As shown in the below image:
As shown in image, the gridline on the blue line remains visible even when its is hovered up on orange line.
How do I fix these?
Here is the fiddle

Related

d3.js tooltip doesnt show

I am trying to add tooltip, it doesnt show for some reason. Please help. Here's the code https://codepen.io/gladiator_kris/pen/pojgEyQ?editors=0010
var tooltip = svg.append("div")
.attr("id", "tooltip")
.style("opacity", 0.8);
.on("mouseover", function(d) {
tooltip.style("display", "flex")
.html(function() {return 'tooltip'})
.style("left", (d3.event.pageX + 10)+"px")
.style("top", (d3.event.pageY - 28) + "px")
})
.on("mouseout", () => {
tooltip.style("display", "none")
});
Many Thanks!
The problem is caused by the fact that the tooltip is an HTML div, and is being appended to the SVG. div is not a valid SVG tag, so it is not displayed.
This can be fixed by appending the div to div#graph, as shown below:
var tooltip = d3.select("#Graph").append("div")

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");
})

D3 JS appending tooltip to each data circle point and finding on hover event for svg element

I have an array of objects.These objects contain a set of attributes named "x" and "y". Now as i iterate through the array,i append circle for each object into array taking these coordinates as center of the circle.I am trying to attach a tooltip on mouse hover of each circle which displays the x and y coordinate of the circle.
The two problems i am facing is
1.How to append a div element to each circle
2.How to get hover event for the circle?
Please help?
<!doctype html>
<html>
<head>
<title>D3 Basics</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
</head>
<body>
<script>
var data=[
{"x":"100","y":"20"},
{"x":"102","y":"22"},
{"x":"200","y":"30"},
{"x":"500","y":"40"},
{"x":"500","y":"30"}
];
var svgHeight=800;
var svgWidth=800;
var margin=50;
var divelement=d3.select("body")
.append("div")
.attr("height",svgHeight)
.attr("width",svgWidth)
.attr("style","border:1px solid black;");
var svgElement=divelement.append("svg")
.attr("height",svgHeight)
.attr("width",svgWidth);
var boxGroupElement=svgElement.append("g")
.attr("transform","translate("+margin+","+margin+")");
//appending data circle points
for (var a=0; a<data.length; a++) {
boxGroupElement.append("circle")
.attr("cx",data[a].x)
.attr("cy",data[a].y)
.attr("r","3")
.attr("fill","yellow")
.attr("stroke","blue");
}
</script>
</body>
</html>
I have marked this as duplicate but there can be something to learn here.
Where you are creating the circles :
for (var a=0; a<data.length; a++) {
boxGroupElement.append("circle")
.attr("cx",data[a].x)
.attr("cy",data[a].y)
.attr("r","3")
.attr("fill","yellow")
.attr("stroke","blue");
}
This is totally wrong if you are using D3. If you put a mouseover event on this like you normally would with D3 to console log the data like so :
.on('mouseover',function(d){ console.log(d);});
This won't work as there is no data appended to your selection. A work around would be the following :
var circles = boxGroupElement.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
.attr("r", "3")
.attr("fill", "yellow")
.attr("stroke", "blue")
Now for the tooltip. I have referenced this above : Show data on mouseover of circle
The second answer precisely. Here is how it works :
Add a div for your tooltip to sit and set visibility to hidden (so it can become visible on mouseover) :
var tooltip = d3.select("body")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.text("a simple tooltip");
And on mouseover a node show data. Here I have shown the position. Bare in mind, this way wouldn't work if you stuck with your original way of creating the circles as you wouldn't be able to find the data :
.on("mouseover", function(d) {
tooltip.text("Pos : " + d.x + ' : ' + d.y);
return tooltip.style("visibility", "visible");
})
.on("mousemove", function() {
return tooltip.style("top",
(d3.event.pageY - 10) + "px").style("left", (d3.event.pageX + 10) + "px");
})
.on("mouseout", function() {
return tooltip.style("visibility", "hidden");
});
Working fiddle : https://jsfiddle.net/thatOneGuy/7qt1aoan/2/
EDIT
If you are adamant you want to keep it like you have the following could be a work around :
.on("mouseover", function(d, i) {
tooltip.text("Pos : " + data[i].x + ' : ' + data[i].y);
return tooltip.style("visibility", "visible");
})
.on("mousemove", function() {
return tooltip.style("top",
(d3.event.pageY - 10) + "px").style("left", (d3.event.pageX + 10) + "px");
})
.on("mouseout", function() {
return tooltip.style("visibility", "hidden");
});
Updated fiddle for this workaround : https://jsfiddle.net/thatOneGuy/7qt1aoan/3/
Again on the mouseover, you can't use data[a].x as this will always return the last element of the data, closures etc, so I use data[i].x which gives you the current circle you are mousing over :)
You need to add mouseover event to boxGroupElement.append("circle") something like this
boxGroupElement.append("circle")
.attr("cx",data[a].x)
.attr("cy",data[a].y)
.attr("r","3")
.attr("fill","yellow")
.attr("stroke","blue").on("mouseover", function(d) {
div.transition().duration(100).style("opacity", .9);
div.html("My Tooltip" + "<br/>" +d )
.style("left", (d3.event.pageX) + "px")
.style("top",(d3.event.pageY - 28) + "px")
.attr('r', 8);
d3.select(this).attr('r', 8)})
.on("mouseout", function(d) {
div.transition().duration(600).style("opacity", 0)
d3.select(this).attr('r', 3);
});
Here is a working example.

How to add D3 tooltips

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

Why is the brush preventing dc.js barChart toolTips to appear?

I don't see why that behaviour was implemented.
Any good reason ?
In order to have a brushing function, a transparent rectangle that captures all mouse events has to be drawn over top of the graph. That prevents any mouse events from triggering the tooltip event handler on the main graph elements, and is the reason the dc.js API warns that leaving brushing behaviour "on" will disable all other interactive behaviour.
If you want both behaviours, consider using a focus + context layout. That example uses plain d3, but you could recreate it with dc.js. Just have two different views of the same data, one with the brush and one with the tooltips or other interactivity.
You can use https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events to block 'mouseover' event for brush so that tooltips are enabled. Then on chart you can create a custom 'mousedown' event and pass it to brush to enable selection
d3.select('.chartContainer').on('mousedown', function(){
brush_elm = self.scrubberContent.select(".brush").node();
new_click_event = new Event('mousedown');
new_click_event.pageX = d3.event.pageX;
new_click_event.clientX = d3.event.clientX;
new_click_event.pageY = d3.event.pageY;
new_click_event.clientY = d3.event.clientY;
brush_elm.dispatchEvent(new_click_event);
});
I had a similar issue using d3 code. I realized that moving the tooltip event after the brush event fixed the problem. For me, it looked like this:
svg.append("g")
.attr("class", "brush")
.call(brush);
svg.selectAll('circle')
.data(humidity_data)
.enter()
.append('circle')
.attr('class', 'humidity_point')
.attr('cx', function(d) {
return x(d['date'])
})
.attr('cy', function(d) {
return y(d['Humidity'])
})
.attr('r', 4)
.attr('fill', '#428bca')
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html(d.Custody + '<br>' + d.City + ', ' + d.Country + '<br>' + d.Humidity + '%')
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(1500)
.style("opacity", 0);
});
This code allowed brushing, but retained the ability to hover over a circle element and see metadata.

Resources