2

I have some pretty standard code that creates a bunch of SVG:circles based on node data and SVG:lines based on link data.

I want to put some rollover code on the circles and lines such that rolling over a circle highlights that circle, the lines that connect to it, and any other circles at the end of those lines.

The challenge I'm having is getting a reference to the link data when I'm handling an event triggered by the circle, which only has node datum attached.

d3nodes = d3svg.selectAll( ".node" )
    .data( nodes )
    .enter().append( "circle" )
        .attr( "class", "node" )
        .attr( "r", NODE_RADIUS )
        .attr( "cx", function( d ){ return d.x; } )
        .attr( "cy", function( d ){ return d.y; } )
        .on( "mouseover", function( d ){ console.log( d ); } ); // Node datum

d3links = d3svg.selectAll( ".link" )
    .data( links )
    .enter().append( "line" )
        .attr( "class", "link" )
        .attr( "x1", function( d ){ return nodes[d.source].x; } )
        .attr( "y1", function( d ){ return nodes[d.source].y; } )
        .attr( "x2", function( d ){ return nodes[d.target].x; } )
        .attr( "y2", function( d ){ return nodes[d.target].y; } )
        .on( "mouseover", function( d ){ console.log( d ); } ); // Link datum

Once I have a reference to links, I can use the following function to get a list of links where that node is either a source or a target:

/**
 * Gets up to 4 nodes, that represent all the nodes linked to this node
 * (either as targets or source).
 *
 * @param {Object} node Object from the "nodes" array (so, the data, not the d3nodes)
 * @param {Object[]} links Array of links (again, the data that was passed to the force layout)
 * @returns {Array} Matching node objects (again, not DOM elements)
 */
function getLinks( node, links ){
    var nodes = [],
        i = 0, l = links.length,
        link;

    for ( ; i < l; ++i ){
        link = links[i];
        if ( link.target === node ){
            nodes.push( link.source );
            continue;
        }

        if ( link.source === node ){
            nodes.push( link.target );
        }
    }

    return nodes;
}

But my function really needs a reference to the links array, and I'm not sure how to get it from within an event handler that only seems to get the node datum.

I'd like to avoid any kind of global variables, or a global lookup table, since I'm going to have any number of these graphs on a page, and they all need to stay separate.

In the past I've stored a reference to the nodes, links and force objects on the DOM element itself (using jQuery.data()), but is that really a best practice?

I feel like I'm missing an important conceptual point, and hope someone can shed some light.

Tom Auger
  • 19,421
  • 22
  • 81
  • 104
  • Have you seen [this question](https://stackoverflow.com/questions/8739072/highlight-selected-node-its-links-and-its-children-in-a-d3-force-directed-grap)? – Lars Kotthoff May 19 '15 at 19:12
  • @LarsKotthoff I had not, and I'm surprised it didn't come up in a search or suggestion - my Google-fu must be weak today. Mike's answer deals more with the performance of the link lookup as opposed to accessing / scoping of the link and node data, so I think there's overlap, but distinction between the two questions. – Tom Auger May 19 '15 at 19:56
  • Well it gives a way of doing what you're trying to do, right? As in, you could use that approach to make it work? I may be missing how your problem is different and why this approach doesn't apply. – Lars Kotthoff May 19 '15 at 20:04
  • 1
    @LarsKotthoff you're not wrong :) However, I'm not sure it completely addresses my situation, because I'm running any number of similar (but distinct) graphs on the same page. So I would need some sort of global structure that linked a particular graph to a list of nodes and links. I'm leaning toward adding a reference to the links within the node record (laughing in the face of the circular references this will cause)! – Tom Auger May 20 '15 at 13:41
  • Ok, I see. Yes, I think attaching link information to the node datum is the way to go then. – Lars Kotthoff May 20 '15 at 17:21

0 Answers0