简体   繁体   中英

Can I append a literal SVG element with d3?

I'd like to append a literal SVG element with d3.

So instead of writing

svg.selectAll("circle")            
    .data(data)                    
    .enter()                       
    .append("circle")    // etc etc

I'd like to do:

svg.selectAll("circle")            
    .data(data)                    
    .enter()                       
    .append('<circle cx="158.9344262295082" cy="200" r="16" fill="red"></circle>')   

so that I could create a complex template elsewhere (for example with handlebars), and then compile it with data and append it.

You can do this, although not via the selection.append() function. Instead you'd need to use the selection.html() function.

This would make it quite difficult to use in the context of data-joins, but not impossible. This is probably the best you could do, which involves adding an additional svg group to the DOM which may not be a bad thing anyway:

 var svg = d3.selectAll("svg"); svg.selectAll("circle") .data([50, 100, 150]) .enter() .append("g") .attr("transform", function(d) { return "translate(" + [d, d] + ")"; }) .html('<circle r="16" fill="red"></circle>'); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> <svg width="500" height="500"></svg> 

I guess taking this answer a bit further, you could actually embed the result that you wish to render directly into your data object. So you've add some code that looked like:

.html(function(d) { return d.templatedHTML; });

At this point however stop and ask yourself the question: "What am I using D3 for?". D3 is described as

Data Driven Documents

If you're using something like handlebars, then you're taking away one of the core responsibilities that D3 was designed for (building some DOM from some data) and giving it to some other library.

I'm not stating you shouldn't do that (as you did mention complex templates) but do just ask yourself the question to make sure that it's a path you wish to go down.

No, you can't. Don't believe me? Check their docs HERE

What you must do is call .append() , followed by several calls of .attr(attr_name, attr_value) to set each attribute's value. D3 does not work like jQuery.

D3 doesn't provide this functionality, and it does not make much sense (when you think about manipulating elements based on data).

But, as a side note, you can implement your own function to append a literal SVG element.

This is a function created by Chris Viau , named appendSVG :

d3.selection.prototype.appendSVG = 
    d3.selection.enter.prototype.appendSVG = function(SVGString) {
        return this.select(function() {
            return this.appendChild(document.importNode(new DOMParser()
                .parseFromString('<svg xmlns="http://www.w3.org/2000/svg">' + SVGString + 
            '</svg>', 'application/xml').documentElement.firstChild, true));
        });
    };

After extending the prototype, you can use it:

selection.appendSVG("<some literal SVG element>");  

Here is a demo. First, we set the data:

var data = [{x:30,y:50},{x:420,y:100},{x:160,y:150},{x:260,y:30}];

Then, we append our literal SVG element in the usual way:

var myLiteral = svg.selectAll(".literal")
    .data(data)
    .enter()
    .appendSVG("<literal SVG here>");

And finally we set the positions using translate :

.attr("transform", function(d){ 
    return "translate(" + d.x + "," + d.y + ")";
});

Check the snippet:

 d3.selection.prototype.appendSVG = d3.selection.enter.prototype.appendSVG = function(SVGString) {return this.select(function() { return this.appendChild(document.importNode(new DOMParser().parseFromString('<svg xmlns="http://www.w3.org/2000/svg">' + SVGString + '</svg>', 'application/xml').documentElement.firstChild, true));});}; var svg = d3.select("body").append("svg").attr("width", 500).attr("height", 300); var data = [{x:30,y:50},{x:420,y:100},{x:160,y:150},{x:260,y:30}]; var myLiteral = svg.selectAll(".literal") .data(data) .enter() .appendSVG("<path d='M 22.889471,25.607172 C 22.589713,24.605127 24.092318,24.708731 24.554936,25.108955 C 25.808602,26.193538 25.053398,28.14136 23.885905,28.938102 C 21.797533,30.363287 19.018523,29.16303 17.893076,27.101823 C 16.241437,24.076919 17.936475,20.36976 20.896603,18.945312 C 24.841988,17.046747 29.504523,19.25402 31.216796,23.116087 C 33.371517,27.976105 30.644503,33.605344 25.878773,35.599962 C 20.106834,38.015712 13.505062,34.765112 11.231216,29.094691 C 8.551568,22.412295 12.327973,14.834577 18.903736,12.283452 C 26.495714,9.3380778 35.051552,13.641683 37.878656,21.12322 C 41.09099,29.624218 36.259254,39.159651 27.87164,42.261821 C 18.462006,45.741988 7.9459296,40.381466 4.5693566,31.087558 C 0.82072068,20.769559 6.7105029,9.2720694 16.910868,5.6215926' style='fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1'/>") .attr("transform", function(d){ return "translate(" + dx + "," + dy + ")"}); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM