Tag Archives: d3

D3 and jQuery Interoperability

D3 and jQuery utilize distinctly different approaches to node manipulation and internal array storage. However, there are mechanisms available for converting between D3 and jQuery, and vice versa.

D3 and jQuery Internals

Let’s assume the following HTML structure:

<div class="test-node">Test One</div>
<div class="test-node">Test Two</div>
<div class="test-node">Test Three</div>
<div class="test-node">Test Four</div>

Now let’s retrieve the “test-node” elements with jQuery:

var testNodes = $(".test-node");
console.log(testNodes);

Console Log

As can be seen above, jQuery stores the selected elements in a first level array, making elements accessible by testNodes[0…3] as expected.  Now let’s examine the D3 equivalent:

var testNodes = d3.selectAll(".test-node");
console.log(testNodes);

Console Log

When using D3, the selected elements get stored in a second level array making the elements accessible by testNode[0][0…n].

Converting from jQuery to D3

var $testNodes = $(".test-node");  //jquery
var testNodes = d3.selectAll( $testNodes.toArray() ); //d3
console.log(testNodes);

Console Log

The above first retrieves the nodes using standard jQuery selector syntax.  Next the “toArray()” method is called returning the native array of selected elements.  Finally the “d3.selectAll()” method is invoked using the native array as the argument.  This forces the native element array into the second level array of D3.

Converting from D3 to jQuery

var testNodes = d3.selectAll(".test-node");
var $testNodes = $(testNodes[0]);
console.log($testNodes);

Console Log

The above first retrieves the nodes using D3 selector syntax.  Next the first level D3 array is used as the argument passed into the jQuery selector.

Class Addition/Removal

Adding and removing a class in jQuery is pretty straightforward:

$(".test-node").addClass("selected"); //add class "selected"
$(".test-node").removeClass("selected"); //remove class "selected"

The same performed in D3 is performed using the following:

d3.selectAll(".test-node").classed("selected", true); //add class "selected"
d3.selectAll(".test-node").classed("selected", false); //remove class "selected"

Data/Datum

D3 has a very elegant data system whereby information can be bound to elements by use of the “datum()” method.

D3 Datum

Let’s examine the following:

var nodes = d3.selectAll(".test-node")
    .datum({key: "hello", value: "world"})
    .append("span");
 
console.log(nodes);

Console Log

The above example selects all of the “.test-node” div classed elements utilizing D3, initializes a data structure/object and passes this structure/object to the “span” elements which get appended to all the matching “.test-node” div classed elements.  This “datum()” method passes the data structure/object to the “span” elements using a property called “__data__”.  As displayed above, the __data__ property has the structure of:

__data__: {
    key: "hello",
    value: "world"
}

The “__data__” property can be accessed directly using second level array syntax (ie. d3.selectAll(“span”)[0][0…n].__data__) or it can be utilized as a default argument passed into nearly all D3 selector instance methods.  Let’s examine an instance method where this takes place:

var nodes = d3.selectAll(".test-node")
    .datum({key: "hello", value: "world"})
    .append("span")
    .text(function(d){
        return d.key + " " + d.value;
    });

In the above example, the only difference from the previous example is the instance method “text()”, whereby the argument passed is a function.  This function defines an argument “d” which D3 passes to these type of instance methods automatically.  The “d” argument being the actual data/datum used to initialize the “span” elements.  The body of the text function returns the string “hello world” using the data/datum, thusly setting the text of the “span” elements to “hello world”.

jQuery Data

jQuery has a simple mechanism for adding arbitrary data to elements by use of the “data()” method.  Let’s examine the following:

var nodes = $(".test-node")
    .append("span")
    .data("datum", {key: "hello", value: "world"});
 
console.log(nodes);
console.log($.cache);

Console Log

In the above example, the “.test-node” elements are retrieved, “span” elements are appended and the datum {key: “hello”, value: “world”} is added to the data.  The console output reveals that the datum is stored in the $.cache in jQuery.

Exchanging D3 Datum with jQuery

Now that we’ve seen how both D3 and jQuery store data/datum, let’s exchange D3 datum with jQuery:

//D3
var nodes = d3.selectAll(".test-node")
    .datum({key: "hello", value: "world"})
    .append("span");
 
console.log(nodes);
 
//jQuery
var $nodes = $(nodes[0]);
$nodes.each(function(){
    $(this).data("datum", $(this).prop("__data__"));
});
 
console.log($nodes);
console.log($.cache);

Console LogIn this example, we first select the “.test-node” classed elements using D3, append “span” elements using the datum/data specified.  Next, we create a jQuery selector using the first level array of the D3 selector.  Using the jQuery selector, the nodes are iterated over in “each” and the “data()” of the jQuery element is set to the “__data__” property of the D3 element.

Exchanging jQuery Data With D3

Now, let’s exchange jQuery data with D3 using a similar technique to the previous example:

//jQuery
var $nodes = $(".test-node")
    .append("span")
    .data("datum", {key: "hello", value: "world"});
 
console.log($nodes);
console.log($.cache);
 
//D3
var nodes = d3.selectAll($nodes.toArray())
    .datum({})
    .each(function(d){
        this.__data__ = $(this).data("datum");
    });
 
console.log(nodes);

Console LogIn the above example, the “.test-node” elements are selected using jQuery, the span child elements are appended and the “data()” method is used to store the “datum” {key: “hello”, value: “world”}.  Next, the D3 selector is populated using the “toArray()” method of the jQuery selector, the “datum()” method is used to initialize an empty data object and finally the nodes are iterated over and the __data__ property is populated with the jQuery data “datum”.

Conclusion

Both frameworks are very powerful and can live together in peace.  Information can be exchanged freely between the the two frameworks utilizing these or similar techniques.

D3 Drag And Drop

Implementing drag and drop in D3 (http://d3js.org/) is a pretty simple task when you know how to do it, the following is a walkthrough from setup to execution and the events along the way.

First thing we are going to want to do is setup our drag behavior.  This is the object that will be responsible for handling the actual drag and drop, events and will be added to the call chain of the objects of which we want to allow the drag and drop behavior.

var drag = d3.behavior.drag();

The above statement initializes a new instance of the drag behavior.  We can use it to bind this behavior to the call chain of other objects.  Let’s assume we want to allow all nodes having class “draggable” to have this drag and drop behavior.  To do this we simply add the drag behavior we just instantiated to the call chain of the objects:

var drag = d3.behavior.drag();
d3.selectAll(".draggable").call(drag);

It’s that simple, now all objects containing the “draggable” class will now have this behavior.  But what if we need to do something a bit more complex?  What if we need to have other things happen along the way, update other areas during a drag, initialize or change variables on start and end drag.

var drag = d3.behavior.drag()
    .on("dragstart", function(){
        //do some drag start stuff...
    })
    .on("drag", function(){
        //hey we're dragging, let's update some stuff
    })
    .on("dragend", function(){
        //we're done, end some stuff
    });

As you would expect, each on(…) call defines a hook into the event chain of the drag behavior allowing you to customize the behavior as you see fit.