I am attempting to add a simply bar chart to my tooltip; it consists of 2 variables -- men and women. I was hoping someone might be able to help me put this inside of the tooltip instead of appending it to where it is currently being appended. I've given this a particular area to be appended just so that I know that it is, in fact, showing up(which it is), but I don't know how to get it into the tool tip. Any help is much appreciated. Oh, and this needs to be done in d3, which is partial to why I am asking this question -- I saw a similar question that wasn't implemented in pure d3, and I couldn't completely follow what was going on to emulate it in this example.
.on("mouseover", function(d)
{
tip.show(d);
var state = d.properties.name;
var men = d.properties.men;
var women = d.properties.women;
var dataset = [men, women];
var barHeight = 20;
var x = d3.scale.linear()
.domain([0, d3.max(dataset)])
.range([0, width/2]);
var chart = d3.select(".chart")
.attr("width", width)
.attr("height", barHeight * dataset.length);
var bar = chart.selectAll("g")
.data(dataset)
.enter().append("g")
.attr("transform", function(d, i)
{
return "translate(0," + i * barHeight + ")";
});
bar.append("rect")
.attr("width", x)
.attr("height", barHeight - 1);
bar.append("text")
.attr("x", function(d)
{
return x(d)/2+5;
})
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(function(d)
{
return "$" + d;
});
})
Since you didn't shared the whole code to create the chart, this answer will deal with your question's title only:
How to create a chart inside a tooltip?
I'm not a d3.tip() user, since I create my own tooltips. But what you want is not complicated at all: As the tooltips are <div> elements, you can definitely add a SVG inside them.
However, you have to know where to create the SVG. So, in the following demo, I'm creating this d3.tip tooltip:
var tool_tip = d3.tip()
.attr("class", "d3-tip")
.offset([20, 120])
.html("<p>This is a SVG inside a tooltip:</p>
<div id='tipDiv'></div>");
//div ID here --^
The important part here is this: there is a inner <div> inside the d3.tip div, with a given ID (in that case, tipDiv). I'm gonna use that ID to create my SVG inside the tooltip:
selection.on('mouseover', function(d) {
tool_tip.show();
var tipSVG = d3.select("#tipDiv")
//select the div here--^
.append("svg")
//etc...
})
Here is the demo:
var svg = d3.select("body")
.append("svg")
.attr("width", 300)
.attr("height", 300);
var tool_tip = d3.tip()
.attr("class", "d3-tip")
.offset([20, 120])
.html("<p>This is a SVG inside a tooltip:</p><div id='tipDiv'></div>");
svg.call(tool_tip);
var data = [14, 27, 19, 6, 17];
var circles = svg.selectAll("foo")
.data(data)
.enter()
.append("circle");
circles.attr("cx", 50)
.attr("cy", function(d, i) {
return 20 + 50 * i
})
.attr("r", function(d) {
return d
})
.attr("fill", "teal")
.on('mouseover', function(d) {
tool_tip.show();
var tipSVG = d3.select("#tipDiv")
.append("svg")
.attr("width", 200)
.attr("height", 50);
tipSVG.append("rect")
.attr("fill", "steelblue")
.attr("y", 10)
.attr("width", 0)
.attr("height", 30)
.transition()
.duration(1000)
.attr("width", d * 6);
tipSVG.append("text")
.text(d)
.attr("x", 10)
.attr("y", 30)
.transition()
.duration(1000)
.attr("x", 6 + d * 6)
})
.on('mouseout', tool_tip.hide);
.d3-tip {
line-height: 1;
padding: 6px;
background: wheat;
border-radius: 4px solid black;
font-size: 12px;
}
p {
font-family: Helvetica;
}
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.7.1/d3-tip.min.js"></script>
<p>Hover over the circles:</p>
Related
possible duplicates: D3 text on mouseover
D3 donut chart text centering
but unsure what is happening in respect to my problem and quite stuck.
Im building a data visualization with many layouts. I am currently trying to make a piechart with text centered in the middle and whenever someone mouse overs the arcs, it displays the text of it in the center.
function GUP_PieRender() {
var svg = d3.select(targetDOMelement).append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(pie(dataset))
.enter().append("g")
.attr("class", "arc")
.on("mouseover", function(d) { d3.select("text").text(d.data.ResearchArea)}); //Problem area
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.ResearchArea); });
g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle");
}
What it is doing instead is displaying the text in another D3 barchart layout that has text. So I must be calling the mouseover event too early and appending it to the last text element in that?
Can I get a remedy?
Thanks.
The problem here (inside your "mouseover" handler) is simply this:
d3.select("text")
When you do this, D3 selects the first text element it finds in that page. You don't want that, obviously.
Therefore, just do:
g.select("text")
That way, you only select text elements inside your g selection.
Alternatively, you can also do:
d3.select(this).select("text")
Since this in this context is the group element.
Here is a demo (I'm trying to imitate your code):
var data = ["foo", "bar", "baz"];
data.forEach(function(d) {
render(d);
})
function render(data) {
var svg = d3.select("body")
.append("svg")
.attr("width", 100)
.attr("height", 100);
var g = svg.selectAll(null)
.data([data])
.enter()
.append("g")
.on("mouseover", function(d) {
g.select("text").text(String)
})
.on("mouseout", function(d) {
g.select("text").text(null)
})
g.append("circle")
.attr("cx", 50)
.attr("cy", 50)
.attr("r", 20);
g.append("text")
.attr("x", 25)
.attr("y", 20);
}
svg {
background-color: tan;
border: 1px solid darkgray;
margin-right: 10px;
}
circle {
fill: teal;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
I am new to java script and I am trying to draw a radar chart. Everything works well except title. Can u please tell me what is wrong? I attached the code below. First I create text var and call it to show.
<style>
body {
overflow: hidden;
margin: 0;
font-size: 14px;
font-family: "Helvetica Neue", Helvetica;
}
#chart {
position: absolute;
top: 60px;
left: 20px;
}
</style>
<script type="text/javascript" src="<c:url value='/js/radar.js'/>"></script>
<div id="body">
<div id="chart"></div>
</div>
<script>
var w = 200;
var h = 200;
var colorscale = d3.scale.category10();
//Legend, titles
var LegendOptions = ['Try Count','Succcess Count', 'Success Rate'];
////////////////////////////////////////////
/////////// Initiate legend ////////////////
////////////////////////////////////////////
var svg = d3.select('#body')
.selectAll('svg')
.append('svg')
.attr("width", w+300)
.attr("height", h)
//Create the title for the legend
var text = svg.append("text")
.attr("class", "title")
.attr('transform', 'translate(90,0)')
.attr("x", w - 70)
.attr("y", 10)
.attr("font-size", "12px")
.attr("fill", "#404040")
.text("What % of owners use a specific service in a week");
//Initiate Legend
var legend = svg.append("g")
.attr("class", "legend")
.attr("height", 100)
.attr("width", 200)
.attr('transform', 'translate(90,20)')
;
//Create colour squares
legend.selectAll('rect')
.data(LegendOptions)
.enter()
.append("rect")
.attr("x", w - 65)
.attr("y", function(d, i){ return i * 20;})
.attr("width", 10)
.attr("height", 10)
.style("fill", function(d, i){ return colorscale(i);})
;
//Create text next to squares
legend.selectAll('text')
.data(LegendOptions)
.enter()
.append("text")
.attr("x", w - 52)
.attr("y", function(d, i){ return i * 20 + 9;})
.attr("font-size", "11px")
.attr("fill", "#737373")
.text(function(d) { return d; })
;
//Options for the Radar chart, other than default
var mycfg = {
w: w,
h: h,
maxValue: 0.6,
levels: 6,
ExtraWidthX: 300
}
Try changing:
var svg = d3.select('#body')
.selectAll('svg')
.append('svg')....
To:
var svg = d3.select('#body')
.append('svg')....
As you are only appending one svg, you do not need the selectAll() portion. The following takes your code and makes that one change:
var w = 200;
var h = 200;
var svg = d3.select('#body')
.append('svg')
.attr("width", w+300)
.attr("height", h)
var text = svg.append('g').append("text")
.attr("class", "title")
.attr('transform', 'translate(90,0)')
.attr("x", w - 70)
.attr("y", 10)
.attr("font-size", "12px")
.attr("fill", "#404040")
.text("What % of owners use a specific service in a week");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="body"></div>
I am trying to adopt bar chart example here to understand data joins in d3 v4. Enter selection works fine but I am unable to figure out how to update. Here is what I have so far: https://jsfiddle.net/hackygkL/
Can someone please help me.
var width = 420,
barheight = 30;
var svg = d3.select('#bar-chart')
.append('svg')
.attr('width', width + 50)
.attr('height', 1000);
var scale = d3.scaleLinear()
.range([0, width]);
function createBar(data) {
scale.domain([0, d3.max(data)]);
var barGroups = svg.selectAll('g')
.data(data, function(d){return d;});
barGroups.exit().remove();
var enterGroup = barGroups.enter() //ENTER
.append('g')
.merge(barGroups) //UPDATE
.attr("transform", function(d, i){
return "translate(0, " + barheight * i + ")";
});
var bars = barGroups.selectAll('rect');
enterGroup.append('rect') //ENTER
.attr('class', 'bar')
.attr('height', barheight - 1)
.merge(bars) //UPDATE
.attr('width', function(d){
return scale(d);
})
.attr('fill', 'steelblue');
var texts = barGroups.selectAll('text');
enterGroup.append("text") //ENTER
.attr('class', 'text')
.attr("y", barheight / 2)
.attr("dy", ".35em")
.merge(texts) //UPDATE
.attr("x", function(d) { return scale(d) + 10; })
.text(function(d) { return d;});
}
Seems to work after merging the groups first, then updating the rects and texts:
https://bl.ocks.org/ckothari/699b112b6e1376779e65973bbabdced6
I am trying to figure out how to show more text on a pie chart using mouseover than just the data that is bound to the pie. Below is my functional code
function Pie(value,names){
svg.selectAll("g.arc").remove()
var outerRadius = 100;
var innerRadius = 0;
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var pie = d3.layout.pie();
var color = d3.scale.category10();
var arcs = svg.selectAll("g.arc")
.data(pie(value))
.enter()
.append("g")
.attr("class", "arc")
.attr("transform", "translate(950,80)");
arcs.append("path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc)
.on("mouseover",function(d,i) {
arcs.append("text")
.attr("dy", ".5em")
.style("text-anchor", "middle")
.style("fill", function(d,i){return "black";})
.text(d.data)
})
.on("mouseout", function(d) {
arcs.select("text").remove();
});}
The names array has the same length as the value array which is passed to the pie. I really hoped that something like this would work by replacing the above mouseover.
.on("mouseover",function(d,i) {
arcs.append("text")
.attr("dy", ".5em")
.style("text-anchor", "middle")
.style("fill", function(d,i){return "black";})
.text(function(d,i){return (d.data +" " + names[i]);)
})
But the only thing it does is to show all the elements of the values array stacked one on top of the other and the last element of the names array. It seems that i is always the last index in this case. How would I go about that? Could I show the text I want in another way? Thank you in advance.
First, the variable arcs is a data-bound d3 selection which represents all the arcs of the pie. So, by calling arcs.append, you are going to append a text element for each piece of your pie chart. I think you mean to only append one text element based on what you moused-over so re-write that as:
svg.append('text')
...
Second, in this expression:
.text(function(d,i){return (d.data +" " + names[i]);)
d and i in the mouseover function already represent the data and index of the pie slice being moused over. There is no reason to wrap this in another function and should be re-written:
.text(d.data +" " + names[i]);
Here's a complete example:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.arc path {
stroke: #fff;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var color = d3.scale.category10();
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var data = [{
value: Math.random(),
}, {
value: Math.random(),
}, {
value: Math.random(),
}, {
value: Math.random(),
}, {
value: Math.random(),
}]
var names = ["A","B","C","D","E"];
var arcs = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
arcs.append("path")
.attr("d", arc)
.style("fill", function(d,i) {
return color(i);
})
.on("mouseover", function(d, i) {
console.log(d);
svg.append("text")
.attr("dy", ".5em")
.style("text-anchor", "middle")
.style("font-size", 45)
.attr("class","label")
.style("fill", function(d,i){return "black";})
.text(names[i]);
})
.on("mouseout", function(d) {
svg.select(".label").remove();
});
</script>
</body>
</html>
I spent hours trying to figure out why my code was not working. I then arbitrarily moved my button code from after the D3 code (at the end between </script> and </body>) to the top (between <script type="text/javascript"> and <body>). It works now, but I don't know why. I don't want to make this mistake again or confuse myself in the future.
<body>
<button>Update</button>
<script type="text/javascript">
var w = 500;
var h = 500;
var barPadding = 1;
var dataset = [ ];
for (var i = 0; i < 14; i++) {var newNumber = Math.round(Math.random() * 70);
dataset.push(newNumber);}
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
//Create Scales for Data conversion
var xScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d,i) {return d;})]) //input
.range([0,w]); // output
var yScale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, h], 0.05); //Vertical separation + barpadding
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", 3)
.attr("y", function (d,i) {return yScale(i);})
.attr("width", function(d,i) {return xScale(d);})
.attr("height", yScale.rangeBand())
.attr("fill", function(d) {return "rgb(" + (d * 10) + ", 0,0 )";});
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {return d;})
.attr("x", function(d) {return xScale(d) -15;})
.attr("y", function(d, i) {return yScale(i) +5 +yScale.rangeBand() / 2;})
.attr("fill", "white")
.attr("font-family", "sans-serif")
.attr("text-anchor", "middle");
//Create Data Update and transition
d3.select("button")
.on("click", function() {
//New values for dataset
dataset = [ ];
for (var i = 0; i < 14; i++) {var newNumber = Math.round(Math.random() * 70);
dataset.push(newNumber);}
//Update all rects, and color gradient
svg.selectAll("rect")
.data(dataset)
.transition()
.attr("x", 3)
.attr("width", function(d,i) {return xScale(d);})
.attr("fill", function(d) {return "rgb(" + (d * 10) + ", 0,0 )";});
//Update text label and position
svg.selectAll("text")
.data(dataset)
.text(function(d) {return d;})
.attr("x", function(d) {return xScale(d) -15;})
.attr("y", function(d, i) {return yScale(i) +5 + yScale.rangeBand() / 2;});
});
</script>
</body>
If you're saying that the code as shown in your question works, with the <button> element before the <script> element, it's because <script> elements are executed as the browser encounters them, top-to-bottom while parsing the page. Which means that any JavaScript that you use is only able to reference elements that are higher in the page source because the browser doesn't know about the later elements yet.
Unless you have code within functions that don't get called until after the DOM is complete, for example if you assign a DOM ready or onload event handler, or a delegated click handler or something.