0

Using the forceNetwork function of the networkD3 package, it is possible to create an interactive network graph that can show the node names when hovering over them.

I am trying to create a graph that not only shows the node where the mouse is hovering over, but also all neighboring nodes, i.e. all nodes that are directly connected to the selected node. However, it should not show any nodes that are not directly connected to the node.

Although I found the argument opacityNoHover, it will affect all the nodes that the mouse is not covering and not just the nodes with a direct connection.

library(networkD3)
# example data
data(MisLinks)
data(MisNodes)

# creating the plot
 forceNetwork(Links = MisLinks, Nodes = MisNodes,
         Source = "source", Target = "target",
         Value = "value", NodeID = "name",
         Group = "group", opacity = 1, fontSize = 15,
         opacityNoHover = 0)
CJ Yetman
  • 8,373
  • 2
  • 24
  • 56
Fred
  • 410
  • 3
  • 12

1 Answers1

3

You could re-write the mouseover and mouseout functions and override them with htmlwidgets::onRender...

library(networkD3)
library(htmlwidgets)

data(MisLinks)
data(MisNodes)

fn <- forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 
                   Target = "target", Value = "value", NodeID = "name",
                   Group = "group", opacity = 1, fontSize = 15, 
                   opacityNoHover = 0)
customJS <- '
function(el,x) { 
    var link = d3.selectAll(".link")
    var node = d3.selectAll(".node")

    var options = { opacity: 1,
                    clickTextSize: 10,
                    opacityNoHover: 0.1,
                    radiusCalculation: "Math.sqrt(d.nodesize)+6"
                  }

    var unfocusDivisor = 4;

    var links = HTMLWidgets.dataframeToD3(x.links);
    var linkedByIndex = {};

    links.forEach(function(d) {
      linkedByIndex[d.source + "," + d.target] = 1;
      linkedByIndex[d.target + "," + d.source] = 1;
    });

    function neighboring(a, b) {
      return linkedByIndex[a.index + "," + b.index];
    }

    function nodeSize(d) {
            if(options.nodesize){
                    return eval(options.radiusCalculation);
            }else{
                    return 6}
    }

    function mouseover(d) {
      var unfocusDivisor = 4;

      link.transition().duration(200)
        .style("opacity", function(l) { return d != l.source && d != l.target ? +options.opacity / unfocusDivisor : +options.opacity });

      node.transition().duration(200)
        .style("opacity", function(o) { return d.index == o.index || neighboring(d, o) ? +options.opacity : +options.opacity / unfocusDivisor; });

      d3.select(this).select("circle").transition()
        .duration(750)
        .attr("r", function(d){return nodeSize(d)+5;});

      node.select("text").transition()
        .duration(750)
        .attr("x", 13)
        .style("stroke-width", ".5px")
        .style("font", 24 + "px ")
        .style("opacity", function(o) { return d.index == o.index || neighboring(d, o) ? 1 : 0; });
    }

    function mouseout() {
      node.style("opacity", +options.opacity);
      link.style("opacity", +options.opacity);

      d3.select(this).select("circle").transition()
        .duration(750)
        .attr("r", function(d){return nodeSize(d);});
      node.select("text").transition()
        .duration(1250)
        .attr("x", 0)
        .style("font", options.fontSize + "px ")
        .style("opacity", 0);
    }

    d3.selectAll(".node").on("mouseover", mouseover).on("mouseout", mouseout);
}
'
onRender(fn, customJS)

enter image description here

CJ Yetman
  • 8,373
  • 2
  • 24
  • 56