绑定到子项的数据不会使用新数据更新

时间:2016-11-04 17:35:55

标签: javascript d3.js

我在D3.js中的enter()选项中添加了一个元素和一个子元素:

let data = [ 1, 2, 3 ];
selection.data( data ).append( 'g' )
    .attr( 'name', d => d )
    .append( 'text' )
    .text( d => d );

这会按预期创建一些元素:

<g name="1"><text>1</text></g>
<g name="2"><text>2</text></g>
<g name="3"><text>3</text></g>

加载新数据时,g元素会收到它,但text元素仍然绑定到以前的数据:

let data = [ 'A', 'B', 'C' ];
selection.attr( 'name', d => d )
    .selectAll( 'text' )
    .text( d => `[${d}]` ); // <- Remains unchanged

结果 - 1,2,3不应再存在:

<g name="A"><text>1</text></g>
<g name="B"><text>2</text></g>
<g name="C"><text>3</text></g>

如何正确使用D3,以便text元素也会收到新数据?

JS小提琴:https://jsfiddle.net/07nvax31/1/

3 个答案:

答案 0 :(得分:1)

执行此操作的一种方法是将<g><text>分开。然后您可以单独重新绑定数据:

// Set up an SVG already in the document
var svg = d3.select('svg');

// Create the groups with data bound to them
var data = [ 1, 2, 3 ];
var gs = svg.selectAll('g')
    .data( data ).enter()
    .append( 'g' )
    .attr( 'name', String);

// Add text as children to each group, with the data passed through
var text = gs.append( 'text' ).text(String);

// Update the data and attributes/text for each
data = [ 'A', 'B', 'C' ];
gs.data( data ).attr( 'name', String )
text.data( data ).text( function(d){ return `[${d}]`; });

这给了我以下内容:

screenshot of svg structure

请注意,我必须稍微修改您的代码以获得完整的示例。

答案 1 :(得分:1)

(这只是一个旁注,comments中的@LarsKotthoff答案是正确的答案)

您可以使用this.parentNode-__data__访问父节点的数据(在本例中为group元素),并保留selectAll

例如,这个:

var svg = d3.select("body").append("svg");
var data = [ 1, 2, 3 ];

var groups = svg.selectAll(".groups").data(data);
groups.enter().append('g')
    .attr('name', (d) => d )
    .append('text')
    .text((d)=> d );

会给你:

<g name="1"><text>1</text></g>
<g name="2"><text>2</text></g>
<g name="3"><text>3</text></g>

但是如果您更改数据并将其再次绑定到组,则可以使用this.parentNode.__data__获取其子组(文本元素)中相应组的数据。

因此,这个:

var data = [ 'A', 'B', 'C' ];

groups.data(data).attr( 'name', (d) => d )
    .selectAll('text')
    .text(function(){ return this.parentNode.__data__});

现在会给你:

<g name="A"><text>A</text></g>
<g name="B"><text>B</text></g>
<g name="C"><text>C</text></g>

请注意,由于代码使用this,我们无法在此处使用胖箭头功能。

查看演示:

var svg = d3.select("body").append("svg");

var data = [1, 2, 3];

var groups = svg.selectAll(".groups").data(data);
groups.enter().append('g')
    .attr('name', (d) => d)
    .append('text')
    .text((d) => d);

var firstSVG = (new XMLSerializer()).serializeToString(svg.node());

var data = ['A', 'B', 'C'];

groups.data(data).attr('name', (d) => d).selectAll('text')
    .text(function() {
        return this.parentNode.__data__
    });

var secondSVG = (new XMLSerializer()).serializeToString(svg.node());

console.log("First SVG:" + firstSVG)
console.log("Second SVG:" + secondSVG)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

答案 2 :(得分:1)

更新时,您只需使用.select("text")代替.selectAll("text")即可使其正常运行。两者之间的区别在于.select()使用父元素的数据更新绑定到所选元素的数据,而.selectAll()则不会。由于每text只有一个g元素,.select()就足够了。