svg "g" element dimensions stuck on 0 (auto) - javascript

I'm trying to create something on JSfiddle regarding a question about d3, but I was ambushed by an unexpected problem - the "g" element I am creating inside an "svg" is refusing to accept any dimensions I give it!
I have tried using inline styling, classes (including "!important"), but nothing seems to work. The dimensions are stuck at 0,0 and inspection shows width and height are "auto" (why?!)
This seems like a trivial issue but for the life of me I can't seem to get the "g" element to accept width and height values
see JSFiddle: http://jsfiddle.net/sangil/uKLU3/

<g> is an autosizing container. It has no dimensions itself but instead it always automatically sizes itself to contain all its contents.
The reason you're not seeing anything is that SVG (unlike html) does not size things using styles but using attributes instead. So you want
g.append("rect")
.attr("width", "300px")
.attr("height", "200px")
.style("fill", "steelblue");

Related

D3: How to stick text with dynamic height to bottom of SVG element?

I want to display some text with dynamic content to be displayed at the bottom below my SVG chart. The text sometimes only consists of one line, sometimes of two. Right now, if there are two lines, the second line is cut off because it is outside of the drawing area.
The text is drawn like this:
chart.append("text")
.attr("class", "source")
.attr("x", MARGINS.left + 10)
.attr("y", HEIGHT - 5)
.attr("width", WIDTH - MARGINS.right)
.text(somedynamiccontent());
So the problem is, that the "y" attribute refers to the top of the text box, but I want it to point to the bottom of the element so that the text element only "grows" up- and not downwards. Does anyone have a solution or workaround for this? I already tried out alignment-baseline property, but i think that's not what i'm looking for.
Thank you.

Images not Generating D3

Here is my problem. My graph currently looks like this: Which is dandy. However, I want the black squares on top to be filled with pictures. Luckily I have a CSS file that has pictures linked with classes. I also have a JSON file that contains all the class names. All those class names are assigned to the squares and I can see the picture in the inspect element on Chrome. The only issue is the pictures don't appear in the square. (Also my axises broke, but that is secondary concern). CSS, JSON
This is where I'm assigning classes and creating the rectangles.
svg.selectAll(".div")
.data(data.chartData, function(d){return d.vNm;})
.enter().append("rect")
.attr("x", function(d){
return x(d.vNm);
})
.attr("y", function(d){
return (y(d.values.reduce(function(sum, d){
return sum + d.amount;
}, 0))) - 64.5;
})
.attr("width", 43)
.attr("height", 43)
.attr("class", function(d){return d.vId;})
.style("fill", function(d) { return segColor(d.data.type); });
One approach to solve your problem is to use html elements like div for the images above the chart instead of svg elements, so you can use the full power of css.
Luckily you don't need to calculate the position of those html elements by yourself, there are some libraries that help you position the images correctly above the bars in the chart.
Check out https://popper.js.org/ for example, you can just call its API for each bar you render using d3.js:
var popper = new Popper(barElement, onPopper, {
placement: 'top'
});
SVG elements do not follow exactly the same CSS rules as typical HTML elements.
In your case, background-image doesn't work.
The least painful way to achieve the effect would be to embed an <image> tag after the <rect>:
<image width="100" height="100" xlink:href="data:image/png;base64,...">
It means that you have to modify your JSON to store the image's base64 data in there instead of CSS.

How to translate SVG and all its children

I have been playing with this fiddle : https://jsfiddle.net/thatOneGuy/8k8ggpcn/4/
Majority of it is not my code, but from line 330-345 I have added, tried to, the ability to move the SVG by a certain amount. But this is not working. I can't seem to figure out why.
I have tried using D3. So added an ID of mainSVGContainer to the SVG at the start :
svg = d3.select("#svg1")
.append("svg").attr('id', 'mainSVGContainer')
And used this to translate :
d3.select('#mainSVGContainer').style('fill','blue').attr("transform", "translate(0 "+difference +")")
Difference is an integer worked out before this call, its around 130. But this doesn't seem to work. It gets written to the DOM but doesn't look like it's affecting the SVG.
I have tried with vanilla JavaScript :
var svgContainer = document.getElementById('mainSVGContainer');
svgContainer.offsetLeft = 1000;
This doesn't work either
And I have tried with inline JS to alter the styling :
svgContainer.style.left = 1000;
Still no luck. I presumed it was due to it being an SVG element but I tried doing the same with the container of this SVG which was a div and no luck.
Any ideas ?
As JSBob and others from the following :
d3 Workaround for svg transform in chrome
d3 Nested SVG plots differently in firefox than in Chrome
I found out Chrome, along with other browsers, don't support the translation of SVG elements. So, as a work around, I appended a g element to the SVG and translated that :
Appending g :
svg = d3.select("#svg1")
.append("svg").attr("height", h)
.attr("width", w)
.append('g')
.attr('id', 'mainSVGContainer')
.attr("height", h)
.attr("width", w)
.attr("class", "graph-svg-component")
Translating g :
d3.select('#mainSVGContainer').transition().duration(1000).attr("transform", "translate(0 "+(-difference) +")")
Added the transition so you can see before and after :)
Updated fiddle : https://jsfiddle.net/thatOneGuy/8k8ggpcn/7/

