Skip to content Skip to sidebar Skip to footer

D3.js Tree - Paging Of Child Nodes V3.5

I have a previous question which solves a problem with paging of many child nodes There are few problems though with implementing in my d3.js context: The first and last child nod

Solution 1:

Notes

  • The order is mandatory pageNodes function needs to be called before collapse function call, As collapse changes the children key to _children and pageNodes function works with children key
  • I created pageNodes function in a way so it does not require calling again and again which would waste computation power, so we call it once on start and the structure is adjusted as per requirement then we just work with click function without requiring to call pageNodes again and again.
  • I have modified the pageNodes function to only page if data contain page: true, for reference you can check unit-group data segment.

Example 1 - require adding page key in data to enable paging for selective data segment.

var treeData = {"name":"Program","column_to_sort_by":null,"type":"program","children":[{"name":"ProgramGroup1","column_to_sort_by":null,"type":"program_group","children":[{"name":"POGroup1","column_to_sort_by":null,"type":"1program_outcome_group","children":[{"name":"PO1","column_to_sort_by":null,"type":"program_outcome","children":[{"name":"Unit1","column_to_sort_by":"Unit1","children":[{"name":"UG1-LE","column_to_sort_by":null,"page":true,"type":"unit_group","children":[{"name":"LE1","column_to_sort_by":"LE1","type":"learning_event"},{"name":"LE10","column_to_sort_by":"LE10","type":"learning_event"},{"name":"LE11","column_to_sort_by":"LE11","type":"learning_event"},{"name":"LE12","column_to_sort_by":"LE12","type":"learning_event"},{"name":"LE13","column_to_sort_by":"LE13","type":"learning_event"},{"name":"LE14","column_to_sort_by":"LE14","type":"learning_event"},{"name":"LE15","column_to_sort_by":"LE15","type":"learning_event"},{"name":"LE2","column_to_sort_by":"LE2","type":"learning_event"},{"name":"LE4","column_to_sort_by":"LE4","type":"learning_event"},{"name":"LE5","column_to_sort_by":"LE5","type":"learning_event"},{"name":"LE6","column_to_sort_by":"LE6","type":"learning_event"},{"name":"LE7","column_to_sort_by":"LE7","type":"learning_event"},{"name":"LE8","column_to_sort_by":"LE8","type":"learning_event"},{"name":"LE9","column_to_sort_by":"LE9","type":"learning_event"}]},{"name":"UG1-Assessments","column_to_sort_by":null,"page":true,"type":"unit_group","children":[{"name":"ASST1","column_to_sort_by":"ASST1","type":"assessment"},{"name":"ASST10","column_to_sort_by":"ASST10","type":"assessment"},{"name":"ASST11","column_to_sort_by":"ASST11","type":"assessment"},{"name":"ASST13","column_to_sort_by":"ASST13","type":"assessment"},{"name":"ASST14","column_to_sort_by":"ASST14","type":"assessment"},{"name":"ASST15","column_to_sort_by":"ASST15","type":"assessment"},{"name":"ASST2","column_to_sort_by":"ASST2","type":"assessment"},{"name":"ASST3","column_to_sort_by":"ASST3","type":"assessment"},{"name":"ASST4","column_to_sort_by":"ASST4","type":"assessment"},{"name":"ASST5","column_to_sort_by":"ASST5","type":"assessment"},{"name":"ASST6","column_to_sort_by":"ASST6","type":"assessment"},{"name":"ASST7","column_to_sort_by":"ASST7","type":"assessment"},{"name":"ASST8","column_to_sort_by":"ASST8","type":"assessment"},{"name":"ASST9","column_to_sort_by":"ASST9","type":"assessment"}]}],"type":"unit"}]},{"name":"PO2","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO3","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO4","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO5","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO6","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO7","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO8","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO9","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO10","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO11","column_to_sort_by":null,"type":"program_outcome"}]},{"name":"POGroup2","column_to_sort_by":null,"type":"1program_outcome_group"}]},{"name":"ProgramGroup2","column_to_sort_by":null,"type":"program_group"}]};

var margin = {
    top: 20,
    right: 120,
    bottom: 20,
    left: 120
  },
  width = 2000 - margin.right - margin.left,
  height = 500 - margin.top - margin.bottom;

var i = 0,
  duration = 750,
  root;

var tree = d3.layout.tree()
  .size([height, width]);

var diagonal = d3.svg.diagonal()
  .projection(function(d) {
    return [d.y, d.x];
  });

var svg = d3.select("body").append("svg")
  .attr("width", width + margin.right + margin.left)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

