This question already has an answer here:
use of "d" in function literal in D3?
(1 answer)
Closed 7 years ago.
I'm trying to understand this code:
var w = 900;
var h = 200;
var barPadding = 1;
var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];
//Create SVG element
var svg = d3.select("div")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * (w / dataset.length);
})
.attr("y", function(d) {
return h - (d * 4);
})
.attr("width", w / dataset.length - barPadding)
.attr("height", function(d) {
return d * 4;
})
I can't figure out what does 'd' and 'i' mean as input parameters inside callback functions. Probably it's very simply.
When you give an array of elements for the data() function, d3 iterates it for you when you do the enter() call. In the callback what d, i means is an element from the dataset array and its index.
When you write:
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
D3 creates a bunch of <bar> elements, one for each entry in the array (for each entry the associated data along with index on the original array is given by d, i). More importantly, it also associates the data for each entry in the array with that DOM element, as a data property.
d is the data value being rendered, i is the index of that data value in the data array.
Therefore, when rendering the first data point, d is 5 , and i is 0
Related
Can somebody please explain the use of "i". I do understand x, y, width and height. I also understand i is an index, but what exactly it does? If I'm changing my example to i * 2 or i * 10, rectangle just getting wider but remains single, i * 21 makes or i * 42 diving it into multiple rectangles.
//D3 goes under here
var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];
var w = 500;
var h = 100;
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h)
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.text(function(d){
return d;
})
.attr("x", function(d, i){
return i * 21;
})
.attr("y", 0)
.attr("width", 20)
.attr("height", 100)
</script>
The second argument, traditionally named i, is the index of the element.
For instance:
var foo = d3.select("body").selectAll(null)
.data(["foo", "bar", "baz"])
.enter()
.append("foo")
.each(function(d, i) {
console.log("datum " + d + " has the index " + i)
})
<script src="https://d3js.org/d3.v4.min.js"></script>
In your case, you said:
If I'm changing my example to i * 2 or i * 10, rectangle just getting wider but remains single
And the explanation for this is very simple:
If you do i * 10, you are setting the y position of the next rectangle 10px to the right of the previous one. However, you set the width for all of them as 20px.
Look what happens if you set the width for a smaller value, for instance 8px:
var dataset = [5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
11, 12, 15, 20, 18, 17, 16, 18, 23, 25
];
var w = 500;
var h = 100;
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h)
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.text(function(d) {
return d;
})
.attr("x", function(d, i) {
return i * 10;
})
.attr("y", 0)
.attr("width", 8)
.attr("height", 100)
<script src="https://d3js.org/d3.v4.min.js"></script>
Therefore, since you set the width of all rectangles as 20px, any i value less than that will make the rectangles appear as a single one: the next rectangle will be painted over the previous one, and because of that there will be no space between them. Besides that, the fact that there is no difference between the "fill" and "stroke" colours made you think that you had a single rectangle, when in fact you have a bunch of them.
Hey I had a barChart and I'm aware in svg the x=0 and y=0 is on the top-left corner so the graphics shows downwards. If I add a couple of statements it turns around.
var w = 500;
var h = 100;
var barPadding = 1;
var dataset = [5, 4, 6, 7, 8, 10, 2, 3, 4, 5, 6, 25, 34, 54, 64, 32, 11, 32, 42, 5, 10, 15, 20, 25, 30, 35, 40];
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var rectangles = svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * (w / dataset.length);
})
.attr("y", function(d) {
return h - d;
})
.attr("width", w / dataset.length - barPadding)
.attr("height", function(d) {
return d * 4;
})
.attr("fill", function(d) {
return "rgb(0, 0, " + (d * 10) + ")";
});
<script data-require="d3#4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
I have two questions,
a)why on the examples they use h-d for the y positioning?
If the svg height is 100 for example and a number on the dataset is 60, shouldn't that position the bar on the y=40 from the top left corner? Yet that is not the case when I render this, the bar shows on the same y position for all the bars
Why is it rendering the bars correctly with those given values? What makes the bar "grow from the bottom" in this case, cause if I do the math it makes me expect a different thing.
In this particular case, the reason why the y values are cut off at the correct place is because the svg has a height of 100. But if you carefully inspect the svg, every rectangle extends beyond the bottom of the svg, they are just not visible since there is no overflow.
h-d is correct for the starting y position, if a value was 60, then (100-60) would equal 40 as you have correctly stated. However, the actual height should be just d rather than d * 4. I do not know where the d * 4 came from.
So if your value is 60, then y would be 40, height would be 60 which would extend to the bottom of the svg.
Hope this helps.
Hello Created A Bar Char Using D3 Using Below Code
var dataset = [5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
11, 12, 15, 20, 18, 17, 16, 18, 23, 25];
var w = 500;
var h = 100;
var barpadding = 1;
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var rect = svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function (d, i) {
return i * (w / dataset.length)
})
.attr("y", function (d) {
return (h - d);
})
.attr("height", function (d) {
return (d *2)
})
.attr("width", w / dataset.length - barpadding)
.attr("fill", function (d) {
return "rgb(0, 0, " + (d * 10) + ")";
});
<div class="bar"></div>
As You Can See At Starting My Height Attribute Is Like
.attr("height", function (d) {
return (d *2)
})
And Corresponding Image Is
Now I Have Changed It To 5 Times Like
.attr("height", function (d) {
return (d *5)
})
But Can't See Any Changes In My Bar's Height Any Help ??
fiddle link
You need to multiply the 'd' variable in the 'y' attribute change as well as the height. So the 'y' function ends up as:
.attr("y", function (d) {
return (h - (d * 5));
})
If you took out the subtracting from 'h' in the 'y' attribute and just left the 'y' attribute with no change, you see that your graph's height does change. The y attribute function is regulating the position of each rectangle so that the extended portion from multiplying it by 2 or 5 is hidden below the graph.
I'm wanting to scale this bar chart to the size of it's container. Ideally do something like w = '100%', h = '100%'. Any way I may do this?
http://jsfiddle.net/mo363jm7/
<div class="test" style="height:50px;width:100px;overflow:hidden"></div>
//Width and height
var w = 200,
h = 100,
barPadding = 1,
dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];
//Create SVG element
var svg = d3.select(".test")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * (w / dataset.length);
})
.attr("y", function(d) {
return h - (d * 4);
})
.attr("width", w / dataset.length - barPadding)
.attr("height", function(d) {
return d * 4;
})
.attr("fill", function(d) {
return "rgb(0, 0, " + (d * 10) + ")";
});
You can ask for the width of your container (or any given element) with the style function, like this:
var width = parseInt(d3.select(".test").style("width"),10);
var height = parseInt(d3.select(".test").style("height"),10);
The last parameter (10) indicates that you want to use the decimal system (if I am not mistaken).
So you can set the width of your svg element to that width. I would even suggest you make a function out of it and call that function on a resize event, so your svg looks like a fluid/responsive element.
you can do that as following:
d3.select(window).on('resize', function(){/* your svg and chart code */});
EDIT: On rereading your question, it appears to me that I might have misunderstood your question. D3 has scaling functions to scale your data so it fits into your svg container. If your div element is not responsive, then you should just set your svg width and height the same as your div element, and use scales on your data so your chart fits in the svg container. More info on scales you can find here: quantitative scales
try to change the height and width of your div to its column height and width like this http://jsfiddle.net/elviz/mo363jm7/2/
<div class="test" style="height:100px;width:200px;overflow:hidden"></div>
I am trying to add a text beside a Bar chart . it will be look like
two bar chart both of them will have a text in their right side. I tried in many ways but couldn't find or may i didn't understand because i am beginners in D3 . This is my first question in stackoverflow .
I wrote this
var w = 500;
var h = 100;
var barPadding = 5;
var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];
//Create SVG element Men 2010
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * (w / dataset.length);
})
.attr("y", function(d) {
return h - (d * 4);}
)
.attr("width", w / dataset.length - barPadding)
.attr("height", function(d) {
return d*4; //Just the data value
})
.attr("fill", function(d) {
return "rgb(0, 0, " + (d * 10) + ")";
});
///for the fixed text
var svgContainertext = d3.select("body").append("svg")
.attr("width", 100)
.attr("height", 20);
svgContainertext.append("g")
.selectAll("text")
.append("text")
.text("2010")
.attr("x",20)
.attr("y",10)
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "black");
But the code is not showing the result. I am new to D3 .
Thanks in advance .
Your text is not showing because you ad the text after .selectAll("text"), which results in an empty selection, hence nowhere to add your text.
Furthermore, the text you are adding is in a new svg, and is not linked anyhow to your chart. I created a plunk to show how to put the text in the same svg: http://plnkr.co/edit/EHVB65sn7Oc67s7woKrj?p=preview
Updated code:
var w = 500;
var h = 100;
var barPadding = 5;
var textWidth = 30;
var dataset = [5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
11, 12, 15, 20, 18, 17, 16, 18, 23, 25
];
//Create SVG element Men 2010
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * ((w - textWidth) / dataset.length);
})
.attr("y", function(d) {
return h - (d * 4);
})
.attr("width", w / dataset.length - barPadding)
.attr("height", function(d) {
return d * 4; //Just the data value
})
.attr("fill", function(d) {
return "rgb(0, 0, " + (d * 10) + ")";
});
///for the fixed text
svg.append("g")
.append("text")
.text("2010")
.attr("x", w - textWidth)
.attr("y", h / 2)
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "black");
What I did is use the same svg to append the text (in a group). I also reduced the space available for the bar chart using a textWidth variable, allowing to view the text and position it properly.
Removing .selectAll("text") will show the text 2010. I made a JSFiddle to show this.
If you want to read about selections, I suggest you have a read of this article
basically it is not working because the .selectAll('text') returns an empty selection and after that you append a text node to nothing.
It works for the rects drawn earlier because the .data() method is used on that selection.
var svgContainertext = d3.select("body").append("svg")
.attr("width", 100)
.attr("height", 20);
svgContainertext.append("g")
.append("text")
.text("2010")
.attr("x",20)
.attr("y",10)
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "black");