Positioning SVG elements via .getBoundingClientRect() in variable-width div

I have a bit of an annoying problem.
I'm trying to position a bunch of SVG circle elements according to an existing bunch of SVG text elements that share similar properties.
The circle elements are created in a very separate process than the text elements, so positioning the new elements just using the same transforms etc. as the old one isn't a viable option.
I'm trying to use .getBoundingClientRect() to get the positions since the text elements are transformed into position (so .getBBox() isn't an option) rather than positioned by x and y attributes.
With .getBoundingClientRect(), I can get the correct size/arrangement of the new elements, but since the width of the svg-containing div is variable, there's always a bit of a weird offset that I can't quite account for.
I created a simplified example of my issue here. Resize and refresh the page to see the issue in action.
The code I use to position the circle elements is replicated below.
var circs = theSvg.selectAll("circle")
.data(theCircles)
.enter()
.append("circle")
.attr("r", 15)
.attr("fill", "#f00")
.style("opacity", 0.3)
.attr("transform", function(d){
var sizeDif = 800/(d3.select(".svgTestHolder")[0][0].getBoundingClientRect()["width"]);
var theNum = parseInt(d.split("&")[1]);
var thePosition = theSvg.selectAll("text").filter(function(e){
return e == theNum;})[0];
var theCoords = thePosition[0].getBoundingClientRect();
var leftOffset = d3.select(".svgTestHolder")[0][0].getBoundingClientRect()["left"];
var leftOffset2 = d3.select(".svgTest")[0][0].getBoundingClientRect()["left"];
var bottomOffset = d3.select(".svgTestHolder")[0][0].getBoundingClientRect()["top"];
var bottomOffset2 = d3.select(".svgTest")[0][0].getBoundingClientRect()["top"];
return
"translate(" + ((theCoords["left"] - leftOffset - leftOffset2)
* sizeDif) + "," + ((theCoords["top"] - bottomOffset - bottomOffset2)
* sizeDif) + ")";
})
EDIT:
This is a very delayed update just to note that while I was unable to answer my question as stated, I was able to make a workable solution based on Paul LeBeau's suggestion to extract the transforms from the target element.
In my case, I had to use a series of consecutive transforms rather than a combination of transforming and changing the x/y position (due to certain realities of the project not represented in the linked example). But I'm happy to have found an answer!
Your example works fine for me on Chrome. But really that's only because the SVG is the only thing on the page. If I add some text above the SVG everything goes wrong.
https://jsfiddle.net/rrpfmm6d/1/
Is this the problem you are talking about?
If so, the reason is because you are making the wrong choice in using getBoundingClientRect(). It provides coordinates in screen space. It's origin is the top left of the window (or iframe in the case of jsfiddle).
You should be using getBBox(). The values it returns are in the same coordinate space as the SVG elements. It's origin is (normally) at the top left of the SVG.
In summary, use the coordinates returned by calling getBBox() on your <text> element to calculate the position for your circle. If the circles are inserted into the same SVG as the text, there will be no need to do any adjusting with the div or svg offsets.

SVG foreign object is not getting displayed using D3

I am using D3 render a simple network diagram. And in each node I want to display html content for that foreign object is used. Foreign object is having html inside. The network is getting rendered. But I am not able to view the html content anybody know why it is not rendering the html?
I am using below code.
dom.svg.selectAll('.node').append("foreignObject")
.attr("width", 100)
.attr("height", 100)
.append("xhtml:body").append("xhtml:p")
.style("color", "red")
.text("Object in SVG");
Here is the fiddle
You can't append foreignObjects (or indeed anything) to circle elements. Instead, append them to a container element like gs for example. Fixed here.

Categories

Resources