【问题标题】:d3.js Tree - Paging of child nodes v3.5d3.js 树 - 子节点的分页 v3.5
【发布时间】:2021-10-03 23:33:01
【问题描述】:

我有一个previous question,它解决了许多子节点的分页问题

虽然在我的 d3.js 上下文中实现有一些问题:

  • 发起分页的第一个和最后一个子节点没有命名 '...' 根据脚本功能。这似乎是 v7 到 v3.5 的问题。

  • 分页应该只发生在叶子节点上,而不是任何中间 分支。

  • 它需要与 d3.js v3.5 一起使用(是的,我知道,但这是待定的 将树迁移到 v7...)。基本上主要问题是第一个和最后一个分页节点的命名......

请参阅fiddle 了解 d3.js v3.5 中的实施和问题

与之前的解决方案相关:

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

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

function click(d) {
if (d.hasOwnProperty('page')) {
      d.parent.children = d.parent.pages[d.page];
    }  else if (d.children) {
    d._children = d.children;
    d.children = null;
  } else {
    d.children = d._children;
    d._children = null;
  }
  update(d);
}

【问题讨论】:

  • 如果我理解正确,您想在选定的节点组上启用分页并希望用某个数字命名分页节点
  • 不完全是,我只需要分页节点根据您的 scipt 将名称显示为“...”。该名称在 v7 中显示 OK,但在 v3.5 中不显示。我正在将我的树转换为 v7,但同时还有一些问题需要解决。所以我希望你的脚本在 3.5 中工作,基本上是这样,只是缺少 '...' 并替换为子列表中第一个节点的名称。
  • 还有。我只希望分页在叶节点上工作,即树末端的节点。您可以将节点设置为仅在包含我想要分页的叶节点的父 d.type (或 d.data.type)== 'unit-group' 上进行分页。我不希望树中的其他节点被分页,只是那些源自 d.type =="unit_group" 的父节点的节点
  • 我已经添加了答案和解释,如果有任何问题请告诉我

标签: javascript jquery d3.js


【解决方案1】:

笔记

  • 必须在调用折叠函数之前调用pageNodes 函数,因为折叠将children 键更改为_childrenpageNodes 函数与children 键一起使用
  • 我以某种方式创建了pageNodes 函数,因此它不需要一次又一次地调用这会浪费计算能力,所以我们在启动时调用它一次并根据需要调整结构然后我们只使用 click 函数而不需要一次又一次地打电话给pageNodes
  • 我已将pageNodes函数修改为仅当数据包含page: true时才分页,参考您可以查看unit-group数据段。

示例 1 - 需要在数据中添加 page key 以启用分页用于选择性数据段。

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;

function pageNodes(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,
}));

function collapse(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");

function update(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
      };
      return diagonal({
        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
      };
      return diagonal({
        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.
function click(d) {
  if (d.hasOwnProperty('pageNumber')) {
    d.parent.children = d.parent.pages[d.pageNumber];
  } else if (d.children) {
    d._children = d.children;
    d.children = null;
  } else {
    d.children = d._children;
    d._children = null;
  }
  update(d);
}

在这里工作example

示例 2 - 通过page 选项,这是一个返回真/假以启用/禁用选择性数据段分页的函数。

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;

function pageNodes(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";
  }
}));

function collapse(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");

function update(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
      };
      return diagonal({
        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
      };
      return diagonal({
        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.
function click(d) {
  if (d.hasOwnProperty('pageNumber')) {
    d.parent.children = d.parent.pages[d.pageNumber];
  } else if (d.children) {
    d._children = d.children;
    d.children = null;
  } else {
    d.children = d._children;
    d._children = null;
  }
  update(d);
}

在这里工作example

【讨论】:

  • 太棒了!我更改了这一行,而不是将 page='true' 添加到 JSON 数据中。工作正常: if (d.type == 'unit_group' && d.children.length > options.maxNode) {...
  • @IlludiumPu36 我添加了在所需数据部分启用/禁用分页的选项,而无需更改数据。
  • 看起来不错!我会有一个关于这个分页功能的新问题。这与用于搜索树的 Select2 列表有关。由于分页,分页正在向 select2 添加 '...' 而不是 d.type == 'learning_event' 的所有节点...。
猜你喜欢
  • 1970-01-01
  • 2012-03-21
  • 2013-10-21
  • 1970-01-01
  • 2021-09-30
  • 2021-10-04
  • 2012-01-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多