Adding tooltip to donut slices - d3.js

I'm trying to add a tooltip to my donut's slices on moues over as follow:
var tooltip = select("g.arc--first")
//.append("div") // if I add this div here it stops working
.append("text")
firstSlice.append('path')
.attr('d', coolArc(firstRadius, thickness))
.attr('class', d => `fill_${weightScale(d.data.weight)}`)
.on('mouseover', (d) => {
tooltip.style("visibility", "visible")
.text(this.nodeByName.get(d.data.name)["Short Description"])
.style("fill", "red")
.style("position", "absolute")
.style("text-align", "center")
.style("width", "60px")
.style("height", "28px")
.style("padding", "2px")
.style("background", "lightsteelblue")
.style("border", "10px")
})
My goal is to have a tooltip similar to http://bl.ocks.org/d3noob/a22c42db65eb00d4e369 , but right now it only shows a red text at the middle of the page. I think I need to have a new div but when I try to add append("div") it stops working and doesn't show the text anymore. How should I fix it?

The tooltip from the example that you mentioned works pretty simply. It is appended as a child element for body (you try to append it as a child for g element, but you cannot append html elements into svg). So you should change you code:
var tooltip = select('body')
.append('div')
.attr('class', 'tooltip');
I also recommend you style this tooltip in your css (add appropriate class name and rules in your css file) you can avoid chaining many .style methods in this case. You should set position: absolute; rule - you can update top and left styles and position the tooltip where you need. Also set visibility: hidden rule - your tooltip should be hidden by default.
In mouseover event handler you need to change:
visibility style to show the tooltip
left and top styles to position the tooltip
text to update text on the tooltip
.on('mouseover', (d) => {
tooltip
.style('visibility', 'visible')
.style('left', d3.event.pageX + 'px')
.style('top', d3.event.pageY + 'px')
.text(d);
})
In mouseout event handler you should just hide the tooltip:
.on('mouseout', (d) => {
tooltip
.style('visibility', 'hidden')
});
See simplified demo in the hidden snippet below:
var tooltip = d3.select("body")
.append("div")
.attr('class', 'tooltip');
d3.selectAll('circle')
.data(['one', 'two', 'three'])
.on('mouseover', (d) => {
tooltip
.style('visibility', 'visible')
.style('left', d3.event.pageX + 'px')
.style('top', d3.event.pageY + 'px')
.text(d);
})
.on('mouseout', (d) => {
tooltip
.style('visibility', 'hidden')
})
.tooltip {
background-color: lightblue;
font-weight: bold;
padding: 5px;
border-radius: 9px;
position: absolute;
display: inline-block;
margin-top: -50px;
visibility: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.js"></script>
<svg width="720" height="120">
<circle cx="20" cy="60" r="10"></circle>
<circle cx="180" cy="60" r="10"></circle>
<circle cx="340" cy="60" r="10"></circle>
</svg>

Related

Adding div Element on d3 Bar Chart

I have a requirement to add div on top of each bar in bar chart using d3. Is it possible to add div instead of adding label. Can anyone help on this?
You can add a div to your chart and show/hide it, based on condition (for example when a user hovers over the element.
Here is a good example by d3noob.
The useful parts to look for in the code:
Define div styles in style tag
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;
}
Append div for the tooltip
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
Then show your tooltip on mouseover and hide on mouseout:
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html(formatTime(d.date) + "<br/>" + d.close)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});

d3.js Not able to Drag all rectangles