root = treeData;
root.x0 = height / 2;
root.y0 = 0;

functionpageNodes(d, options) {
  if (d.children) {
    d.children.forEach(c =>pageNodes(c, options));
    if (d.page && d.children.length > options.maxNode) {
      d.pages = {}
      const count = options.maxNode - 2;
      const l = Math.ceil(d.children.length / count);
      for (let i = 0; i < l; i++) {
        const startRange = i * count;
        const endRange = i * count + count;
        let pageNumber = i == 0 ? l - 1 : i - 1;
        d.pages[i] = d.children.slice(startRange, endRange);
        d.pages[i].unshift({
          ...d.pages[i][0],
          data: {
            name: options.getLabel ? options.getLabel(pageNumber) : "..."
          },
          pageNumber,
          name: "..."
        })

        // console.log(i, d.pages[i]);
        pageNumber = i != (l - 1) ? i + 1 : 0;
        d.pages[i].push({
          ...d.pages[i][0],
          data: {
            name: options.getLabel ? options.getLabel(pageNumber) : "..."
          },
          pageNumber,
          name: "..."
        });
      }
      d.children = d.pages[0];
      console.log(d.pages)
    }
  }
}
root.children.forEach(c =>pageNodes(c, {
  maxNode: 8,
}));

functioncollapse(d) {
  if (d.children) {
    d._children = d.children;
    d._children.forEach(collapse);
    d.children = null;
  }
}

root.children.forEach(collapse);
update(root);

//svg.style("height", "500px");functionupdate(source) {

  // Compute the new tree layout.var nodes = tree.nodes(root).reverse(),
    links = tree.links(nodes);

  // Normalize for fixed-depth.
  nodes.forEach(function(d) {
    d.y = d.depth * 180;
  });

  // Update the nodes…var node = svg.selectAll("g.node")
    .data(nodes, function(d) {
      return d.id || (d.id = ++i);
    });

  // Enter any new nodes at the parent's previous position.var nodeEnter = node.enter().append("g")
    .attr("class", "node")
    .attr("transform", function(d) {
      return"translate(" + source.y0 + "," + source.x0 + ")";
    })
    .on("click", click);

  nodeEnter.append("circle")
    .attr("r", 1e-6)
    .attr('stroke', function(d) {
      return d.color ? d.color : 'blue';
    })
    .style("fill", function(d) {
      return d._children ? "#ccc" : "#fff";
    });

  nodeEnter.append("text")
    .attr("x", function(d) {
      return d.children || d._children ? -13 : 13;
    })
    .attr("dy", ".35em")
    .attr("text-anchor", function(d) {
      return d.children || d._children ? "end" : "start";
    })
    .text(function(d) {
      return d.name;
    })
    .style("fill-opacity", 1e-6);

  // Transition nodes to their new position.var nodeUpdate = node.transition()
    .duration(duration)
    .attr("transform", function(d) {
      return"translate(" + d.y + "," + d.x + ")";
    });

  nodeUpdate.select("circle")
    .attr("r", 10)
    .style("fill", function(d) {
      var collapseColor = d.color ? d.color : '#ccc';
      return d._children ? collapseColor : "#fff";
    });

  nodeUpdate.select("text")
    .style("fill-opacity", 1);

  // Transition exiting nodes to the parent's new position.var nodeExit = node.exit().transition()
    .duration(duration)
    .attr("transform", function(d) {
      return"translate(" + source.y + "," + source.x + ")";
    })
    .remove();

  nodeExit.select("circle")
    .attr("r", 1e-6);

  nodeExit.select("text")
    .style("fill-opacity", 1e-6);

  // Update the links…var link = svg.selectAll("path.link")
    .data(links, function(d) {
      return d.target.id;
    });

  // Enter any new links at the parent's previous position.
  link.enter().insert("path", "g")
    .attr("class", "link")
    .attr("d", function(d) {
      var o = {
        x: source.x0,
        y: source.y0
      };
      returndiagonal({
        source: o,
        target: o
      });
    });

  // Transition links to their new position.
  link.transition()
    .duration(duration)
    .attr("d", diagonal);

  // Transition exiting nodes to the parent's new position.
  link.exit().transition()
    .duration(duration)
    .attr("d", function(d) {
      var o = {
        x: source.x,
        y: source.y
      };
      returndiagonal({
        source: o,
        target: o
      });
    })
    .remove();

  // Stash the old positions for transition.
  nodes.forEach(function(d) {
    d.x0 = d.x;
    d.y0 = d.y;
  });
}

