Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
400 views
in Technique[技术] by (71.8m points)

d3.js - How to create SVG elements of different types based on data?

I am trying to create a diverse legend, which has circles triangles, rectangles, lines etc, and I want to create these independently and then use d3 to arrange their position and coloring, but how would I access these data directly?

d3.selectAll('g.legend')
  .data([
    // is there a way to have d3 create an element in memory but not append it?
    { svgFn: function() { this.append('rect') }, ...otherinfo },
    { svgFn: function() { this.append('circle') }, ...otherinfo },
  ]).enter()
    .append('g')
      .append(function(d) { d.svgFn.call(this)})
      .attr...
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This question is a variation of the How to create elements based on data? or How to dynamically append elements? pattern. In my opinion your approach will be overly complex and cluttered because you need to duplicate functions to create elements in your data. This doesn't seem to be an elegant solution.

I would prefer specifying only the type of the element to create, i.e. {type: "circle"}, {type: "rect"} in your data objects, etc., and let the selection.append() method do the working. This method will accept a callback which in turn may evaluate the type specifed in your data and create the elements accordingly:

# selection.append(type) <>
[...]
Otherwise, the type may be a function which is evaluated for each selected element, in order, being passed the current datum (d), the current index (i), and the current group (nodes), with this as the current DOM element. This function should return an element to be appended.

This would simplify your code to just:

d3.selectAll('g.legend')
  .data([
    { type: 'rect', other: info },
    { type: 'circle', other: info }
  ])
  .enter().append('g')
  .append(function(d) { 
    return document.createElementNS(d3.namespaces.svg, d.type);
  });

Addendum

As requested by user2167582's comment the solution for assigning attributes can also be easily incorporated.

With D3 v4 using the d3-selection-multi module you may use the multi-value syntax passing in objects containing key-value pairs of attributes to set. Assuming your array of elements to be created to look like this:

var elementsAndAttributes = [
    { type: 'rect', attrs: { "fill": "blue", "width": "10", "height": "10" } },
    { type: 'circle', attrs: { "fill": "red", "cx": "20", "cy": "20", "r": "10" } }
];

You can then bind this data and create elements with their attributes in a single run:

d3.selectAll('g.legend')
  .data(elementsAndAttributes)
  .enter().append('g')
  .append(function(d) {                // Create elements from data
    return document.createElementNS(d3.namespaces.svg, d.type);   // v4 namespace
  })
    .attrs(function(d) {               // Set the attributes object per element
      return d.attrs;
    });

When still using D3 v3 things are a bit different. Although v3 had the support for multi-value object configuration built-in, you were not allowed to provide the object as the return value of a function (see the issue #277 "Multi-value map support." for a discussion on why that was). You can, however, use selection.each() to achieve the same thing.

d3.selectAll('g.legend')
  .data(elementsAndAttributes)
  .enter().append('g')
  .append(function(d) {                // Create elements from data
    return document.createElementNS(d3.ns.prefix.svg, d.type);  // v3 namespace
  })
    .each(function(d) {                // Iterate over all appended elements
      d3.select(this).attr(d.attrs);   // Set the attributes object per element
    });

Ignoring the differences in the way D3 references the namespace constants, this last version using selection.each() will actually work in both D3 v3 as well as v4.


Further reading: My answer to "Object Oriented d3".


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...