.append() to different selection depending on condition? - d3.js

I have a similar issue as in Updating SVG Element Z-Index With D3
My preferred solution would be to have two groups within my svg as described in the answer by notan3xit.
But I have one data set, and a boolean flag on one of the data properties determines to which group the svg element belongs.
svg.selectAll("path")
.data(d)
.enter()
.if(d.property).appendToGroup1()
.else.appendToGroup2()
This obviously doesn't work, but something like this.
I only want to iterate through the data once, and during runtime append the generated svg elements to the according groups.
Any ideas how to go about achieving that with d3.js?

Try
.each(function(d){
var toAppend = $(this);
if (!!d.property){
toAppend.appendtoGroup1();
} else {
toAppend.appendToGroup2();
}
})//...
The general idea would be to pass d into a callback, and apply the right append method inside that callback.
It should be cleaner to first save reference to what you want to append to, in an enclosing scope so you can easily call .append() on it.

Try this:
var selectedPath=svg.selectAll("path").data(d);
svg.selectAll("path").each(function(d,i){
if(d3.select(this).attr("property's name"))
selectedPath.append("some data to group 1")
else
selectedPath.append("some data to group 2")
});
If I had your code better able to help.

Related

D3.js: How to get the selection index, instead of the data index?

There are a lot of instances where i need to act upon the index of the element inside it's selection group (update, enter, exit), instead of it's index within the data. How can i get this index?
For example, if i am exiting 4 elements.
legends.exit()
.transition()
.delay(d=>i*25)
.style("opacity",0)
i in this case are [8,12,15,20]. How do i set the index to be [0,1,2,3];
My quick and dirty solution is this. Can i do this within the context of d3?
var i = 0;
legends.exit()
.transition()
.delay(d=>i++*25)
.style("opacity",0)
You should be able to adapt the following code to your solution:
.delay(function(d,i){ return (i-legends.size()); })
Alternatively, the following worked in my small test example but I'm not sure why exactly. The filter is necessary.
legends.exit().filter(function(d){return true;})
.transition()
.delay((d,i)=>i*25)
.style("opacity",0)
My best guess would be that applying the filter 'resets' the indexing of the selection.

Extracting a selection of countries from JSON file

I'm using this great example: Countries By Area
However I'm wanting to modify this code for my own use and project only a selection of chosen countries.
I've managed to read the JSON file into an array, and the code is now looking through that array but I don't see any way on rendering a country (or countries) if a criteria is met.
For example, if I want to simply render the country, who has an ID of 533, I don't see any way of attaching a condition.
Can anyone shed any light on how I may be able to do this.
Have edited my original question here as I've managed to do it, but I'm sure there's a more elegant way of achieving it:
Original code was:
var svg = d3.select("#map").selectAll("svg")
.data(topojson.feature(world, world.objects.countries).features)
Which I've changed to:
var svg = chartDetails.plotArea.selectAll("svg")
.data(
function(d){
a = topojson.feature(tempWorld, tempWorld.objects.countries).features
var returnobject =[]
$.each(a, function (i, v) { if (v.id == 826) { returnobject.push(v) } });
return returnobject
})
826 refers to United Kingdom.
If your array looks like this:
var a = [];
a.push({id:1, name:'Argentina'});
a.push({id:2, name:'Bahamas'});
Then you might need to iterate it to find the key(id) you want.
Take a look at these solutions:

Getting d3.js data from html