// Toggle children on click.functionclick(d) {
  if (d.hasOwnProperty('pageNumber')) {
    d.parent.children = d.parent.pages[d.pageNumber];
  } elseif (d.children) {
    d._children = d.children;
    d.children = null;
  } else {
    d.children = d._children;
    d._children = null;
  }
  update(d);
}

Here working example

Example 2 - through page options which is a function which would return true/false to enable/disable paging for selective data segment.

var treeData = {"name":"Program","column_to_sort_by":null,"type":"program","children":[{"name":"ProgramGroup1","column_to_sort_by":null,"type":"program_group","children":[{"name":"POGroup1","column_to_sort_by":null,"type":"1program_outcome_group","children":[{"name":"PO1","column_to_sort_by":null,"type":"program_outcome","children":[{"name":"Unit1","column_to_sort_by":"Unit1","children":[{"name":"UG1-LE","column_to_sort_by":null,"type":"unit_group","children":[{"name":"LE1","column_to_sort_by":"LE1","type":"learning_event"},{"name":"LE10","column_to_sort_by":"LE10","type":"learning_event"},{"name":"LE11","column_to_sort_by":"LE11","type":"learning_event"},{"name":"LE12","column_to_sort_by":"LE12","type":"learning_event"},{"name":"LE13","column_to_sort_by":"LE13","type":"learning_event"},{"name":"LE14","column_to_sort_by":"LE14","type":"learning_event"},{"name":"LE15","column_to_sort_by":"LE15","type":"learning_event"},{"name":"LE2","column_to_sort_by":"LE2","type":"learning_event"},{"name":"LE4","column_to_sort_by":"LE4","type":"learning_event"},{"name":"LE5","column_to_sort_by":"LE5","type":"learning_event"},{"name":"LE6","column_to_sort_by":"LE6","type":"learning_event"},{"name":"LE7","column_to_sort_by":"LE7","type":"learning_event"},{"name":"LE8","column_to_sort_by":"LE8","type":"learning_event"},{"name":"LE9","column_to_sort_by":"LE9","type":"learning_event"}]},{"name":"UG1-Assessments","column_to_sort_by":null,"type":"unit_group","children":[{"name":"ASST1","column_to_sort_by":"ASST1","type":"assessment"},{"name":"ASST10","column_to_sort_by":"ASST10","type":"assessment"},{"name":"ASST11","column_to_sort_by":"ASST11","type":"assessment"},{"name":"ASST13","column_to_sort_by":"ASST13","type":"assessment"},{"name":"ASST14","column_to_sort_by":"ASST14","type":"assessment"},{"name":"ASST15","column_to_sort_by":"ASST15","type":"assessment"},{"name":"ASST2","column_to_sort_by":"ASST2","type":"assessment"},{"name":"ASST3","column_to_sort_by":"ASST3","type":"assessment"},{"name":"ASST4","column_to_sort_by":"ASST4","type":"assessment"},{"name":"ASST5","column_to_sort_by":"ASST5","type":"assessment"},{"name":"ASST6","column_to_sort_by":"ASST6","type":"assessment"},{"name":"ASST7","column_to_sort_by":"ASST7","type":"assessment"},{"name":"ASST8","column_to_sort_by":"ASST8","type":"assessment"},{"name":"ASST9","column_to_sort_by":"ASST9","type":"assessment"}]}],"type":"unit"}]},{"name":"PO2","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO3","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO4","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO5","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO6","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO7","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO8","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO9","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO10","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO11","column_to_sort_by":null,"type":"program_outcome"}]},{"name":"POGroup2","column_to_sort_by":null,"type":"1program_outcome_group"}]},{"name":"ProgramGroup2","column_to_sort_by":null,"type":"program_group"}]};

var margin = {
    top: 20,
    right: 120,
    bottom: 20,
    left: 120
  },
  width = 2000 - margin.right - margin.left,
  height = 500 - margin.top - margin.bottom;

var i = 0,
  duration = 750,
  root;

var tree = d3.layout.tree()
  .size([height, width]);

var diagonal = d3.svg.diagonal()
  .projection(function(d) {
    return [d.y, d.x];
  });

var svg = d3.select("body").append("svg")
  .attr("width", width + margin.right + margin.left)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

root = treeData;
root.x0 = height / 2;
root.y0 = 0;

