score:31

Accepted answer

you can use the following:

var svg = document.createelementns('http://www.w3.org/2000/svg', 'svg');

note the use of createelementns. this is required because svg elements are not in the same xhtml namespace as most html elements.

this code creates a new svg element, as you would regardless of using d3 or not, and then creates a selection over that single element.

this can be made marginally more succinct but clearer and less error prone as:

var svg = document.createelementns(d3.ns.prefix.svg, 'svg');

score:1

i am working with version 4.4.4

var svg = document.createelementns(d3.namespaces.svg, "svg");

score:3

d3 author mike bostock suggests another (simpler) approach in his comment on an old d3 github "issue" asking about this very topic:

another strategy you could consider is to remove the element from the dom immediately after creating it:

var svg = d3.select("body").append("svg")
    .remove()
    .attr("width", w)
    .attr("height", w);

svg.append("circle")
    .attr("r", 200);

document.body.appendchild(svg.node());

this approach does indeed append the element on creation, but .removes it immediately prior to manipulation and creation of child elements, so there should be no browser repaint. while technically contrary to the original question, this is probably the most idiomatic way to meet the requirement.

score:8

here's an example function that creates an unattached group element:

function createsomething(){
  return function(){
    var group = d3.select(document.createelementns(d3.ns.prefix.svg, 'g'));
    // add stuff...
    return group.node();
  }
}

you can call it like so:

node.append(createsomething());

explanation

let's say you are rendering a collapsible tree and you want to have plus/minus icons with a circle border as the toggles. your draw function is already enormous so you want the code for drawing the plus sign in it's own function. the draw/update method will take care of proper positioning.

one option is to pass the existing container into a function:

createplus(node).attr({
  x: 10,
  y: 10
});

function createplus(node){
  var group = node.append('g');
  // add stuff...
  return group;
}

we can make this better by applying the technique from @drew and @paul for creating unattached elements.

node.append(createplus())
    .attr({
      x: 10,
      y: 10
    });

function createplus(){
  var group = d3.select(document.createelementns(d3.ns.prefix.svg, 'g'));
  // add stuff...
  return group;
}

except that throws an error because append() expects either a string or a function.

the name may be specified either as a constant string or as a function that returns the dom element to append.

so we just change it to:

node.append(function(){
  return createplus();
});

but that still doesn't work. it causes the following error:

typeerror: failed to execute 'appendchild' on 'node': parameter 1 is not of type 'node'.

luckily i found selection.node() which does work! though, admittedly, i have no idea why.

function createplus(){
  var group = d3.select(document.createelementns(d3.ns.prefix.svg, 'g'));
  // add stuff...
  return group.node();
}

we can save us a little more time by moving the anonymous function into createplus:

node.append(createplus())

function createplus(){
  return function(){
    var group = d3.select(document.createelementns(d3.ns.prefix.svg, 'g'));
    // add stuff...
    return group.node();
  }
}

score:15

finally, with the release of d3 v5 (march 22nd, 2018) this can now be done in d3 itself. this does not affect the other answers, whatsoever, which are still valid and in the end, d3 is going to use document.createelementns() like described in previous posts.

as of v5 you can now use:

# d3.create(name) <>

given the specified element name, returns a single-element selection containing a detached element of the given name in the current document.

this new feature can be used as follows:

// create detached <svg> element.
const detachedsvg = d3.create("svg");

// manipulate detached element.
detachedsvg
  .attr("width", 400)
  .attr("height", 200);

// bind data. append sub-elements (also not attached to dom).
detachedsvg.selectall(null)
  .data([50, 100])
  .enter()
  //...

 // attach element to dom.
 d3.select("body")
   .append(() => detachedsvg.node());

have a look at the following snippet for a working demo creating a detached sub-tree, which is attached on a click event:

// create detached <svg> element.
const detachedsvg = d3.create("svg");

// manipulate detached element.
detachedsvg
  .attr("width", 400)
  .attr("height", 200);

// bind data. attach sub-elements.
detachedsvg.selectall(null)
  .data([50, 100])
  .enter().append("circle")
    .attr("r", 20)
    .attr("cx", d => d)
    .attr("cy", 50);

// still detached. attach on click.
d3.select(document)
  .on("click", () => {
     // attach element to dom.
     d3.select("body")
       .append(() => detachedsvg.node());
  });
<script src="https://d3js.org/d3.v5.js"></script>

the main advantages of this approach are ease of use and clarity. the creation of the element is done by d3 behind the scenes and it is made available as a full-fledged selection with all its methods at hand. apart from manipulating the newly created detached element, the returned selection can easily be used for data-binding.

it is also worth noting, that the function is not restriced to create elements from the svg namespace but can be used to create an element from any namespace registered with d3.

score:24

to save a little bit of time you can use d3.ns.prefix.svg

var svg = document.createelementns(d3.ns.prefix.svg, 'svg');

Related Query

More Query from same tag