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>
This how I am setting color range for my map
var color = d3.scaleThreshold()
.domain([1,1000])
.range(d3.schemeCategory20b);
My values in data which I have set in domain does not exceed 1000, so i have given domain range from 1 to 1000 on setting this range it is not displaying color as expected, like I wanna show my map with colors like domain
range [1,50] -> green & [51,250] -> yellow,[251,700] -> dark green &
[701,1000] -> yellow
How can I do that? Any ideas?
In a threshold scale, if the range has N values the domain has to have N - 1 values.
Therefore, just tell the scale exactly what you've told us in the question:
var color = d3.scaleThreshold()
.domain([50, 250, 700])
.range(["green", "yellow", "darkgreen", "yellow"]);
Here is a basic demo, hover over each rectangle to see its value:
var body = d3.select("body");
var color = d3.scaleThreshold()
.domain([50, 250, 700])
.range(["green", "yellow", "darkgreen", "yellow"]);
body.selectAll(null)
.data(d3.range(51).map(d => d * 20))
.enter()
.append("div")
.style("background-color", d => color(d))
.on("mouseover", function(d) {
console.log("value: " + d + ", color: " + color(d))
})
div {
min-width: 5px;
min-height: 20px;
display: inline-block;
margin: 1px;
border: 1px solid gray;
}
.as-console-wrapper { max-height: 30% !important;}
<script src="https://d3js.org/d3.v4.min.js"></script>
I am very new to programming in d3.js, and I was wondering if someone could help me out with the following:
I have modified a donut chart that I found on the web. I modified it to become responsive while still maintaining an aspect ratio of 1 for the circular donut chart. I was wondering what the best way would be to place a title above or below it. I have tried various approaches but I am unable to create one that works consistently.
Simplified JSFiddle here:
JSFiddle
code also attached here:
var width = 400;
var height = 400;
var x = {
value: 80.43,
color1: "#007EA7",
color2: "#d9d9d9"
};
var margin = {
left: 10,
top: 10,
right: 10,
bottom: 10
};
width = Math.min(width - margin.left - margin.right,
height - margin.top - margin.bottom);
height = width * 1; // Should be a perfect circle.
svg = d3.select("body")
.append("div")
.classed("svg-container", true)
.append("svg")
.attr("preserveAspectRatio", "xMidYMin meet")
.attr("viewBox", "0 0 " + (width) + " " + (height))
.classed("svg-content-responsive", true);
// Below is code to create the actual donut chart. It used the width and height attributes calculated above.
var outerRadius = Math.min(width, height) / 2,
innerRadius = (outerRadius / 5) * 4;
τ = 2 * Math.PI;
fontSize = (Math.min(width, height) / 5);
arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius)
.startAngle(0);
var background = svg.append("path")
.datum({
endAngle: τ
})
.style("fill", x.color2)
.attr("d", arc)
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var text = svg.append("text")
.text(Math.round(x.value * 10) / 10 + '%')
.attr("text-anchor", "middle")
.style("font-size", fontSize + 'px')
.attr("dy", height / 2 + fontSize / 2.5)
.attr("dx", width / 2);
foreground = svg.append("path")
.datum({
endAngle: x.value / 100 * τ
})
.style("fill", x.color1)
.attr("d", arc)
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
.svg-container {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
}
.svg-content-responsive {
width: 100%;
height: 100%;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
Any comments, advise, references or code snippets that might help me are greatly appreciated.
Thanks,
Florian
Is there something wrong with adding something like this:
var title = svg.append("text")
.text('This is the title')
.attr("text-anchor", "middle")
.style("font-size", '12px')
.attr("dy", 10)
.attr("dx", width / 2);
exactly like the text?
PS: adding your approaches that failed would help people answer the question
If anyone stumbles on this question in the future: I decided to place my title below the chart and managed to solve it by slightly cheating.
I used the following CSS:
.svg-container {
height:100%;
width:100%;
min-height: 100%;
position:relative;
}
.svg-content-responsive {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
height:100%;
width: 100%;
position: relative;
padding-bottom: 50px;
}
.svg-gauge-title-container {
width: 100%;
height: 50px;
position:relative;
margin-top:-50px;
}
.svg-gauge-title {
position: relative;
width: 100%;
}
i.e. I added 50px of padding-bottom to the chart, and also added a title container with 50px of margin above. Might not be the cleanest way, but it works.
I am using CSS to show font size and background color on mouseover on text in D3.js
d3.select(this).append("text")
.classed("hover", true)
.attr('transform', function(d){
return 'translate(5, 50)';
})
.text(d.name);
"hover" class is not appling, its just displaying simple text
here is my CSS class
text.hover {
position: absolute;
text-align: left;
background-color: #FFFFEF;
width: 400px;
height: 135px;
padding: 10px;
border: 1px solid #D5D5D5;
font-family: arial,helvetica,sans-serif;
position: absolute;
font-size: 1.1em;
color: #333;
padding: 10px;
border-radius: 3px;
background-color: rgba(255,255,255,0.9);
color: #000;
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
-moz-box-shadow: 0 1px 5px rgba(0,0,0,0.4);
border:1px solid rgba(200,200,200,0.85);
}
What is the best way to apply CSS on text
The way you are handling the text is like you want it to be a div. You are using the wrong attributes, how can text have a background fill ?
I have edited the fiddle provided and shown that the class does work : https://jsfiddle.net/u1gpny6o/1/
All I have put in the hover class is a fill like so :
.hover {
fill:red;
}
What is it you're trying to do ? Is it create a div with text ? If so create a div, give it the class you have in the question, and append text to that div, does that make sense ?
EDIT:
From your comments I have come up with this : https://jsfiddle.net/u1gpny6o/3/
From this question (not the selected answer but the second one) : Show data on mouseover of circle
I have made a tooltip div like so :
var tooltip = d3.select("body")
.append("div")
.classed('hover', true)
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.text("a simple tooltip");
So you can edit attributes in the css :
.hover {
background: #FFFFEF;
width: 400px;
height: 135px;
padding: 10px;
stroke: black;
stroke-width: 2px;
}
And it will appear on mouseover, move on mousemove(to mouse coordinates, this can be edited) and disappear on mouseout :
.on("mouseover", function(d) {
tooltip.text(d.name)
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");
});
What you were doing previously made no sense. You had the class hover and you were duplicating attributes in the CSS, for example, setting color twice and so on. Also giving the elements attributes you can't give. For example, SVG elements can't have a border but can have a stroke and so on.
As you mentioned, you want to load the visualization in a pop up window. I would do it like so :
function update(id, data){
var container = d3.select('#'+id) // then use this to append your vis to
. //the rest of the code the create the vis
.
.
.
}
And then when you hover over the node, just pass the id of the pop up to the update function along with the data (if need be) like so :
update(popupid, data);
It's not working likely because of the way you set up the hover class (doing text.hover) and how you are attaching it. Why not attach a mouseover listener to text and then assign the class:
Define your hover CSS class:
`.hover { ...styles ...}`
Use it on mouseover:
`select your text
.on("mouseover", function() {
select text
.classed("hover",true);
});`
Then you can reverse it similarly with mouseout.
I have a multi series donut chart created with the help of this question D3.js - Donut charts with multiple rings in d3.js, see fiddle below.
I'd like to be able to add hover effects, and also make each part clickable in the sense I'd like to assign a certin href to each slice of the chart. I have looked around quite a bit, but can't get my head around it - d3.js is quite complex for me I guess.
The code I have now: http://jsfiddle.net/mephisto73/o6shxw0d/
(function(){
var $container = $('.chart-container'),
τ = 2 * Math.PI,
width = $container.width(),
height = $container.height(),
outerRadius = Math.min(width,height)/2.5,
innerRadius = (outerRadius/5)*4,
fontSize = (Math.min(width,height)/4);
var dataset = {
weeks: [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
months: [1,1,1,1,1,1,1,1,1],
trimester: [1,1,1]
};
var color = d3.scale.ordinal() .range(['rgba(141,211,199,0.8)','rgb(255,255,179)','rgb(190,186,218)','rgb(251,128,114)','rgb(128,177,211)','rgb(253,180,98)','rgb(179,222,105)','rgb(252,205,229)','rgb(217,217,217)','rgb(188,128,189)','rgb(204,235,197)','rgb(255,237,111)']);
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc();
var svg = d3.select('.chart-container').append("svg")
.attr("width", '100%')
.attr("height", '100%')
.attr('viewBox','0 0 '+Math.min(width,height) +' '+Math.min(width,height) )
.attr('preserveAspectRatio','xMinYMin')
.append("g")
.attr("transform", "translate(" + Math.min(width,height) / 2 + "," + Math.min(width,height) / 2 + ")");
var gs = svg.selectAll("g").data(d3.values(dataset)).enter().append("g").attr("class", "arc");
var path = gs.selectAll("path")
.data(function(d) { return pie(d); })
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", function(d, i, j) { return arc.innerRadius(innerRadius+(40*j)).outerRadius(innerRadius+(5*(j+5)))(d); });
});
$(function(){
var tooltip = d3.select(".tooltip");
var $container = $('.chart-container'),
τ = 2 * Math.PI,
width = $container.width(),
height = $container.height(),
outerRadius = Math.min(width,height)/2.5,
innerRadius = (outerRadius/5)*4,
fontSize = (Math.min(width,height)/4);
var dataset = {
weeks: [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
months: [1,1,1,1,1,1,1,1,1],
trimester: [1,1,1]
};
var color = d3.scale.ordinal() .range(['rgba(141,211,199,0.8)','rgb(255,255,179)','rgb(190,186,218)','rgb(251,128,114)','rgb(128,177,211)','rgb(253,180,98)','rgb(179,222,105)','rgb(252,205,229)','rgb(217,217,217)','rgb(188,128,189)','rgb(204,235,197)','rgb(255,237,111)']);
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc();
var svg = d3.select('.chart-container').append("svg")
.attr("width", '100%')
.attr("height", '100%')
.attr('viewBox','0 0 '+Math.min(width,height) +' '+Math.min(width,height) )
.attr('preserveAspectRatio','xMinYMin')
.append("g")
.attr("transform", "translate(" + Math.min(width,height) / 2 + "," + Math.min(width,height) / 2 + ")");
var gs = svg.selectAll("g").data(d3.values(dataset)).enter().append("g").attr("class", "arc");
var path = gs.selectAll("path")
.data(function(d) { return pie(d); })
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", function(d, i, j) { return arc.innerRadius(innerRadius+(40*j)).outerRadius(innerRadius+(5*(j+5)))(d); })
.on("mousemove", function(d){
tooltip.style("left", d3.event.pageX+10+"px");
tooltip.style("top", d3.event.pageY-25+"px");
tooltip.style("display", "inline-block");
tooltip.select("span").text("Value: "+d.value);
}).on("mouseout",function(){
tooltip.style("display","none");
}).on("click",function(){
//write code to open
});
});
html,body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin:0;
padding:0;
width:100%;
height:100%;
}
text {
font: 10px sans-serif;
}
form {
position: absolute;
right: 10px;
top: 10px;
}
.arc path {
stroke: #fff;
width:5px;
}
.arc path:hover {
background-color:#ccc;
}
.chart-container {
width:70%;
height:70%;
border: 1px dotted silver;
}
svg text{
font-size: 1em;
font-family: sans-serif;
}
.tooltip{
position: absolute;
display: none;
width: auto;
height: auto;
background: none repeat scroll 0 0 white;
border: 0 none;
border-radius: 8px 8px 8px 8px;
box-shadow: -3px 3px 15px #888888;
color: black;
font: 12px sans-serif;
padding: 5px;
text-align: center;
}
path:hover{
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.10/d3.min.js"></script>
<div class="chart-container"></div>
<div class='tooltip'>
<span></span>
</div>
I've added functionality for on mousemove, mouseout and click.
Try to read and do the modification in click function.
Hope you got it,If not ask me for more.