functionpageNodes(d, options) {
  if (d.children) {
    d.children.forEach(c =>pageNodes(c, options));
    if (options.page && options.page(d) && d.children.length > options.maxNode) {
      d.pages = {}
      const count = options.maxNode - 2;
      const l = Math.ceil(d.children.length / count);
      for (let i = 0; i < l; i++) {
        const startRange = i * count;
        const endRange = i * count + count;
        let pageNumber = i == 0 ? l - 1 : i - 1;
        d.pages[i] = d.children.slice(startRange, endRange);
        d.pages[i].unshift({
          ...d.pages[i][0],
          data: {
            name: options.getLabel ? options.getLabel(pageNumber) : "..."
          },
          pageNumber,
          name: "..."
        })

        // console.log(i, d.pages[i]);
        pageNumber = i != (l - 1) ? i + 1 : 0;
        d.pages[i].push({
          ...d.pages[i][0],
          data: {
            name: options.getLabel ? options.getLabel(pageNumber) : "..."
          },
          pageNumber,
          name: "..."
        });
      }
      d.children = d.pages[0];
      console.log(d.pages)
    }
  }
}
root.children.forEach(c =>pageNodes(c, {
  maxNode: 8,
  page: function(d) {
    return d.type == "unit_group";
  }
}));

functioncollapse(d) {
  if (d.children) {
    d._children = d.children;
    d._children.forEach(collapse);
    d.children = null;
  }
}

root.children.forEach(collapse);
update(root);

//svg.style("height", "500px");functionupdate(source) {

  // Compute the new tree layout.var nodes = tree.nodes(root).reverse(),
    links = tree.links(nodes);

  // Normalize for fixed-depth.
  nodes.forEach(function(d) {
    d.y = d.depth * 180;
  });

  // Update the nodes…var node = svg.selectAll("g.node")
    .data(nodes, function(d) {
      return d.id || (d.id = ++i);
    });

  // Enter any new nodes at the parent's previous position.var nodeEnter = node.enter().append("g")
    .attr("class", "node")
    .attr("transform", function(d) {
      return"translate(" + source.y0 + "," + source.x0 + ")";
    })
    .on("click", click);

  nodeEnter.append("circle")
    .attr("r", 1e-6)
    .attr('stroke', function(d) {
      return d.color ? d.color : 'blue';
    })
    .style("fill", function(d) {
      return d._children ? "#ccc" : "#fff";
    });

  nodeEnter.append("text")
    .attr("x", function(d) {
      return d.children || d._children ? -13 : 13;
    })
    .attr("dy", ".35em")
    .attr("text-anchor", function(d) {
      return d.children || d._children ? "end" : "start";
    })
    .text(function(d) {
      return d.name;
    })
    .style("fill-opacity", 1e-6);

  // Transition nodes to their new position.var nodeUpdate = node.transition()
    .duration(duration)
    .attr("transform", function(d) {
      return"translate(" + d.y + "," + d.x + ")";
    });

  nodeUpdate.select("circle")
    .attr("r", 10)
    .style("fill", function(d) {
      var collapseColor = d.color ? d.color : '#ccc';
      return d._children ? collapseColor : "#fff";
    });

  nodeUpdate.select("text")
    .style("fill-opacity", 1);

  // Transition exiting nodes to the parent's new position.var nodeExit = node.exit().transition()
    .duration(duration)
    .attr("transform", function(d) {
      return"translate(" + source.y + "," + source.x + ")";
    })
    .remove();

  nodeExit.select("circle")
    .attr("r", 1e-6);

  nodeExit.select("text")
    .style("fill-opacity", 1e-6);

  // Update the links…var link = svg.selectAll("path.link")
    .data(links, function(d) {
      return d.target.id;
    });

  // Enter any new links at the parent's previous position.
  link.enter().insert("path", "g")
    .attr("class", "link")
    .attr("d", function(d) {
      var o = {
        x: source.x0,
        y: source.y0
      };
      returndiagonal({
        source: o,
        target: o
      });
    });

  // Transition links to their new position.
  link.transition()
    .duration(duration)
    .attr("d", diagonal);

  // Transition exiting nodes to the parent's new position.
  link.exit().transition()
    .duration(duration)
    .attr("d", function(d) {
      var o = {
        x: source.x,
        y: source.y
      };
      returndiagonal({
        source: o,
        target: o
      });
    })
    .remove();

  // Stash the old positions for transition.
  nodes.forEach(function(d) {
    d.x0 = d.x;
    d.y0 = d.y;
  });
}

// Toggle children on click.functionclick(d) {
  if (d.hasOwnProperty('pageNumber')) {
    d.parent.children = d.parent.pages[d.pageNumber];
  } elseif (d.children) {
    d._children = d.children;
    d.children = null;
  } else {
    d.children = d._children;
    d._children = null;
  }
  update(d);
}

Here working example

Post a Comment for "D3.js Tree - Paging Of Child Nodes V3.5"