d3 - 无法添加 select/option 下拉到 svg-g 元素

d3 - unable to add select/option drop down to svg-g element

我打算向 SVG 添加圆形包、单选按钮和下拉菜单,并在它们之间切换(在同一个 svg 上)。

我无法将 select/option 下拉菜单添加到使用 width/height of 600/600 创建的 SVG。在元素控制台中,我可以看到 g-元素和下拉 select 选项。 但是在屏幕上,g#menu 的大小为 0x0?为什么会这样?任何 pointers/inputs?

注意:我在这里尝试使用 D3.js 动态添加下拉菜单,而不是在 body 元素中硬编码 selectio/option 或 div。我这样做是为了在不同的视图之间切换。

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;});

这个问题很常见,所以我在文档中创建了这个示例。该示例处理错误地附加到 SVG 的 SVG 元素,但同样的推理适用于附加到 SVG 的 HTML 元素:您必须知道哪些元素可以具有 children 和 什么 种 children 他们可以拥有。此 post.

底部有更多详细信息

简而言之:您 不能 将任何 HTML 元素附加到 SVG(除非使用 foreignObject)。该元素将显示在控制台中,但不起作用。

注意:我正在回答你写的问题并忽略你的代码,因为有趣的是,在你的代码中,你正确地将 HTML 元素附加到 body.


正确附加 SVG 元素

这是一个相对常见的错误:您创建了一个 rect 元素,例如在条形图中,您想要添加文本标签(比方说,该条的值)。因此,使用用于追加 rect 并定义其 xy 位置的同一变量,您可以追加 text 元素。非常合乎逻辑,你可能会想。但这行不通。

这个错误是怎么产生的?

让我们看一个具体的例子,一个创建条形图的非常基本的代码(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);

这给了我们这个结果:

但是您想添加一些文本元素,可能是每个栏的简单值。所以,你这样做:

bars.append("text")
   .attr("x", 10)
   .attr("y", function(d,i){ return 10 + i*40})
   .text(function(d){ return d});

而且,瞧:没有任何反应!如有疑问,here is the fiddle.

"But I'm seeing the tag!"

如果您检查最后一段代码创建的 SVG,您会看到:

此时很多人说:"But I'm seeing the text tag, it's appended!"。是的,是的,但这并不意味着它会起作用。您可以附加 任何内容 !看到这个,例如:

svg.append("crazyTag");

它会给你这样的结果:

<svg>
    <crazyTag></crazyTag>
</svg>

但是你不会仅仅因为标签在那里就期待任何结果,对吗?

以正确的方式附加 SVG 元素

了解 SVG 元素可以容纳什么 children,阅读 specifications。在我们的最后一个示例中,代码不起作用,因为 rect 元素不能包含 text 元素。那么,如何显示我们的文字呢?

创建另一个变量,并将 text 附加到 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});

这是结果:

here is the fiddle.

试试这个

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>