Using d3.js to create an SVG at several divs - javascript

Suppose I have some HTML which looks like:
<html>
<body>
<div class = "a"></div>
...
<div class = "a"></div>
...
<div class = "a"></div>
...
</body>
<html>
where the ...'s are just paragraphs or other code.
Problem: I'd like to be able to use d3.js to append an SVG at each of these div's.
For example, let's say I want to make a rectangle like:
var svg = ((SOMETHING GOES HERE!))
.append("svg")
.attr("width", w)
.attr("height", h);
var sep = svg.selectAll("rect")
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 10)
How can I use the selectors on the first line to do this? I've tried selectall() and select() with various "div.a", ".", etc., things, but nothing seems to work.

You can just append the SVG to your div with:
d3.selectAll("div.a").append("svg")
And if you want to select and act upon those SVGs after you made them just add a sub-select:
d3.selectAll("div.a").select("svg").append("rect").attr("height", 50).attr("width", 50)
Or you could class your SVGs and select them after you made them:
d3.selectAll("div.a").append("svg").attr("class", "divSVG")
d3.selectAll("svg.divSVG).append("rect").attr("height", 50).attr("width", 50)
Also, if you want them to appear before your elements, use insert instead of append:
d3.selectAll("div.a").insert("svg", "p")

Related

How to properly use the Use tag in SVG with d3 js?

I know that d3 renders elements based on the called order. So if I want to render white text over a black rectangle, I can just call the rect first and then call the white text.
However, in my particular case, my rect's dimension is based on the my white text, so I have to call the white text first.
A go-around I found was to use the use tag, but I couldn't get it to work, here's my current attempt:
the text:
var textToolTip = gToolTip
.append("text")
.attr("id", "toUse")
.text(.....)...
the use tag:
var useText = gToolTip.append("use").attr("xlink:xlink:href", "#toUse");
I have also tried to give textToolTip xlink:href but it didn't work. For use tag, I have tried to use xlink:href instead of xlink:xlink:href, it didn't work. I am using double xlink because of this answer I found: How do I define an SVG doc under <defs>, and reuse with the <use> tag?
If I'm understanding you correctly, it would be simpler to append the rectangle, append the text, then size the rectangle to the text:
<!DOCTYPE html>
<html>
<head>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<svg width="900" height="900"></svg>
<script>
var svg = d3.select('svg');
var rect = svg.append("rect")
.style("fill", "black")
.attr("x", 20)
.attr("y", 20);
var text = svg.append("text")
.text("Now is the time for all good men to come to the aid of their country")
.attr("x", 20)
.attr("y", 20)
.attr("alignment-baseline","hanging")
.style("fill","steelblue")
.style("font-family", "arial")
.style("font-size", "14pt")
var bbox = text.node().getBBox();
rect.attr("width", bbox.width);
rect.attr("height", bbox.height);
</script>
</body>
</html>

How can I append the one SVG (graph) inside another SVG node (rect) after onclick using d3.js?

I want to populate the data inside the parent graph node, that is I want to add nested svg on click event using d3.js.
I have tried following code but the output is looking very different I want to show the graph inside the after on click event of first svg node.
var svg = d3.select("body").append("svg")
.attr("width", 600)
.attr("height", 400)
svg.append('rect')
.attr('class', 'content shape')
.attr("width", 20)
.attr("height", 20)
.attr("stroke", "black")
.attr("stroke-width", 1)
.style("fill", function (d) {
return d._children ? "lightsteelblue" : "#fff";
})
.on("click", createMap);
var createMap (mapData){
if(mapdata.data){
d3.select(this).attr("width", 200)
.attr("height", 200)
.call(function(d){
var nodeMap = getGeneratedmap(mapdata.data);
return nodeMap
}
}
}
var getGeneratedmap(mapdata){
var generatedmapSVG = d3.select("#subnodeMap").append("svg")
....
....(logic for map generation)
....
return generatedmapSvg
}
<body>
<div id="mainMap">
<svg></svg>
</div>
<div id="subnodeMap"></div>
</body>
Please guide me and give the proper way to solve the above issue. Thanks in advance.
Your first SVG, the var svg is being appended to <body>. The second SVG is being appended to the div with ID #subnodemap, so you are not actually nesting the first SVG inside the second. You probably want to do something like:
var generatedmapSVG = svg.append("svg")
That's about as much as I can help you without a working example in Codepen or similar, hope it gets you towards a solution.

D3:reflect data changes on visualization keeping the same array size. Trying to represent a sort algorithm visually

I'm trying to represent a selection sort visually with d3 to show some students and I'm having problems updating the data once the positions swap(I will add the transitions and delays once it's working). The positional attributes don't seem to be working as well, I don't know why, any ideas?. The codepen is here:
HTML:
<div id="canvas">
</div>
CSS:
rect{
border:1px solid black;
}
JS:
function selectionSort(array,svg){
//where the index will position
var positioningIndex=0;
var aux;
var minIndex;
var minVal=Number.MAX_VALUE;
while(positioningIndex<array.length){
//get the index of the mínimum
for(var i=positioningIndex;i<array.length;i++){
if(array[i]<minVal){
minIndex=i;
minVal=array[i];
}
}
//swap the mínimum to the positioningIndex
aux=array[minIndex];
array[minIndex]=array[positioningIndex];
array[positioningIndex]=aux;
//update visualization
svg.selectAll("rect").data(array);
minVal=Number.MAX_VALUE;
++positioningIndex;
}
return array;
}
var dataSet=[10,7,8,44];
var svg=d3.select("#canvas").selectAll("rect")
.append("svg")
.attr("width", 960)
.attr("height", 500);
var rect=svg.data(dataSet)
.enter()
.append("rect");
rect.text(function(el){
return el;
})
.attr("width", 30)
.attr("height", 30)
.attr("x", function(d, i) {
return i*5;
})
.attr("y", 30);
.style("color","green");
array=selectionSort(dataSet,svg);
You've got a lot of mixing up of html and svg elements going on there
First off, your svg element isn't getting appended:
var svg=d3.select("#canvas").selectAll("rect") // <- selectAll("rect") is causing problems
.append("svg")
No existing rect elements at the start means no svg's getting appended (do you mean to add one for each rect?) Edit: and in fact that one error is the cause of everything that happens afterwards - the selectAll("rect") needs moved to the line where elements are added to the svg - not on the line where the svg itself is added -->
var rect=svg.selectAll("rect").data(dataSet) // rect should really be g
.enter()
.append("rect");
Secondly, and because of the above error, the elements called 'rect' that are added (and added directly to the #canvas id div) aren't svg:rect objects - they're just html elements with the name 'rect' - see Is there a way to create your own html tag in HTML5?. The browser just treats them as inline elements, so none of your x's or y's make a difference they just line up one after the other
Finally, if this was svg you wouldn't be able to add text directly to a rect, you'd need to use a group (g) element and add both rect and text elements to that to keep them associated, and style("transform", translate(x,y)) the group element to move them around.
var g=svg.selectAll("g").data(dataSet) // <-- now changed from rect
.enter()
.append("g") // <-- and here
.attr ("transform", function(d,i) {
return "translate("+(i*35)+" 30)";
})
;
// texts n rects added here
g.append("text").text(function(el){
return el;
})
.attr("dy", "1em")
g.append("rect")
.attr("width", 30)
.attr("height", 30)
;
See http://codepen.io/anon/pen/bwJWEa?editors=1111

Positioning multiple SVG elements in div HTML

I have 2 charts which looks like this:
I need to display the tag-cloud next to the bar-chart. I am using D3 to display these charts and my HTML division looks like this (svg1 displays bar-chart and svg2 displays tag-cloud):
I would appreciate any help. Here is the DEMO. In the demonstration I want circle to appear next to the bar chart.
Thanks!
Why not just float the elements like here on your demo
d3.select("body").append("div")
.attr("id","area1")
.style("width", 500)
.style("float","left");
And
function wordCloud(){
var wC = {};
d3.select("body")
.append("svg")
.style("float","left")
.attr("width", 50)
.attr("height", 50).append("circle").attr("cx", 25).attr("cy", 25).attr("r", 25).style("fill", "purple");
return wC;
}

D3 Changing the width attribute of an object depending on it's child

I am trying to change the width of the entire frame of something in D3 after it has already been created, depending on the width of some text inside of it (so that it doesn't get cut off). However, I'm having trouble returning the width of the text element.
The body looks like so:
this.body = d3.select("#" + this.placeholderName)
.append("svg:svg")
.attr("class", "gauge")
.attr("width", this.config.size)
.attr("height", this.config.size + 150);
The text inside the body looks like this:
this.body.append("svg:text")
.attr("x", this.config.cx - 60)
.attr("y", this.config.cy / 2 + fontSize / 2)
.attr("dy", fontSize / 2)
.attr("text-anchor", "start")
.text(this.config.label)
.style("font-size", fontSize + "px")
.style("fill", "#333")
.style("stroke-width", "0px")
Finally, I change the width of the frame with this. What would I need to put as the second argument here?
this.body.attr("width", <WIDTH OF TEXT ABOVE>);
Thanks
First, when you create your text element, give it a class or id so you can easily select it later.
Lets say you give it .attr('class', 'bodyText').
Then you can get its length using SVGTextContentElement.getComputedTextLength()
You can get this value for your text node by using selection.node() to grab the actual text element from a d3 selection, and then calling the method on the node. Like this:
var textLength = d3.select('.bodyText').node().getComputedTextLength();
Then you can use this value when setting your width property.
Have you tried this:
this.body.append("svg:text").attr("id","textValue")
and
var w=d3.select("#textValue").attr("width");
Now
this.body.attr("width", w);
I think it should work.

Categories

Resources