Saturday 16 March 2013

Directional Force Layout Diagram (Node Highlighting)

The following post is a portion of the D3 Tips and Tricks document which is free to download. To use this post in context, consider it with the others in the blog or just download the pdf  and / or the examples from the downloads page:-)

-------------------------------------------------------


Following on from the Basic Force Layout Diagram, our next goal is to highlight our nodes so that we can get a better view of what ones they are (the view can get a little crowded as the nodes begin to increase in number).

To do this we are going to use a couple more of the mouse events that we first introduced in the tooltips section.

For this example we are going to use the  `click` event (Triggered by a mouse click (mousedown and then mouseup over an element)) and the `dblclick` event (Triggered by two clicks within a short time over an element).

The single click will enlarge the node and the associated text and the double click will return the node and test to its original size.

The way to implement this is to first set a hook to capture when the event occurs, which calls a function which is laid out later in the script.

The hook is going to be part of the JavaScript where we define our nodes;
var node = svg.selectAll(".node")
    .data(force.nodes())
  .enter().append("g")
    .attr("class", "node")
    .on("click", click)        // Add in this line
    .on("dblclick", dblclick)  // Add in this line too
    .call(force.drag);
The two additional lines above tell the script that when it sees a click or a double-click on the node (since it's part of the node set-up) to run either the `click` or `dblclick` functions.

The following two function blocks should be placed after the `tick` function but before the closing curly bracket and bracket as indicated;
function tick() {
    path.attr("d", function(d) {
        var dx = d.target.x – d.source.x,
            dy = d.target.y – d.source.y,
            dr = Math.sqrt(dx * dx + dy * dy);
        return "M" + 
            d.source.x + "," + 
            d.source.y + "A" + 
            dr + "," + dr + " 0 0,1 " + 
            d.target.x + "," + 
            d.target.y;
    });

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

        // <= Put the functions in here!

});
 The `click` function is as follows;
function click() {
    d3.select(this).select("text").transition()
        .duration(750)
        .attr("x", 22)
        .style("fill", "steelblue")
        .style("stroke", "lightsteelblue")
        .style("stroke-width", ".5px")
        .style("font", "20px sans-serif");
    d3.select(this).select("circle").transition()
        .duration(750)
        .attr("r", 16)
        .style("fill", "lightsteelblue");
}
 The first line declares the function name (`click`). Then we select the node that we've clicked on and then the associated text before we begin the declaration for our transition (`d3.select(this).select("text").transition()`).

Then we define the new properties that will be in place after the transition. We move the text's x position (`.attr("x", 22)`), make the text fill steel blue (`.style("fill", "steelblue")`), set the stroke around the edge of the text light steel blue (`.style("stroke", "lightsteelblue")`), set that stroke to half a pixel wide (`.style("stroke-width", ".5px")`) and increase the font size to 20 pixels (`.style("font", "20px sans-serif");`).

Then we do much the same for the circle component of the node. Select it, declare the transition, increase the radius and change the fill colour.

The `dblclick` function does exactly the same as the `click` function, but reverses the action to return the text and circle to the original settings.
function dblclick() {
    d3.select(this).select("circle").transition()
        .duration(750)
        .attr("r", 6)
        .style("fill", "#ccc");
    d3.select(this).select("text").transition()
        .duration(750)
        .attr("x", 12)
        .style("stroke", "none")
        .style("fill", "black")
        .style("stroke", "none")
        .style("font", "10px sans-serif");
}
 The end result is a force layout diagram where you can click on nodes to increase their size (circle and text) and then double click to reset them if desired.

And here's a live version.

The code and data for this example can be found as Directional Force Layout Diagram with Node Highlighting on bl.ocks.org.

The above description (and heaps of other stuff) is in the D3 Tips and Tricks book that can be downloaded for free (or donate if you really want to :-)).

5 comments:

  1. not sure if you want to but if you wanted to include the bl.ocks.org in your post, you could just do an iframe like

    iframe src="http://www.w3schools.com" width="960" height="500"></iframe

    Keep up the good work. I really appreciate your posts.

    ReplyDelete
    Replies
    1. Great suggestion. I played with the idea a couple of months back with Plunker, but never considered it for bl.ocks.org. I've added it in above to see how it goes, but I'll probably experiment with it a bit to get a good fit with blogger (I presume I'll have to amend the original to avoid scroll bars etc). Likewise I will want to have a version that those with older browsers can handle, so I'll retain the images. But this is a great suggestion. A winner. Cheers!

      Delete
  2. Excelent examples and code. I've an issue with drag and click. I would want to being able to drag the nodes without triggering the onclick event. It's anyway i can do this?

    I'm trying to accomplish this in:
    http://autores.uy/node/3668

    (As you can see, when you click on a circle to drag it, after you release the mouse button, the onclick action is triggered, which takes you to the url of the person).

    Thanks for any help you can give me.

    ReplyDelete
    Replies
    1. Apologies for the extremely late answer to your question. I expect that you have moved on, but I'll try and provide some guidance for anyone else who might read.
      Interesting question. At first read it seemed a little 'weird' but its more interesting the more you consider it.
      My first thought is that if the user changed the drag behaviour to clicking and dragging with the right mouse button that might work, but that's not really a good way forward (the user should work normally right?). An alternative might be to select nodes using a drag-select type feature and then move the selected nodes with the keyboard? Perhaps with the mouse hovered, the user could press the space bar to allow the node to be moved? An interesting question. Thanks for answering and again apologies for the late reply.

      Delete
  3. This comment has been removed by a blog administrator.

    ReplyDelete