tspan is always hidden on FF - d3.js

I have a d3 code jsfiddle. Upper text here is visible in all browser except FF.
Here is the complete code:
var data = [2000, 400];
var chart = d3.select("#container").append("svg")
.attr("class", "chart")
.attr("width", 800) // bar has a fixed width
.attr("height", 300)
.style("padding-top", "20px")
var x = d3.scale.linear()
.domain([0, d3.max(data)])
.range([10, 100]);
chart.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("y", 18)
.attr("x", 10)
.attr("width", function(d){
return x(d) - 10
})
.attr("height", 10)
var text = [0];
var data = ['Upper text here']
chart.selectAll("text")
.data(text)
.enter()
.append("text")
.attr("x",x)
.attr("dy", ".25em")
.append('tspan')
.text('Upper text here')
.attr('x', x)
.attr("y", function(_, i) {
return i === 1 ? 42 : -9;
})
.attr('dy', '.35em')
Not sure, whats the mistake here.

In SVG 1.1 the overflow property on <svg> elements defaults to hidden.
In SVG 2 it is proposed that it default to visible.
It seems like Chrome has implemented this proposed SVG 2 change but Firefox probably won't.
You really should draw inside the SVG canvas and not depend on overflow visible as it has a pretty big performance hit since the browser can no longer assume that the maximum SVG canvas bounds are the outer SVG element's height and width.
If you really must draw outside the bounds then simply set overflow explicitly to get consistent cross-browser behaviour e.g.
var chart = d3.select("#container").append("svg")
.attr("class", "chart")
.attr("width", 800) // bar has a fixed width
.attr("height", 300)
.attr("overflow", "visible")
.style("padding-top", "20px")

Related

Append new shapes to existing shapes within SVG document D3

