diff --git a/vendor/magento/module-catalog/Block/Adminhtml/Category/Widget/Chooser.php b/vendor/magento/module-catalog/Block/Adminhtml/Category/Widget/Chooser.php
index dba669bc5ca..89304819161 100644
--- a/vendor/magento/module-catalog/Block/Adminhtml/Category/Widget/Chooser.php
+++ b/vendor/magento/module-catalog/Block/Adminhtml/Category/Widget/Chooser.php
@@ -152,7 +152,7 @@ class Chooser extends \Magento\Catalog\Block\Adminhtml\Category\Tree
         if (in_array($node->getId(), $this->getSelectedCategories())) {
             $item['checked'] = true;
         }
-        $item['is_anchor'] = (int)$node->getIsAnchor();
+        $item['is_anchor'] = $node->getIsAnchor() !== null ? (int) $node->getIsAnchor() : 1;
         $item['url_key'] = $node->getData('url_key');
         return $item;
     }
diff --git a/vendor/magento/module-catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml b/vendor/magento/module-catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml
index 7bc85087a7e..c2043570e0d 100644
--- a/vendor/magento/module-catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml
+++ b/vendor/magento/module-catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml
@@ -1,61 +1,50 @@
 <?php
 /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
+ * Copyright 2015 Adobe
+ * All Rights Reserved.
  */
 
-/** @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer */
+/**
+ * @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer
+ * @var \Magento\Framework\Escaper $escaper
+ */
 ?>
 
 <?php $_divId = 'tree' . $block->getId() ?>
-<div id="<?= $block->escapeHtmlAttr($_divId) ?>" class="tree"></div>
+<div id="<?= $escaper->escapeHtmlAttr($_divId) ?>" class="tree"></div>
 <?php
 $isUseMassAction = $block->getUseMassaction() ? 1 : 0;
 $isAnchorOnly = $block->getIsAnchorOnly() ? 1 : 0;
