d3js force directed - On hover to node, highlight/colourup linked nodes and links?

11,205

The way I solved this problem in my own project was to examine the links to see if their source or target properties matched the hovered node. This should be a step in the right direction.

// On node hover, examine the links to see if their
// source or target properties match the hovered node.
node.on('mouseover', function(d) {
  link.style('stroke-width', function(l) {
    if (d === l.source || d === l.target)
      return 4;
    else
      return 2;
    });
});

// Set the stroke width back to normal when mouse leaves the node.
node.on('mouseout', function() {
  link.style('stroke-width', 2);
});

Updated fiddle: http://jsfiddle.net/2pdxz/2/

Share:
11,205
dzordz
Author by

dzordz

Updated on June 04, 2022

Comments

  • dzordz
    dzordz almost 2 years

    I have force directed graph with 4 types of nodes and two types of links. One type of node (the small blue one) I'm using as "connection node" between two bigger nodes.

    on mouseover node, it gets bigger, but also I wanna colour up "connections" to this node in different color. So whey you hover over a node... all links and little "connection nodes" gets in the different color, so you can know to which other bigger node is this one hovered connected.

    You can see my situation on following link: http://jsfiddle.net/2pdxz/

    For example: If you hover up Google node, links to Yahoo with "connection" node gets highlighted and links to CDO and CTO gets highlighted

    I've tried to add this part of code:

    function fade(opacity) 
    {  link.style("opacity", function(d) {
       return d.source === d || d.target === d ? 1 : opacity;
                    });
                
                }
    

    which I picked up from some other question with the similar problem but I don't know how this could actually help me, because I'm noob about d3js.

    The exact code looks like this

    Script:

    var data = {"nodes":[
                                {"name":"YHO", "full_name":"Yahoo", "type":1, "slug": "www.yahoo.com", "entity":"company", "img_hrefD":"", "img_hrefL":""},
                                {"name":"GGL", "full_name":"Google", "type":2, "slug": "www.google.com", "entity":"company", "img_hrefD":"", "img_hrefL":""},
                                {"name":"BNG", "full_name":"Bing", "type":2, "slug": "www.bing.com", "entity":"company", "img_hrefD":"", "img_hrefL":""},
                                {"name":"YDX", "full_name":"Yandex", "type":2, "slug": "www.yandex.com", "entity":"company", "img_hrefD":"", "img_hrefL":""},
                                
                                {"name":"Desc1", "type":4, "slug": "", "entity":"description"},
                                {"name":"Desc2", "type":4, "slug": "", "entity":"description"},
                                {"name":"Desc4", "type":4, "slug": "", "entity":"description"},
                                
                                {"name":"CEO", "prefix":"Mr.", "fst_name":"Jim", "snd_name":"Bean", "type":3, "slug": "", "entity":"employee"},
                                {"name":"ATT", "prefix":"Ms.", "fst_name":"Jenna", "snd_name":"Jameson", "type":3, "slug": "", "entity":"employee"},
                                {"name":"CTO", "prefix":"Mr.", "fst_name":"Lucky", "snd_name":"Luke", "type":3, "slug": "", "entity":"employee"},
                                {"name":"CDO", "prefix":"Ms.", "fst_name":"Pamela", "snd_name":"Anderson", "type":3, "slug": "", "entity":"employee"},
                                {"name":"CEO", "prefix":"Mr.", "fst_name":"Nacho", "snd_name":"Vidal", "type":3, "slug": "", "entity":"employee"},
                            ], 
                    "links":[
                                {"source":0,"target":4,"value":1,"distance":5},
                                {"source":0,"target":5,"value":1,"distance":5},
                                {"source":0,"target":6,"value":1,"distance":5},
                                
                                {"source":1,"target":4,"value":1,"distance":5},
                                {"source":2,"target":5,"value":1,"distance":5},
                                {"source":3,"target":6,"value":1,"distance":5},
                                
                                {"source":7,"target":3,"value":10,"distance":6},
                                {"source":8,"target":3,"value":10,"distance":6},
                                {"source":9,"target":1,"value":10,"distance":6},
                                {"source":10,"target":1,"value":10,"distance":6},
                                {"source":11,"target":2,"value":10,"distance":6},
                                ]
                       }    
                
                
                
            var w = 560,
                h = 500,
                radius = d3.scale.log().domain([0, 312000]).range(["10", "50"]);
            
            var vis = d3.select("body").append("svg:svg")
                .attr("width", w)
                .attr("height", h);
                
                //vis.append("defs").append("marker")
                //.attr("id", "arrowhead")
                //.attr("refX", 22 + 3) /*must be smarter way to calculate shift*/
                //.attr("refY", 2)
                //.attr("markerWidth", 6)
                //.attr("markerHeight", 4)
                //.attr("orient", "auto")
                //.append("path")
                    //.attr("d", "M 0,0 V 4 L6,2 Z"); //this is actual shape for arrowhead
            
            //d3.json(data, function(json) {
                var force = self.force = d3.layout.force()
                    .nodes(data.nodes)
                    .links(data.links)
                    .linkDistance(function(d) { return (d.distance*10); })
                    //.friction(0.5)
                    .charge(-250)
                    .size([w, h])
                    .start();
            
            
            
                var link = vis.selectAll("line.link")
                    .data(data.links)
                    .enter().append("svg:line")
                    .attr("class", function (d) { return "link" + d.value +""; })
                    .attr("x1", function(d) { return d.source.x; })
                    .attr("y1", function(d) { return d.source.y; })
                    .attr("x2", function(d) { return d.target.x; })
                    .attr("y2", function(d) { return d.target.y; })
                    .attr("marker-end", function(d) {
                                                        if (d.value == 1) {return "url(#arrowhead)"}
                                                        else    { return " " }
                                                    ;});
    
                function fade(opacity) {
                    
                        link.style("opacity", function(d) {
                            return d.source === d || d.target === d ? 1 : opacity;
                        });
                    
                    }
                    
                    
                function openLink() {
                    return function(d) {
                        var url = "";
                        if(d.slug != "") {
                            url = d.slug
                        } //else if(d.type == 2) {
                            //url = "clients/" + d.slug
                        //} else if(d.type == 3) {
                            //url = "agencies/" + d.slug
                        //}
                        window.open("//"+url)
                    }
                }
                    
            
                
            
                var node = vis.selectAll("g.node")
                    .data(data.nodes)
                  .enter().append("svg:g")
                    .attr("class", "node")
                    .call(force.drag);
            
                
                node.append("circle")
                    .attr("class", function(d){ return "node type"+d.type})
                    .attr("r",function(d){if(d.entity == "description"){ return 6 } else { return 18 }})
                    //.on("mouseover", expandNode);
                    //.style("fill", function(d) { return fill(d.type); })
                    
                            
            
                node.append("svg:image")
                    .attr("class", function(d){ return "circle img_"+d.name })
                    .attr("xlink:href", function(d){ return d.img_hrefD})
                    .attr("x", "-36px")
                    .attr("y", "-36px")
                    .attr("width", "70px")
                    .attr("height", "70px")
                    .on("click", openLink())
                    .on("mouseover", function (d) { if(d.entity == "company")
                                                        {
                            d3.select(this).attr("width", "90px")
                                            .attr("x", "-46px")
                                            .attr("y", "-36.5px")
                                           .attr("xlink:href", function(d){ return d.img_hrefL});                           
                                                        }
                        })
                    .on("mouseout", function (d) { if(d.entity == "company")
                                                    {
                            d3.select(this).attr("width", "70px")
                                            .attr("x", "-36px")
                                            .attr("y", "-36px")
                                           .attr("xlink:href", function(d){ return d.img_hrefD});
                                                    }
                        });    
                        
            
                node.append("text")
                    .attr("class", function(d){ return "nodetext title_"+d.name })
                    .attr("dx", 0)
                    .attr("dy", ".35em")
                    .style("font-size","10px")
                    .attr("text-anchor", "middle")
                    .style("fill", "white")
                    .text(function(d) { if (d.entity != "description"){return d.name} });
                    
                    
    
                node.on("mouseover", function (d) {
                    if (d.entity == "company"){   
                        d3.select(this).select('text')
                            .transition()
                            .duration(300)
                            .text(function(d){
                                    return d.full_name;
                                })
                            .style("font-size","15px")
                            
                    }
                    else if(d.entity == "employee"){
                        var asdf = d3.select(this);
                        asdf.select('text').remove();
                        
                        asdf.append("text")
                                    .text(function(d){return d.prefix + ' ' + d.fst_name })
                                    .attr("class","nodetext")
                                    .attr("dx", 0)
                                    .attr("dy", ".35em")
                                    .style("font-size","5px")
                                    .attr("text-anchor", "middle")
                                    .style("fill", "white")
                                    .transition()
                                    .duration(300)
                                    .style("font-size","12px");
                        
                        asdf.append("text").text(function(d){return d.snd_name })
                                    .attr("class","nodetext")
                                    .attr("transform","translate(0, 12)")
                                    .attr("dx", 0)
                                    .attr("dy", ".35em")
                                    .style("font-size","5px")
                                    .attr("text-anchor", "middle")
                                    .style("fill", "white")
                                    .transition()
                                    .duration(300)
                                    .style("font-size","12px");                                         
                    }
                    else {
                        d3.select(this).select('text')
                            .transition()
                            .duration(300)
                            .style("font-size","15px")
                    }
                            
                    if (d.entity == "company") {
                        d3.select(this).select('image')
                            .attr("width", "90px")
                            .attr("x", "-46px")
                            .attr("y", "-36.5px")
                            .attr("xlink:href", function (d) {
                                return d.img_hrefL
                                });               
                    }
                    
                    if (d.entity == "company") {
                    
                        d3.select(this).select('circle')
                                        .transition()
                                        .duration(300)
                                        .attr("r",28)
                                        
                    }
                    else if (d.entity == "employee"){
                        d3.select(this).select('circle')
                                        .transition()
                                        .duration(300)
                                        .attr("r",32)
                    }
                })
                
                 
                 node.on("mouseout", function (d) {
                    if (d.entity == "company") {
                        d3.select(this).select('text')
                            .transition()
                            .duration(300)
                            .text(function(d){return d.name;})
                            .style("font-size","10px")
                        }
                    else if(d.entity == "employee"){
                        ///////////////////////////
                        // CHANGE
                        ///////////////////////////
                        
                        d3.select(this).selectAll('text').remove();
                        
                        //d3.select(this).select('text')
                        d3.select(this).append('text')
                            .text(function(d){return d.name;})
                            .style("font-size","14px")  
                            .attr("dx", 0)
                            .attr("dy", ".35em")
                            .attr("text-anchor", "middle")
                            .style("fill", "white")
                            .attr("class","nodetext")
                            .transition()
                            .duration(300)
                            .style("font-size","10px")
                        
                    }
                    else {
                        d3.select(this).select('text')
                            .transition()
                            .duration(300)
                            .style("font-size","10px")
                    }
                            
                        
                     if (d.entity == "company") {
                        d3.select(this).select('image')
                            .attr("width", "70px")
                            .attr("x", "-36px")
                            .attr("y", "-36px")
                            .attr("xlink:href", function (d) {
                            return d.img_hrefD
                        });
                    }
                    
                    if (d.entity == "company" || d.entity == "employee") {
                    
                        d3.select(this).select('circle')
                                        .transition()
                                        .duration(300)
                                        .attr("r",18)
                    }
                    
                });
            
                force.on("tick", function() {
                  link.attr("x1", function(d) { return d.source.x; })
                      .attr("y1", function(d) { return d.source.y; })
                      .attr("x2", function(d) { return d.target.x; })
                      .attr("y2", function(d) { return d.target.y; });
            
                  node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
                });
            //});
    

    CSS:

    @charset "utf-8";
    /* CSS Document */
    
    .link10 { stroke: #ccc; stroke-width: 2px; stroke-dasharray: 3, 3; }
    .link1 { stroke: #000; stroke-width: 2px;}
    .nodetext { pointer-events: none;}
    
    .node.type1 {
      fill:#690011;
    }
    .node.type2 {
      fill:#BF0426;
    }
    .node.type3 {
      fill:#E5B96F;
    }
    .node.type4 {
      fill:#FFFFFF;
      stroke:#1695A3; 
      stroke-width: 3px;
    }
    
    .node.type5 {
        fill:#1BC9E0;
    }
    
    .node.type6 {
        fill:#E01B98;
    }
    
    image.circle {
        cursor:pointer;
    }
    
    .fadein{
    display:none;
    font-size:20px;
    }
    
    .rectD{
    background-color:#000000;
    width:70px;
    height:30px
    }
    
    .rectL{
    background-color:#000000;
    width:90px;
    height:30px
    }
    

    You can edit and see working example in my linked jsfiddle..

    Any suggestion or help is welcome..