简体   繁体   中英

D3 v4 drag/drop tree node into new parents children

I'm having a bit of trouble tweaking the snippet below to fully update a node and all of its children when using 4 of D3.

This code fires when a node is dragged and dropped into another parent node (on "dragend" event). It's purpose is to remove the dragged node from its current parent node and insert it into a new parents children.

The current code actually still does what it's intended to do when using v4 but now with the new version there seems to be a few more things that need to be updated to the "draggingNode" so it will transition properly into its new position. At minimum, the "draggingNode" needs to be applied it's new "parent" and also update it's "depth" to where it's new position is within the tree. Each child of the "draggingNode" also needs to have its "depth" updated to it's new position within the tree.

  • selectedNode : node the "draggingNode" is dropped on (new parent)
  • draggingNode : node currently being dragged

Target Snippet ( full source code ):

var index = draggingNode.parent.children.indexOf(draggingNode);
if (index > -1) {
    draggingNode.parent.children.splice(index, 1);
    // had to update the "draggingNode" parent "children" array if it's 
    // empty otherwise the "firstWalk()" core function throws an error when
    // trying to call "children[0]".
    if (draggingNode.parent.children.length == 0) {
        draggingNode.parent.children = null;
    }
}
if (typeof selectedNode.children !== 'undefined' || typeof selectedNode._children !== 'undefined') {
    if (typeof selectedNode.children !== 'undefined') {
        selectedNode.children.push(draggingNode);
    } else {
        selectedNode._children.push(draggingNode);
    }
} else {
    selectedNode.children = [];
    selectedNode.children.push(draggingNode);
}

I believe the code above can remain untouched, but what I need to do is add in some updates for the "draggingNode" so that it can be updated properly.

My initial thoughts were to simply add the following to the snippet above just before adding it into the "selectedNode" array:

draggingNode.parent = selectedNode;
draggingNode.depth = selectedNode.depth + 1;

This works great! The issue is that it only works on nodes without children. Child node properties need to be updated.

QUESTION IS

How can I update the "draggingNode", and all of its children, so that their properties are properly adjusted to their new parents and depths?

Is there a function that I can use like hierarchy or stratify to properly update them? If so I'm not quite sure how to make those work. Or maybe there is no functions available within the API and I just have to loop through all the children and update their depths?

Well turns out I've came up with a solution that seems to work well for me.. I ended up creating a function to traverse through all the descendants of the dragged node and update their node depths.

function setNodeDepth(d) {
    d.depth = d == root ? 0 : d.parent.depth + 1;
    if (d.children) {
        d.children.forEach(setNodeDepth);
    } else if (d._children) {
        d._children.forEach(setNodeDepth);
    }
}

Here's what I ended up with..

var index = draggingNode.parent.children.indexOf(draggingNode);
if (index > -1) {
    draggingNode.parent.children.splice(index, 1);
    // had to update the "draggingNode" parent "children" array if it's 
    // empty otherwise the "firstWalk()" core function throws an error when
    // trying to call "children[0]".
    if (draggingNode.parent.children.length == 0) {
        draggingNode.parent.children = null;
    }
}

// update the "draggingNode" parent as well as update all its children 
// so that their properties are properly adjusted to their new depths.
draggingNode.parent = selectedNode;
draggingNode.descendants().forEach(setNodeDepth);

// check for visible children.
if (selectedNode.children) {
    selectedNode.children.push(draggingNode);
// check for hidden children.
} else if (selectedNode._children) {
    selectedNode._children.push(draggingNode);
// no children exist, create new children array and add "draggingNode".
} else {
    selectedNode.children = [];
    selectedNode.children.push(draggingNode);
}

I just upgraded my D3 tree to V4 and had the same sort of problems. Looks like D3 V4 is doing more internal bookkeeping than before and messing around with the node properties isn't a good idea. When using typings, some properties are actually marked as read-only (like depth) which does already tell you something...

Instead, reflect the new layout by updating your own data structure and refresh the root node using d3.hierarchy(). Then call your update method with the new root. This worked perfectly for me, no hacking required!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM