Reputation: 5631
Is there a simple way to expand the following piece of code so that div.someclass
be automatically created if it does not exist?
d3.select("body").select("div.someclass").selectAll("p")
.data([ 1, 2, 3 ])
.enter()
.append("p")
.text(function (d, i) {
return "index: " + i + ", value: " + d;
});
I'm still at the early stages of learning D3JS. My understanding of it so far is that "instead of telling D3 how to do something, you tell D3 what you want". So I was surprised to see that the above piece of code requires <div class="someclass"></div>
to be declared in the HTML.
An alternative is to insert the div programmatically:
/** Append HTML placeholder programmatically **/
placeholder = d3.select("body").append("div").attr("class", "someclass");
/** Bind the data to the DOM **/
/** instead of telling D3 how to do something, tell D3 what you want: in the absence of <p>, this will return a virtual selection **/
placeholder.selectAll("p")
.data([ 1, 2, 3 ])
.enter()
.append("p")
.text(function (d, i) {
return "index: " + i + ", value: " + d;
});
Is there a shorter/better way?
Upvotes: 4
Views: 22483
Reputation: 55678
If I understand correctly, you're asking how to append div.someClass
IFF it doesn't exist. There's a D3 pattern for this, though it's a bit odd:
// create a selection for the container with a static 1-element array
var container = d3.select("body").selectAll("div.someclass")
.data([0]);
// now add it if it doesn't exist
container.enter().append('div').attr('class', 'someclass');
// now use it for the subselection
container.selectAll("p")
.data([1, 2, 3]);
// etc
The odd part here is .data([0])
- the 0
is conventional, not required; this could be any static single-element array. The first time this is called, a new element will be created (unless something else created div.someclass
first). The second time, the element is already there, so there's no .enter()
selection and nothing more will be appended.
This is a fairly common pattern in reusable components that are meant to be called repeatedly on update - see the d3.svg.axis
code for an example.
Upvotes: 12
Reputation: 5323
The way you're doing it is the right way. Usually, you add classes programmatically to the nodes that you're creating programmatically. For example, you're creating new <p>
elements so that would be the time to add a class there if you wanted one:
placeholder.selectAll("p")
.data([ 1, 2, 3 ])
.enter()
.append("p")
.attr("class", "myclass") <-- add class here
.text(function (d, i) {
return "index: " + i + ", value: " + d;
});
With elements for which you're not managing the lifecycle using D3, you need to also manage the attributes on those elements in some other way. This could be hard-coded in the initial HTML or you can add special handling exactly as you have.
Alternatively, there is also an API called selection.classed that will add or remove classes if and only if they are not already there. This can be handy if your element has potentially many classes and you just want to toggle a single one out of the list (as opposed to selection.attr("class", "myclass"
) which will clobber the entire attribute.
Upvotes: 0