In D3 I'm used to creating visuals from a blank div. However I'm trying to get my head around the following.
I have an svg document (https://raw.githubusercontent.com/gangrel11/samplefiles/main/d3%20task1.svg) which is just 3 rectangles.
What I'm trying to do is append 3 new shapes (circles) to each one of the existing rectangles so that they appear in the centre of each one.
This is where I got to:
const svgUrl = "https://raw.githubusercontent.com/gangrel11/samplefiles/main/d3%20task1.svg"
d3.xml(svgUrl).then(render);
function render(svg) {
// add svg
d3.select("body").node().append(svg.documentElement)
var svg = d3.select("body").append("svg");
svg.selectAll("rect")
.append("rect")
.attr("width", 20)
.attr("height", 20)
.attr("x", 20)
.attr("y", 10)
.attr("fill", "red")
}
Here is my jsfiddle
You have a few problems:
You are adding another svg element instead of using the existing one.
My suggestion is changing:
d3.select("body").append("svg");
to
d3.select("body").select("svg #layer1");
notice that I also targeted the g element #layer1 that get transformed.
you try to append rect element to rect element but svg doesn't know how to draw rect inside rect - this syntax is invalid.
instead, you can target each element and use his position using the .each method and append them after all the existing rects.
code:
function render(svg) {
// add svg
d3.select("body").node().append(svg.documentElement)
var svg = d3.select("body").select("svg #layer1");
svg.selectAll("rect")
.each(function (rect){
const {x, y} = this.getBoundingClientRect();
svg.append("rect")
.attr("width", 20)
.attr("height", 20)
.attr("x", this.getAttribute('x'))
.attr("y", this.getAttribute('y'))
.attr("fill", "red");
});
}

in d3js bar chart i want to have fixed bar width

http://bl.ocks.org/d3noob/8952219
I want to have bar size width to be fixed..
from above example i have changed the code from
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", "steelblue")
.attr("x", function(d) { return x(d.date); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
to
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", "steelblue")
.attr("x", function(d) { return x(d.date); })
.attr("width", 50)
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
but the labels are not moving to proper place also bars are getting overlapped
You have to change the range() of your x scale, to fit with your bar width value:
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
to (if you want 50px as bar width)
var x = d3.scale.ordinal().range([0, data.length * 50]);
The range() method is used to define the display space for your scale.
I was looking for a similar solution. What #JulCh gave as an answer did not work out of the box for me, but lead me in the right direction.
Try:
var x = d3.scale.ordinal()
.range(d3.range(data.length).map(function (d) { return d * 50; }));
Where the inner d3.range creates an array containing the number of elements determined by data.length or some constant number (the number of bars you would like displayed).
Example: If data.length or some constant is 8 then [0,1,2,3,4,5,6,7] is returned from d3.range(8)
The map function then multiplies your fixed width of 50 against each element in the array returning [0,50,100,150,200,250,300,350].
D3 will then use these exact values to place your bars.

d3.js trying to call data method twice on same selector produces strange results

I'm new to D3 and am trying to build a table like structure out of rectangles. I would like the header to be a different color than the rest of the rectangles. I've written the following code:
table = svgContainer.selectAll('rect')
.data([managedObj])
.enter()
.append('rect')
.attr("width", 120)
.attr("height", 20)
.attr("fill", "blue")
.text(function(d) {
return d.name;
});
// create table body
table.selectAll('rect')
.data(managedObj.data)
.enter()
.append('rect')
.attr("y", function() {
shift += 20;
return shift;
})
.attr("width", 120)
.attr("height", 20)
.attr("fill", "red")
.text(function(d) {
return d.name;
});
This is producing the following results:
This is almost what I intended except it is nesting the second group of rectangles inside the first rectangle. This causes only the first blue rectangle to be visible. I'm assuming this has something to do with calling the data method twice. How can I fix this issue?
I think I understand the intended result, so I'll give it a go:
This line :
table.selectAll('rect')
is selecting the rectangle just created here:
table = svgContainer.selectAll('rect')....append('rect')....
You don't want to append rectangles to that rectangle (or any rectangle for that matter) because this won't work, but you do want to append them to the SVG itself.
So instead of table.selectAll you should be using svgContainer.selectAll, but there are two other issues:
if you use svgContainer.selectAll('rect') you will be selecting the rect you have already appended, when you actually want an empty selection. See the answer here.
you cannot place text in a rect (See answer here), instead you could append g elements and then append text and rect elements to those. And, for ease of positioning, you could translate the g elements so that positioning the rectangles and text is more straight forward.
So, your code could look like:
var data = ["test1","test2","test3","test4"];
var svgContainer = d3.select('body').append('svg').attr('width',900).attr('height',400);
var header = svgContainer.selectAll('g')
.data([data])
.enter()
.append('g')
.attr('transform','translate(0,0)');
header.append('rect')
.attr("width", 120)
.attr("height", 20)
.attr("fill", "blue");
header.append('text')
.attr('y',15)
.attr('x',5)
.text(function(d) {
return "header";
});
// create table body
var boxes = svgContainer.selectAll('.box')
.data(data)
.enter()
.append('g')
.attr('class','box')
.attr('transform',function(d,i) { return 'translate(0,'+((i+1)*20)+')'; });
boxes.append('rect').attr("width", 120)
.attr("height", 20)
.attr("fill", "red");
boxes.append('text')
.attr('y',15)
.attr('x',5)
.text(function(d) {
return d;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

While creating a bar graph using d3 how to associate the bar height with the numbers on the scale?

When creating a bar graph with data for both the axis in d3.js, how do I link the position and height of the bar with the numbers on the axes?
no point adding new libraries as mentioned in the comments. Just scale your width and height of your bars with your axis.
Simple example : http://bl.ocks.org/d3noob/8952219
Notice these lines of code :
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", "steelblue")
.attr("x", function(d) { return x(d.date); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
He is passing the number he wants to use as the height (d.value) to the y scale. That way the labels on the axis coincide to the height of the bars

Cannot get d3 svg tutorial to work in jsfiddle

I'm trying to follow a d3 tutorial and I've created a JSFiddle for the following code
var dataset = [1,2,3,4,5];
var sampleSVG = d3.select("#viz")
.append("svg")
.attr("width", 400)
.attr("height", 75);
sampleSVG.selectAll("circle")
.data(dataset)
.enter().append("circle")
.style("stroke", "gray")
.style("fill", "red")
.attr("height", 40)
.attr("width", 75)
.attr("x", function(d, i){return i*80})
.attr("y", 20);
However, I see the generated circles in the svg but I can't see them on the screen. Can anyone see what I'm missing?
Here is a FIDDLE:
var dataset = [1,2,3,4,5];
sampleSVG.selectAll("circle")
.data(dataset)
.enter().append("circle")
.style("stroke", "gray")
.style("fill", "red")
.attr("cx", function(d, i){return (i + 1 ) *60})
.attr("cy", 30)
.attr("r", 20);
I just focused on the main parts that needed change. You can study the differences. Basically, you had the wrong attributes for a circle (x and y, instead of cx and cy) and was missing the radius attribute. Finally, height and width are not circle attributes.

Resources