I have build the below code to create a customer vertical slider that can be plotted in an x and y axis plot.
I have taken input from a csv file and then using it to build the below output in d3.js.The code in d3.js is as below :
Edit 1: I am currently able to drag only the yellow rectangles only, but not all the rectangles. This is how the the modified graph looks like
Need your suggestions on -
1) How to drag all the rectangles individually
2) Keep the rectangle color belonging to a line same. For Example : all the rectangle with the first line will have same color and all the rectangles along the 2nd line will have another color and so on.
It will be of immense help, thanks !
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test Data</title>
<style>
div.tooltip {
position: absolute;
text-align: center;
width: 90px;
height: 32px;
padding: 1px;
font: 11px verdana;
background: rebeccapurple;
color: white;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
.grid .tick {
stroke: lightgrey;
opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
svg {
float: left;
border-bottom: solid 1px #ccc;
border-right: solid 1px #ccc;
margin-right: -1px;
margin-bottom: -1px;
}
rect {
opacity: 0.9;
}
svg {
float: left;
border-bottom: solid 1px #ccc;
border-right: solid 1px #ccc;
margin-right: -1px;
margin-bottom: -1px;
}
rect {
opacity: 0.9;
}
</style>
<script type="text/javascript" src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script type="text/javascript">
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.select(this).classed("dragging", true);
}
function dragged(d) {
d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
}
function dragended(d) {
d3.select(this).classed("dragging", false);
}
// load csv from the same directory
d3.csv("test.csv", function (data) {
data.forEach(function (d) {
x_value: +data.x_value; // convert to number with +
promotedprice: +data.promotedprice; // convert to number with +
nonpromotedprice: +data.nonpromotedprice;
avgprice: +data.avgprice;
brand: data.brand;
});
var svg = d3.select("body").append("svg")
.attr("width", 700)
.attr("height", 450)
// Attach the Promoted Price Rectangle
var g = svg.selectAll("rect")
.data(data)
.enter()
.append("g")
.classed('rect', true)
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var accent = d3.scaleOrdinal(d3.schemeAccent);
/*.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragend));*/
/*g.append("rect")
.attr("width", 18)
.attr("height", function (d) { return (d.nonpromotedprice - d.promotedprice) + 100; })
.attr("x", function (d) { return d.x_value; })
.attr("y", function (d) { return (d.nonpromotedprice - d.promotedprice) / 2; })
.attr("fill", "#E6EAEE")
*/
g.append("line") // attach a line
.style("stroke", "gray")
.style("stroke-width", 12) // colour the line
.attr("x1", function (d) { return d.x_value; }) // x position of the first end of the line
.attr("y1", function (d) { return d.nonpromotedprice; }) // y position of the first end of the line
.attr("x2", function (d) { return d.x_value; }) // x position of the second end of the line
.attr("y2", function (d) { return d.promotedprice; });
g.append("rect")
.attr("width", 20)
.attr("height", 15)
.attr("x", function (d) { return d.x_value - 10; })
.attr("y", function (d) { return d.promotedprice; })
.attr("fill", "#F9EA55")
g.append("rect")
.attr("width", 20)
.attr("height", 15)
.attr("x", function (d) { return d.x_value - 10; })
.attr("y", function (d) { return d.nonpromotedprice; })
.attr("fill", "#ED8B00")
g.append("rect")
.attr("width", 20)
.attr("height", 15)
.attr("x", function (d) { return d.x_value - 10; })
.attr("y", function (d) { return d.avgprice; })
.attr("fill", "#28468E")
;
g.selectAll("rect")
.style('cursor', 'pointer');
g.selectAll("text")
.data(data)
.enter()
.insert("text", "line")
.text(function (d) { return d.brand; })
.style("text-anchor", "top")
.style("fill", "red")
.style("font-family", "Arial")
.style("font-size", 34);
function dragstarted(d) {
d3.select(this).raise().classed("active", true);
}
function dragged(d) {
d3.select(this).select("rect")
//.attr("x", d.x = d3.event.x)
.attr("y", d.y = d3.event.y);
}
function dragended(d) {
d3.select(this).classed("active", false);
}
});
</script>
</body>
Thanks in Advance

D3 - Legend in seperate div element

I have a plunker here - https://plnkr.co/edit/inW1NfaUwechJt8C1hC9?p=preview
I'm trying to create a legend for this graph
I can do it by adding the legend to the svg but Id like more control over the styling and position
Is it possible to create the legend in a separate div element and use something like a ul list.
var legend = d3.select(".legend")
.data(colors)
.enter()
.append("g")
.attr("class", "legend")
.attr("transform", (d, i) => {
return "translate(20," + i * 35 + ")";
});
Yes, Based on your comment it appears as though a html list approach is what you want, I'll answer for that approach.
Rather than appending a g for each legend item, append li to a list. You can also append the div and ul with d3, just as any svg component. The only major change is appending the right type of element and setting its relevant properties - as this will be different with html than svg.
As with svg components, you can style with css or .attr/.style methods (I use both below).
Here's an example:
var data = [{"name":"Category 1", "value":1},{"name":"Category 2", "value":2},{"name":"Category 3", "value":3},{"name":"Category 4", "value":4},{"name":"Category 5", "value":5}];
var scale = d3.scaleLinear()
.range(["red","orange"])
.domain([1,5]);
var divLegend = d3.select("#divLegend");
divLegend.append("p")
.html("Legend Title")
.style("text-align","center")
.style("font-size","20px")
var list = divLegend.append("ul");
var entries = list.selectAll("li")
.data(data)
.enter()
.append("li");
// append rectangle and text:
entries.append("span")
.attr("class","rect")
.style("background-color", function(d) { return scale(d.value); })
entries.append("span")
.attr("class","label")
.html(function(d) { return d.name; })
ul {
list-style-type: none;
}
.rect {
width: 10px;
height: 10px;
display: inline-block;
}
.label {
margin-left: 10px;
}
#divLegend {
width: 150px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
<div id="divLegend"></div>

D3: Mouseover to display data in top right corner

I'm working in D3 with a tooltip in a mouseover event. What I have is the data displayed slightly to the right and above the dot. However, it's a line graph and this is sometimes unreadable. What I would like is the data displayed in some fixed position (I've decided the upper right hand corner is free of clutter). Can anyone give me some advice? Code below
.on("mouseover", function(d) {
d3.select(this).attr("r", 5.5).style("fill", "red");
tooltip.transition()
.duration(200)
.style("opacity", .9);
tooltip.html("(" + xTemp(d)
+ ", " + yTemp(d)+ ")")
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 28) + "px");
I assume your tooltip is a <div> with position: absolute.
In that case, you can wrap your SVG in a container div (since SVGs don't support offsetTop and offsetLeft) and set the position of your tooltip using offsetTop and offsetLeft.
Here is a simple demo. I put two paragraphs ("Foo" and "Bar"), so you can see that the position of the <div> is correct, even if the SVG is not the first element on the page:
var svg = d3.select("svg");
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
svg.selectAll(null)
.data(d3.range(50, 300, 50))
.enter()
.append("circle")
.attr("cx", Number)
.attr("cy", 100)
.attr("r", 20)
.style("fill", "teal")
.on("mouseover", function(d) {
d3.select(this).style("fill", "red");
tooltip.html(d)
.style("top", svg.node().parentNode.offsetTop + 10 + "px")
.style("left", svg.node().parentNode.offsetLeft + 270 + "px")
.style("opacity", .9);
})
.on("mouseout", function() {
d3.select(this).style("fill", "teal")
tooltip.style("opacity", 0)
})
svg {
border: 1px solid gray;
}
.tooltip {
position: absolute;
broder: 1px solid black;
background-color: tan;
padding: 2px;
border-radius: 2px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<p>Foo</p>
<p>Bar</p>
<div>
<svg id="svg"></svg>
</div>

D3: display graph in tooltip

I know it is possible to display an image in a D3 tooltip. What I am trying to do is to display a bar graph in a tooltip (i.e when the mouse hovers over the object a bar graph appears). I have adapted code from http://bl.ocks.org/jarobertson/1483052#gistfile1.html and combined it with the bar graph code by Robert Lewand. And well, it doesn't work. I dont even get any errors in the console that could perhaps put me on the right path. Is it possible to do? Here is my code:
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<head>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?1.27.1"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<style type="text/css">
div.tooltip {
position: absolute;
text-align: center;
width: 500px;
height: 550px;
padding: 8px;
font: 10px sans-serif;
background: #ddd;
border: solid 1px #aaa;
border-radius: 8px;
pointer-events: none;
}
.chart rect {
fill: steelblue;
}
.chart text {
fill: white;
font: 10px sans-serif;
text-anchor: middle;
}
</style>
</head>
<body>
<script type="text/javascript">
var w = 960,
h = 500;
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
svg.append("svg:g")
.attr("transform", "translate(480,50)rotate(60)scale(2)")
.append("svg:rect")
.attr("width", 140)
.attr("height", 140)
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseout", mouseout);
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 1e-6);
function mouseover() {
div.transition()
.duration(500)
.style("opacity", 1);
}
// where the tooltip previosly contained an image
function mousemove() {
div
.html("<h1>Bar Graph</h1><br> <svg class='chart'></svg>")
.style("left", (d3.event.pageX - 34) + "px")
.style("top", (d3.event.pageY - 12) + "px");
}
function mouseout() {
div.transition()
.duration(500)
.style("opacity", 1e-6);
}
// make bar graph
var width = 300,
height = 300;
var y = d3.scale.linear()
.range([height, 0]);
var chart = d3.select(".chart")
.attr("width", width)
.attr("height", height);
d3.tsv("data.tsv", type, function(error, data) {
y.domain([0, d3.max(data, function(d) { return d.value; })]);
var barWidth = width / data.length;
var bar = chart.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(" + i * barWidth + ",0)"; });
bar.append("rect")
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.attr("width", barWidth - 1);
bar.append("text")
.attr("x", barWidth / 2)
.attr("y", function(d) { return y(d.value) + 3; })
.attr("dy", ".75em")
.text(function(d) { return d.value; });
});
function type(d) {
d.value = +d.value; // coerce to number
return d;
}
</script>
</body>
</html>
Thanks in advance!
apologies, the data.tsv file contains the following:
Sentiment value
Strongly positive 211
Positive 222
Neutral 654
Negative 618
Strongly negative 343
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?1.27.1"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<style type="text/css">
div.tooltip {
position: absolute;
text-align: center;
width: 500px;
height: 550px;
padding: 8px;
font: 10px sans-serif;
background: #ddd;
border: solid 1px #aaa;
border-radius: 8px;
pointer-events: none;
}
.chart rect {
fill: steelblue;
}
.chart text {
fill: white;
font: 10px sans-serif;
text-anchor: middle;
}
</style>
<script type="text/javascript">
var w = 960,
h = 500;
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
svg.append("svg:g")
.attr("transform", "translate(480,50)rotate(60)scale(2)")
.append("svg:rect")
.attr("width", 140)
.attr("height", 140)
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseout", mouseout);
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 1e-6);
function mouseover() {
div.transition()
.duration(500)
.style("opacity", 1);
}
// where the tooltip previosly contained an image
function mousemove() {
div
.html("<h1>Bar Graph</h1><br> <svg class='chart'></svg>")
.style("left", (d3.event.pageX - 34) + "px")
.style("top", (d3.event.pageY - 12) + "px");
}
function mouseout() {
div.transition()
.duration(500)
.style("opacity", 1e-6);
}
// make bar graph
var width = 300,
height = 300;
var y = d3.scale.linear()
.range([height, 0]);
var chart = d3.select(".chart")
.attr("width", width)
.attr("height", height);
d3.tsv("data.tsv", type, function(error, data) {
y.domain([0, d3.max(data, function(d) { return d.value; })]);
var barWidth = width / data.length;
var bar = chart.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(" + i * barWidth + ",0)"; });
bar.append("rect")
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.attr("width", barWidth - 1);
bar.append("text")
.attr("x", barWidth / 2)
.attr("y", function(d) { return y(d.value) + 3; })
.attr("dy", ".75em")
.text(function(d) { return d.value; });
});
function type(d) {
d.value = +d.value; // coerce to number
return d;
}
</script>
'data.tsv' file is not with us,
and we have written only
function mousemove() {
div
.html("<h1>Bar Graph</h1><br> <svg class='chart'></svg>")
.style("left", (d3.event.pageX - 34) + "px")
.style("top", (d3.event.pageY - 12) + "px");
}
above function will place 'Bar Graph' text and one svg element in tooltip.
Hope you will get it.
If not ask for more......

Resources