Reputation: 15
I am intending to add a circle pack, radio and drop down menu to a SVG and toggle between these (on the same svg).
I wasn't able to add the select/option dropdown menu to the SVG that is created with width/height of 600/600. In the element console, I am able to see the g
-element and the drop down select option.
But on the screen, the g#menu
has a 0x0 size? Why is this so? Any pointers/inputs?
NOTE: Here I am trying to add the dropdown menu dynamically using D3.js instead of hardcoding a selectio/option or div in the body element. I do this to enable toggling between different views.
var svg1 = d3.select("body")
.append("svg")
.attr("width", 600)
.attr("height", 600)
.append("g")
.attr("transform",
"translate(0,0)"));
var buttonNames = ["button 1", "button 2", "button 3", "button 4"];
var menug = svg1.append("g")
.attr("id","menug")
.attr("transform","translate(60,60)");
var menu = d3.select("#menug")
.append("select")
.attr("id", "menu");
menu.selectAll("option")
.data(buttonNames)
.enter().append("option")
.text(function(d){return d;});
Upvotes: 0
Views: 5083
Reputation: 102174
This question is so common that I created this example in the documentation. The example deals with SVG elements incorrectly appended to an SVG, but the same reasoning applies to HTML elements being appended to an SVG: you have to know what elements can have children and what kind of children they can have. More details at the bottom of this post.
In a nutshell: you cannot append any HTML element to an SVG (unless using foreignObject). The element will show up in the console, but won't work.
Note: I'm answering your written question and ignoring your code because, funny enough, in your code, you are correctly appending the HTML element to the body
.
Correctly appending an SVG element
This is a relatively common mistake: You created an rect
element, in a bar chart for instance, and you want to add a text label (let's say, the value of that bar). So, using the same variable that you used to append the rect
and define its x
and y
position, you append your text
element. Very logic, you may think. But this will not work.
How does this mistake occur?
Let's see a concrete example, a very basic code for creating a bar chart (fiddle here):
var data = [210, 36, 322, 59, 123, 350, 290];
var width = 400, height = 300;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var bars = svg.selectAll(".myBars")
.data(data)
.enter()
.append("rect");
bars.attr("x", 10)
.attr("y", function(d,i){ return 10 + i*40})
.attr("width", function(d){ return d})
.attr("height", 30);
Which gives us this result:
But you want to add some text elements, maybe a simple value to each bar. So, you do this:
bars.append("text")
.attr("x", 10)
.attr("y", function(d,i){ return 10 + i*40})
.text(function(d){ return d});
And, voilà: nothing happens! If you doubt it, here is the fiddle.
"But I'm seeing the tag!"
If you inspect the SVG created by this last code, you're gonna see this:
And at this point a lot of people say: "But I'm seeing the text tag, it's appended!". Yes, it is, but this doesn't mean it will work. You can append anything! See this, for example:
svg.append("crazyTag");
It will give you this result:
<svg>
<crazyTag></crazyTag>
</svg>
But you don't expect any result just because the tag is there, do you?
Append SVG elements the correct way
Learn what SVG elements can hold children, reading the specifications. In our last example, the code doesn't work because rect
elements cannot contain text
elements. So, how to display our texts?
Create another variable, and append the text
to the SVG:
var texts = svg.selectAll(".myTexts")
.data(data)
.enter()
.append("text");
texts.attr("x", function(d){ return d + 16})
.attr("y", function(d,i){ return 30 + i*40})
.text(function(d){ return d});
And this is the outcome:
And here is the fiddle.
Upvotes: 4
Reputation: 1231
Try this out
var buttonNames = ["button 1", "button 2", "button 3", "button 4"];
var l=4;
for(i=0;i<buttonNames.length;i++){if(l<buttonNames[i].length)l=buttonNames[i].length};
var width = 400, height = 300;
l=l*10;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("class","dropdown");
select = svg.append("g")
.attr("class","select");
select.append("rect")
.attr("x", 10)
.attr("y", 10 )
.attr("width", l)
.attr("height", 30);
select.append("text")
.attr("x", 15)
.attr("y",30 )
.attr("id","mydropdown")
.text( buttonNames[0]);
var options = svg.selectAll(".myBars")
.data(buttonNames)
.enter()
.append("g");
options.attr("class", "option").on("click", function() { document.getElementById("mydropdown").innerHTML=this.getElementsByTagName("text")[0].innerHTML;
d3.event.stopPropagation();
});
options.append("rect").attr("x", 10)
.attr("y", function(d,i){ return 40 + i*30})
.attr("width", l)
.attr("height", 30);
options.append("text").attr("x", function(d){ return 15})
.attr("y", function(d,i){ return 60 + i*30})
.text(function(d){ return d});
rect {
fill: teal;
}
.dropdown .option {
display:none;
}
.dropdown rect{
stroke-width:0.5;
stroke:rgb(0,0,0)
}
.dropdown:hover .option {
display:unset;
}
.dropdown {
cursor:pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Upvotes: 2