+
 $scriptString = <<<script
 
 require(['jquery', 'jquery/jstree/jquery.jstree'], function($) {
 
-    let tree = $('#tree{$block->escapeJs($block->getId())}');
-    let useMassAction = {$isUseMassAction};
-    let isAnchorOnly = {$isAnchorOnly};
+    const tree = $('#tree{$block->escapeJs($block->getId())}');
+    const useMassAction = {$isUseMassAction};
+    const isAnchorOnly = {$isAnchorOnly};
     let checkedNodes = [];
-
-    function addLastNodeProperty(nodes) {
-        return nodes.map(node => {
-            return node.children
-                ? { ...node, children: addLastNodeProperty(node.children) }
-                : { ...node, lastNode: true };
-        });
-    }
+    let anchorNodes = [];
+    let nonAnchorNodes = [];
 
     function actionBasedOnIsAnchorOnly() {
-        tree.jstree().get_json('#', { flat: true }).each((node, value) => {
-            const attrId = node.a_attr.id;
-            const rootNode = tree.jstree().get_node("#");
-            const rootId = rootNode.children[0];
-
-            if (isAnchorOnly === 1 && node.id === rootId) {
-                tree.jstree(true).disable_node(node);
-            } else if (isAnchorOnly === 0 && node.id !== rootId) {
-                tree.jstree(true).disable_node(node);
-            }
-        });
+        if (isAnchorOnly) {
+            tree.jstree(true).disable_node(nonAnchorNodes);
+        } else {
+            tree.jstree(true).disable_node(anchorNodes);
+        }
     }
 
-    function handleLoadedTree(e, data) {
+    function handleLoadedNode(e, data) {
         const container = $(e.target).closest('div.chooser_container');
-        checkedNodes = container.find('input[type="text"].entities').val().split(',').map(item => item.trim());
-
-        data.instance.get_json('#', { flat: true }).forEach(nodeId => {
-            const node = data.instance.get_node(nodeId);
-
-            if (checkedNodes.includes(node.id)) {
-                tree.jstree(true).select_node(node.id);
-            }
+        if (container.find('input[type="text"].entities').val() !== '') {
+            checkedNodes = container.find('input[type="text"].entities').val().split(',').map(item => item.trim());
+        }
 
+        if (data.status) {
+            tree.jstree(true).select_node(checkedNodes);
             actionBasedOnIsAnchorOnly();
-        });
+        }
     }
 
     function handleChange(e, data) {
@@ -65,18 +54,16 @@ require(['jquery', 'jquery/jstree/jquery.jstree'], function($) {
 
         if (useMassAction) {
             const clickedNodeID = data.node.id;
-            const currentCheckedNodes = data.instance.get_checked();
 
             if (data.action === 'select_node' && !checkedNodes.includes(clickedNodeID)) {
-                checkedNodes = currentCheckedNodes;
+                checkedNodes.push(clickedNodeID);
             } else if (data.action === 'deselect_node') {
-                checkedNodes = currentCheckedNodes.filter(nodeID => nodeID !== clickedNodeID);
+                checkedNodes = checkedNodes.filter(nodeID => nodeID !== clickedNodeID);
             }
 
             checkedNodes.sort((a, b) => a - b);
-
             const container = $(e.target).closest('div.chooser_container');
-            container.find('input[type="text"].entities').val(checkedNodes.join(', '));
+            container.find('input[type="text"].entities').val(checkedNodes.join(','));
         } else {
             node = data.node;
             node.attributes = node.original;
@@ -85,77 +72,55 @@ require(['jquery', 'jquery/jstree/jquery.jstree'], function($) {
         }
     }
 
-    function getCheckedNodeIds(tree, node) {
-        if (node.children_d && node.children_d.length > 0) {
-            const selectChildrenNodes = node.children_d.filter(item => checkedNodes.includes(item));
-
-            if (selectChildrenNodes.length > 0) {
-                tree.jstree(true).select_node(selectChildrenNodes);
+    function updateChildrenKey(treeJson) {
+        treeJson.forEach(node => {
+            if (Array.isArray(node.children) && node.children.length === 0) {
+                node.children = true;
+            } else if (Array.isArray(node.children)) {
+                updateChildrenKey(node.children);
             }
-        }
-    }
-
-    function addLastNodeFlag(treeData) {
-        if (treeData.children) {
-            treeData.children.forEach(child => addLastNodeFlag(child));
-        } else {
-            treeData.lastNode = true;
-        }
-    }
-
-    function handleSuccessResponse(response, childNode, data) {
-        if (response.length > 0) {
-            response.forEach(newNode => {
-                addLastNodeFlag(newNode);
-
-                // Create the new node and execute node callback
-                data.instance.create_node(childNode, newNode, 'last', node => {
-                    if (useMassAction) {
-                        if (checkedNodes.includes(node.id)) {
-                            tree.jstree(true).select_node(node.id);
-                        }
-                        getCheckedNodeIds(tree, node);
-                        actionBasedOnIsAnchorOnly();
-                    }
-                });
-            });
-        }
-    }
-
-    function handleOpenNode(e, data) {
-        let parentNode = data.node;
 
-        if (parentNode.children.length > 0) {
-            let childNode = data.instance.get_node(parentNode.children, false);
-
-            // Check if the child node has no children (is not yet loaded)
-            if (childNode.children && childNode.children.length === 0
-                && childNode.original && !childNode.original.lastNode) {
-                $.ajax({
-                    url: '{$block->escapeJs($block->escapeUrl($block->getLoadTreeUrl()))}',
-                    data: {
-                        id: childNode.original.id,
-                        store: childNode.original.store,
-                        form_key: FORM_KEY
-                    },
-                    dataType: 'json',
-                    success: function (response) {
-                        handleSuccessResponse(response, childNode, data);
-                    },
-                    error: function (jqXHR, status, error) {
-                        console.log(status + ': ' + error + 'Response text:' + jqXHR.responseText);
-                    }
-                });
+            if (node.is_anchor === 1) {
+                anchorNodes.push(node.id);
+            } else {
+                nonAnchorNodes.push(node.id);
             }
-        }
+        });
+        return treeJson;
     }
 
     var jstreeConfig = {
         core: {
-            data: addLastNodeProperty({$block->getTreeJson()}),
+            data: function (obj, callback) {
+                if (obj.id != '#' && obj.children.length === 0) {
+                    let data = {
+                        id: obj.id,
+                        store: obj.original.store,
+                        node: obj.id,
+                        form_key: FORM_KEY
+                    };
+
+                    $.ajax({
+                        url: '{$block->escapeJs($block->getLoadTreeUrl())}',
+                        type: "POST",
+                        data: data,
+                        dataType: 'json',
+                        success: function (response) {
+                            response = updateChildrenKey(response);
+                            callback.call(this, response);
+                        },
+                        error: function (jqXHR, status, error) {
+                            console.log(status + ': ' + error);
+                        }
+                    });
+                } else {
+                    let defaultTree = updateChildrenKey({$block->getTreeJson()});
+                    callback.call(this, defaultTree);
+                }
+            },
             check_callback: true
         },
-        plugins: []
+        plugins: ['dnd']
     };
 
     if (useMassAction) {
@@ -168,18 +133,17 @@ require(['jquery', 'jquery/jstree/jquery.jstree'], function($) {
     tree.jstree(jstreeConfig);
 
     if (useMassAction) {
-        tree.on('loaded.jstree', (e, data) => handleLoadedTree(e, data));
+        tree.on('load_node.jstree', (e, data) => handleLoadedNode(e, data));
     }
 
     tree.on('changed.jstree', (e, data) => handleChange(e, data));
-    tree.on('open_node.jstree', (e, data) => handleOpenNode(e, data));
 });
 
 script;
 ?>
 <?= /* @noEscape */ $secureRenderer->renderStyleAsTag(
     'overflow-x: auto;',
-    '#tree' . $block->escapeJs($block->getId())
+    '#tree' . $escaper->escapeJs($block->getId())
 );
 ?>
 <?= /* @noEscape */ $secureRenderer->renderTag('script', [], $scriptString, false); ?>
diff --git a/vendor/magento/module-catalog/view/adminhtml/web/js/category-checkbox-tree.js b/vendor/magento/module-catalog/view/adminhtml/web/js/category-checkbox-tree.js
index 6a81e30ea54..b38bcc9f881 100644
--- a/vendor/magento/module-catalog/view/adminhtml/web/js/category-checkbox-tree.js
+++ b/vendor/magento/module-catalog/view/adminhtml/web/js/category-checkbox-tree.js
@@ -10,24 +10,6 @@ define([
 ], function ($) {
     'use strict';
 
-    /**
-     * Recursively adds the 'lastNode' property to the nodes in the nested object.
-     *
-     * @param {Array} nodes
-     * @returns {Array}
-     */
-    function addLastNodeProperty(nodes) {
-        return nodes.map(node => {
-            return node.children ? {
-                ...node,
-                children: addLastNodeProperty(node.children)
-            } : {
-                ...node,
-                lastNode: true
-            };
-        });
-    }
-
     /**
      * Main function that creates the jstree
      *
@@ -48,9 +30,9 @@ define([
                 rootId: config.rootId,
                 expanded: config.expanded,
                 categoryId: config.categoryId,
-                treeJson: addLastNodeProperty(config.treeJson)
+                treeJson: config.treeJson
             },
-            checkedNodes = [];
+            initialSelection = [];
 
         /**
          * Get the jstree element by its ID
@@ -58,132 +40,83 @@ define([
         const treeId = $('#' + options.divId);
 
         /**
-         * Function to check child nodes based on the checkedNodes array
-         *
-         * @param {Object} node
+         * @return {Element}
          */
-        function getCheckedNodeIds(node) {
-            if (node.children_d && node.children_d.length > 0) {
-                const selectChildrenNodes = node.children_d.filter(item => checkedNodes.includes(item));
-
-                if (selectChildrenNodes.length > 0) {
-                    treeId.jstree(false).select_node(selectChildrenNodes);
-                }
-            }
+        function getTargetInput() {
+            return options.jsFormObject.updateElement;
         }
 
         /**
-         * Initialize the jstree with configuration options
-         */
-        treeId.jstree({
-            core: {
-                data: options.treeJson,
-                check_callback: true
-            },
-            plugins: ['checkbox'],
-            checkbox: {
-                three_state: false
-            }
-        });
-
-        /**
-         * Event handler for 'loaded.jstree' event
+         * Recursively marks nodes which children are not loaded.
+         *
+         * @param {Array} nodes
+         * @returns {Array}
          */
-        treeId.on('loaded.jstree', function () {
+        function prepareNodes(nodes) {
+            return nodes.map(
+                function (node) {
+                    let obj = {...node, state: {}};
+
+                    if (Array.isArray(obj.children)) {
+                        if (obj.children.length > 0) {
+                            obj.children = prepareNodes(obj.children);
+                        } else {
+                            obj.children = true;
+                        }
+                    }
 
-            /**
-             * Get each node in the tree
-             */
-            $(treeId.jstree().get_json('#', {
-                flat: false
-            })).each(function () {
-                let node = treeId.jstree().get_node(this.id, false);
+                    if (obj.expanded) {
+                        obj.state.opened = true;
+                    }
 
-                if (node.original.expanded) {
-                    treeId.jstree(true).open_node(node);
-                }
+                    if (initialSelection.includes(obj.id)) {
+                        obj.state.selected = true;
+                    }
 
-                if (options.jsFormObject.updateElement.defaultValue) {
-                    checkedNodes = options.jsFormObject.updateElement.defaultValue.split(',');
+                    return obj;
                 }
-            });
-        });
-
-        /**
-         * Event handler for 'load_node.jstree' event
-         */
-        treeId.on('load_node.jstree', function (e, data) {
-            getCheckedNodeIds(data.node);
-        });
-
-        /**
-         * Add lastNode property to child who doesn't have children property
-         *
-         * @param {Object} treeData
-         */
-        function addLastNodeFlag(treeData) {
-            if (treeData.children) {
-                treeData.children.forEach((child) => addLastNodeFlag(child));
-            } else {
-                treeData.lastNode = true;
-            }
+            );
         }
 
         /**
-         * Function to handle the 'success' callback of the AJAX request
+         * Load the node and execute the callback function
          *
-         * @param {Array} response
-         * @param {Object} childNode
-         * @param {Object} data
+         * @param {Object} node
+         * @param {Function} callback
          */
-        function handleSuccessResponse(response, childNode, data) {
-            if (response.length > 0) {
-                response.forEach(function (newNode) {
-                    addLastNodeFlag(newNode);
-
-                    /**
-                     * Create the new node and execute node callback
-                     */
-                    data.instance.create_node(childNode, newNode, 'last', function (node) {
-                        if (checkedNodes.includes(node.id)) {
-                            treeId.jstree(false).select_node(node.id);
-                        }
-                        getCheckedNodeIds(node);
-                    });
+        function load(node, callback) {
+            let target = getTargetInput(),
+                instance = this;
+
+            if (node.id === $.jstree.root) {
+                callback.call(instance, prepareNodes(options.treeJson));
+            } else if (Array.isArray(node.children) && node.children.length === 0) {
+                $.ajax({
+                    url: options.dataUrl,
+                    data: {
+                        id: node.id,
+                        selected: target.value
+                    },
+                    dataType: 'json',
+                    success: function (response) {
+                        callback.call(instance, prepareNodes(response));
+                    },
+                    error: function (jqXHR, status, error) {
+                        console.log(status + ': ' + error + '\nResponse text:\n' + jqXHR.responseText);
+                    }
                 });
+            } else {
+                callback.call(instance, false);
             }
         }
 
         /**
-         * Event handler for 'open_node.jstree' event
+         * Event handler for 'init.jstree' event
          */
-        treeId.on('open_node.jstree', function (e, data) {
-            let parentNode = data.node;
-
-            if (parentNode.children.length > 0) {
-                let childNode = data.instance.get_node(parentNode.children, false);
-
-                /**
-                 * Check if the child node has no children (is not yet loaded)
-                 */
-                if (childNode.children && childNode.children.length === 0
-                    && childNode.original && !childNode.original.lastNode) {
-                    $.ajax({
-                        url: options.dataUrl,
-                        data: {
-                            id: childNode.id,
-                            selected: options.jsFormObject.updateElement.value
-                        },
-                        dataType: 'json',
-                        success: function (response) {
-                            handleSuccessResponse(response, childNode, data);
-                        },
-                        error: function (jqXHR, status, error) {
-                            console.log(status + ': ' + error + '\nResponse text:\n' + jqXHR.responseText);
-                        }
-                    });
-                }
-            }
+        treeId.on('init.jstree', function () {
+            let target = getTargetInput();
+
+            initialSelection = target.value ? target.value.split(',').map(id => id.trim()) : [];
         });
 
         /**
@@ -193,19 +126,34 @@ define([
             if (data.action === 'ready') {
                 return;
             }
-            const clickedNodeID = data.node.id, currentCheckedNodes = data.instance.get_checked();
-
-            if (data.action === 'select_node' && !checkedNodes.includes(clickedNodeID)) {
-                checkedNodes = currentCheckedNodes;
-            } else if (data.action === 'deselect_node') {
-                checkedNodes = currentCheckedNodes.filter((nodeID) => nodeID !== clickedNodeID);
-            }
-            checkedNodes.sort((a, b) => a - b);
 
             /**
              * Update the value of the corresponding form element with the checked node IDs
+             *
+             * keep the checked nodes that are not in the tree yet,
+             * and merge them with the currently checked nodes
+             * then sort the resulted array
              */
-            options.jsFormObject.updateElement.value = checkedNodes.join(', ');
+            let target = getTargetInput(),
+                selected = initialSelection
+                    .filter(node => data.instance.get_node(node) === false)
+                    .concat(data.instance.get_checked());
+
+            target.value = [...new Set(selected)].sort((a, b) => a - b).join(',');
+        });
+
+        /**
+         * Initialize the jstree with configuration options
+         */
+        treeId.jstree({
+            core: {
+                data: load,
+                check_callback: true
+            },
+            plugins: ['checkbox'],
+            checkbox: {
+                three_state: false
+            }
         });
     };
 });