I'm new to d3.js and trying to understand how to retrieve elements that are already on an html page.
If I try something like the following, 'd' is undefined when I look in the console log. I can access some of the td information via the 'this' keyword, but looking at the d3 API https://github.com/mbostock/d3/wiki/Selections#wiki-style, it says if style takes a value as a function, it passes the current 'datum' and index.
I can get the index fine, but datum is always undefined. Is there something obvious I'm missing to retrieve the values, or have the wrong way around ?
<table class="results">
<tr><td>13/1/0014</td><td>81</td><td>3</td><td></td></tr>
<tr><td>12/1/0014</td><td>690</td><td>47</td><td></td></tr>
<tr><td>5/1/0014</td><td>450</td><td>26</td><td></td></tr>
</table>
d3.selectAll(".results td:nth-child(4n+2)")
.style("background-color", function(d) {
console.log( d );
//change style depending on d, but d is always undefined
//I can access the elements via this, but not d ?
});
jsfiddle http://jsfiddle.net/a5WkH/2/
D3 assumes that you bind data to the elements using .data() or .datum(). If you haven't done that, something like function(d) { ... } won't work. Technically what happens is that D3 adds the data bound to a DOM element as the .__data__ attribute to that element. So if you really don't want to use .data() or .datum(), you could do something like this.
d3.selectAll(".results").each(function() { this.__data__ = d3.select(this).text(); });
This is what I have done here. It is usually better to use .data() though.

Calling a function once when data enters in d3

Is there a way to call a function when data enters using data().enter()?
For example, in this current jsfiddle: http://jsfiddle.net/p3m8A/4/ , I have a function that draws a group and I want to call this function when new data enters. The current jsfiddle doesn't do anything but the objective is to click on the red square and using .data.enter draw a purple square when the red square is clicked.
The specific part I'm trying to get to work is :
canvas.selectAll("#boxGroup")
.data(data)
.enter().function(d,i) {
drawBox(150,20,d);
};
Thanks
The callback passed to the call method is actually passed the selection, not the data.
enterSelection.call(function(selection){/* this === selection */});
So, what you were probably looking for is the each method.
enterSelection.each(function(d, i){/* this is selection */ drawBoxes(150,20,d);});
You want the method .call(function(d))
This will run your function once, passing d as the array of all of the data you have provided. i is not defined for using call after enter().
If you want to draw multiple boxes, based on d, your code would look something like this:
canvas.selectAll("boxGroup")
.data(data)
.enter()
.call(function(d){drawBoxes(150,20,d);});
I've created a basic fiddle of this here.
Note that this is what you want to use if you want to call a function on the selection returned by .enter() in the same spot as you're using it. It's also possible to bind a function to the enter event of a given DOM element by using .on('enter',function), but this would require that the element that you are entering data into already exist.

How data in a d3 scatterplot could be referenced/called outside d3 selection

I have a d3 scatterplot and want to know how to get the value of the data appended to one single record (circle) in order to call it later in the code.
Say we have the following data:
ID X Y
A 1 1
B 2 2
C 3 3
How can I get the X value of the record B?
Does anyone have an idea how to do it?
thx
You may need to show more code, as it depends on how you're attaching the data. But assuming that you have something like
var circle = svg.selectAll("circle").data(myArrayOfDataObjects)
.enter().append("circle");
You can assign an ID or class to the DOM element you create:
circle.attr("id", function(d) { return "dataRow" + d.ID; });
Now you can use d3, plain JS, or the library of your choice to get a handle on the DOM element later on. D3 attaches its data as a __data__ property on the DOM element, so you can reference that to get your datum. D3 also provides the .datum() method to get this value:
var myDataRow = d3.selectAll("#dataRowB").datum();
var xValue = myDataRow.X;
Or, in plain JavaScript:
var myDataRow = document.getElementById("#dataRowB").__data__;
// etc
If jQuery is an option, in your d3 you can use the jQuery .data() function to attach the d3 data directly to the SVG element. Simply run something like .each(function(d){ $(this).data(d) }).
You can then access the data anywhere by using $(selector).data(), where selector points to the DOM element or elements that you're interested in, with something like '#RecordB'.
If your data is more complex, (say, d = {foo: 'foo', bar: 'bar'}), you'd use $(selector).data()[foo] to grab whatever data is keyed to 'foo' in the data associated with whatever is pointed at by the selector.

Resources