🏠 back to Observable

Strange D3.js word press issue - SVG diagonals not calcuated properly

I am having some issues when implementing by D3 hierarchy tree on WordPress.

D3.jsWordPressIssue

It seems transition SVG diagonals is not calculated properly when implemented in WordPress.

However, the same script works well in my local webserver and JSfiddle etc. You can fiddle with it here. https://jsfiddle.net/9ovL6s8k/3/

I look forward to your help and direction on how to fix this issue.

UPDATE:
There is some issue in the way TreeData is passed when a node is clicked as for trees with large amount of data children/descendants are not parsed correctly i.e. the hierarchy defined in the data is not respected if the nodes variable is defined like so var nodes = treeData.descendants().reverse().

If I remove the .reverse() then the data is read and parsed correctly. I think this is because of data now being passed top-bottom rather than bottom-top. Am I correct?

Not sure if this is linked to above issue.

Amit

Code below for your ready reference:

        // Set the dimensions and margins of the diagram
    var margin = {top: 20, right: 90, bottom: 30, left: 90},
        width = 960 - margin.left - margin.right,
        height = 500 - margin.top - margin.bottom;
        
    // declares a tree layout and assigns the size
    var treemap = d3.tree().size([height, width]);
    
    var flatData = [
        {"name": "Mother Father", "parent": null},
        {"name": "Kid 1", "parent": "Mother Father" },
        {"name": "Kid 2", "parent": "Mother Father" },
        {"name": "Kid 11", "parent": "Kid 1" },
        {"name": "Kid 12", "parent": "Kid 1" },
  {"name": "Kid 21", "parent": "Kid 2" },
        {"name": "Kid 22", "parent": "Kid 2" }, 
  {"name": "Kid 211", "parent": "Kid 21" },
        {"name": "Kid 212", "parent": "Kid 21" }   
        ];
        
    // convert the flat data into a hierarchy
    var treeData = d3.stratify()
        .id(function(d) { return d.name; })
        .parentId(function(d) { return d.parent; })
        (flatData);
        
    // assign the name to each node
    treeData.each(function(d) {
    d.name = d.id;
    }); 

    // append the svg object to the body of the page
    // appends a 'group' element to 'svg'
    // moves the 'group' element to the top left margin
    var svg = d3.select("#box").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 + ")");

    var i = 0,
        duration = 550,
        root;



    // Assigns parent, children, height, depth
    root = d3.hierarchy(treeData, function(d) { return d.children; });
    root.x0 = height / 2;
    root.y0 = 0;

    // Collapse after the second level
    root.children.forEach(collapse);

    update(root);

    // Collapse the node and all it's children
    function collapse(d) {
      if(d.children) {
        d._children = d.children
        d._children.forEach(collapse)
        d.children = null
      }
    }

    function update(source) {

      // Assigns the x and y position for the nodes
      var treeData = treemap(root);

      // Compute the new tree layout.
      var nodes = treeData.descendants(),
          links = treeData.descendants().slice(1);

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

      // ****************** Nodes section ***************************

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

      // Enter any new modes 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);

      // Add Circle for the nodes
      nodeEnter.append('circle')
          .attr('class', 'node')
          .attr('r', 1e-6)
          .style("fill", function(d) {
              return d._children ? "lightsteelblue" : "#fff";
          });

      // Add labels for the nodes
      nodeEnter.append('text')
          .attr("dy", ".35em")
          .attr("x", function(d) {
              return d.children || d._children ? -13 : 13;
          })
          .attr("text-anchor", function(d) {
              return d.children || d._children ? "end" : "start";
          })
          .text(function(d) { return d.data.name; });

      // UPDATE
      var nodeUpdate = nodeEnter.merge(node);

      // Transition to the proper position for the node
      nodeUpdate.transition()
        .duration(duration)
        .attr("transform", function(d) { 
            return "translate(" + d.y + "," + d.x + ")";
         });

      // Update the node attributes and style
      nodeUpdate.select('circle.node')
        .attr('r', 10)
        .style("fill", function(d) {
            return d._children ? "lightsteelblue" : "#fff";
        })
        .attr('cursor', 'pointer');


      // Remove any exiting nodes
      var nodeExit = node.exit().transition()
          .duration(duration)
          .attr("transform", function(d) {
              return "translate(" + source.y + "," + source.x + ")";
          })
          .remove();

      // On exit reduce the node circles size to 0
      nodeExit.select('circle')
        .attr('r', 1e-6);

      // On exit reduce the opacity of text labels
      nodeExit.select('text')
        .style('fill-opacity', 1e-6);

      // ****************** links section ***************************

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

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

      // UPDATE
      var linkUpdate = linkEnter.merge(link);

      // Transition back to the parent element position
      linkUpdate.transition()
          .duration(duration)
          .attr('d', function(d){ return diagonal(d, d.parent) });

      // Remove any exiting links
      var linkExit = link.exit().transition()
          .duration(duration)
          .attr('d', function(d) {
            var o = {x: source.x, y: source.y}
            return diagonal(o, o)
          })
          .remove();

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

      // Creates a curved (diagonal) path from parent to the child nodes
      function diagonal(s, d) {

        path = `M ${s.y} ${s.x}
                C ${(s.y + d.y) / 2} ${s.x},
                  ${(s.y + d.y) / 2} ${d.x},
                  ${d.y} ${d.x}`

        return path
      }

      // Toggle children on click.
      function click(d) {
        if (d.children) {
            d._children = d.children;
            d.children = null;
          } else {
            d.children = d._children;
            d._children = null;
          }
        update(d);
      }
    }

I’m not sure if D3/Wordpress integration is really within the scope of this forum; perhaps one of the community moderators could address that.

Having said that, I happen to maintain a small Wordpress site purely for experimentation and was able to get the code from your JS Fiddle working just fine. You can have a look here.

Of course, there’s a wide spectrum of plugins that might impact your results.

That is extactly what I did and it behaves differently to yours. Very Strange!

I am not using any third partly plugin. Are you using any js plugins to improve this experience?

No - it’s a pretty vanilla setup. I’m not exactly a Wordpress expert, though.

Seems I am out of options. Thanks so much for your help. I will try and debug what happens when I implement on Wordpress and will update this post. Thank you.