javascriptpythonnetworkxvis.jsvis.js-network

Vis.js How to show only children/descendents of selected node?


If not posible with vis.js, I could do the entire thing on something else. But this functionality is crucial. So, show everything if nothing selected; show only children (with the "from" arrow) of some node if that node is selected. Or to select the node in some list, or type it somewhere. https://codepen.io/andre-fr-silva/pen/ZEBPpqK

var container = document.getElementById("mynetwork");
var data = {
  nodes: nodes,
  edges: edges,
};

Solution

  • Direct Decendents

    This can be achieved using the select event defined in the documentation here in combination with the hidden property on nodes defined here. You can update the data in the DataSet, the network will then display the updates. In summary the logic I would suggest is to:

    1. If no nodes are selected
      1. Unhide all nodes
    2. If one or more nodes are selected
      1. Hide all nodes
      2. Display the selected nodes
      3. Display nodes connected from the selected node

    The following implements this logic.

    network.on('select', function (properties) {
      // Define an array of nodes ot update, this is quicker than
      // updating each node individually
      let nodesToUpdate = [];
      
      // If no nodes are selected, unhide all hidden nodes
      if(properties.nodes.length === 0){
        // Populate array with list of nodes to unhide
        data.nodes.forEach(node => {
          if(node.hidden){ 
            nodesToUpdate.push({id:node.id, hidden: false});
          }
        });
        
        // Update nodes and return
        data.nodes.update(nodesToUpdate);
        return;
      }
        
      // One or more nodes are selected
      // Populate array with list of all nodes, hiding them
      data.nodes.forEach(node => {
        nodesToUpdate.push({id:node.id, hidden: true});
      });
    
      // Pouplate array with list of selected and connected nodes to unhide
      // Note: Nodes will already exist in the array, but these later updates
      //       will overwrite the earlier ones.
      properties.nodes.forEach(node => {
        // Add selected node
        nodesToUpdate.push({id:node, hidden:false});
        
        // Add connected nodes to the selected node
        data.edges.forEach(edge => {
          // Unhide if connected from selected node and connected node exists
          if(edge.from === node && data.nodes.get(edge.to)){
            nodesToUpdate.push({id:edge.to, hidden: false});
          }
        });
      }); 
      
      // Submit updates to hide/unhide nodes
      data.nodes.update(nodesToUpdate);
    });
    

    Please note this could be optimised further, removing or updating items in the nodesToUpdate array instead of duplicating them.

    All Decendents

    A similar approach can be used to show all dependents. Using recursion is the easiset way to achieve this, with checks to make sure the code doesn't get caught in am endless loop (if one exists in the network). In the sample code below a new function named addChildNodes is declared that is called recursively to add each nodes children as the edges are followed.

    function addChildNodes(nodesToUpdate, node){
      // Add child nodes for the passed node
      // Loop around all edges
      data.edges.forEach(edge => {
        // Check if connected from the passed node and connected node exists
        if(edge.from === node.id && data.nodes.get(edge.to)){
          // Find the child node in the update array
          let childNode = nodesToUpdate.find(node => node.id === edge.to);
        
          // Check if the child node is hidden
          // If the node is not hidden then it's already been processed
          // Don't process it again otherwise could get caught in a loop
          if(childNode.hidden){     
            // Node is currently hidden, therefore hasn't been processed yet
            // Set node to be displayed
            childNode.hidden = false;
            
            // Recursive call to function to process its children
            addChildNodes(nodesToUpdate, childNode);
          }
        }
      });
    }
    
    network.on('select', function (properties) {
      // Define an array of nodes ot update, this is quicker than
      // updating each node individually
      let nodesToUpdate = [];
      
      // If no nodes are selected, unhide all hidden nodes
      if(properties.nodes.length === 0){
        // Populate array with list of nodes to unhide
        data.nodes.forEach(node => {
          if(node.hidden){ 
            nodesToUpdate.push({id:node.id, hidden: false});
          }
        });
        
        // Update nodes and return
        data.nodes.update(nodesToUpdate);
        return;
      }
        
      // One or more nodes are selected
      // Populate array with list of all nodes, hiding them
      data.nodes.forEach(node => {
        nodesToUpdate.push({id:node.id, hidden: true});
      });
    
      // Update the arra setting list of selected and connected nodes to unhide
      properties.nodes.forEach(nodeId => {
        // Find the selected node in the array
        let node = nodesToUpdate.find(node => node.id === nodeId);
        
        // Update selected node to be displayed
        node.hidden = false;
      
        // Call recursive function to add all dependents
        addChildNodes(nodesToUpdate, node);
      }); 
      
      // Submit updates to hide/unhide nodes
      data.nodes.update(nodesToUpdate);
    });