diff --git a/js/Plugin/jstree/hm.jstree.js b/js/Plugin/jstree/hm.jstree.js
index b5dd9d797ebf1d5eb361c90c174de24385097dc0..957c08927fb833402977c2b2dcdd00b8d31b3f2a 100644
--- a/js/Plugin/jstree/hm.jstree.js
+++ b/js/Plugin/jstree/hm.jstree.js
@@ -100,7 +100,7 @@
                 }
 
                 if (data.position < list.length - 1) {
-                  after = list[data.position + 1];
+                  after = list[data.position];
                 }
 
                 // Update the data on server side.
@@ -109,6 +109,7 @@
                   target: data.position,
                   parent: parent,
                   old_parent: old_parent,
+                  old_position: data.old_position,
                   after: after,
                   before: before
                 })
diff --git a/src/Controller/HmMenuController.php b/src/Controller/HmMenuController.php
index f7ad4c237f00d66adebe2cac87de611acd941c73..e1c12489fee9adba3809a45b23b9dbd1e80dc308 100644
--- a/src/Controller/HmMenuController.php
+++ b/src/Controller/HmMenuController.php
@@ -170,6 +170,7 @@ class HmMenuController extends ControllerBase {
       return new Response($this->t('Access denied!'));
     }
 
+    $old_position = (int) $request->get('old_position');
     $target_position = $request->get('target');
     $parent = $request->get('parent');
     $updated_links = $request->get('keys');
@@ -220,7 +221,7 @@ class HmMenuController extends ControllerBase {
         }
       }
 
-      $new_hierarchy = $this->hmPluginTypeManager->updateHierarchy($target_position, $all_siblings, $updated_links);
+      $new_hierarchy = $this->hmPluginTypeManager->updateHierarchy($target_position, $all_siblings, $updated_links, $old_position);
       foreach ($updated_links as $id) {
         if (!isset($links_uuid[$id])) {
           $entity = $this->entityTypeManager->getStorage('menu_link_content')->load($id);
diff --git a/src/Controller/HmTaxonomyController.php b/src/Controller/HmTaxonomyController.php
index 97d56109ed733d01b7c59b7f90de6f373bb4bf73..d7478a4d5b7f8c4d19b8e323b69d1dab5365997c 100644
--- a/src/Controller/HmTaxonomyController.php
+++ b/src/Controller/HmTaxonomyController.php
@@ -166,6 +166,7 @@ class HmTaxonomyController extends ControllerBase {
     }
 
     $target_position = $request->get('target');
+    $old_position = (int) $request->get('old_position');
     $old_parent_id = $request->get('old_parent');
     // Remove the parent index from the parent id.
     $old_parent_id = explode('_', $old_parent_id)[0];
@@ -199,7 +200,7 @@ class HmTaxonomyController extends ControllerBase {
         }
       }
 
-      $new_hierarchy = $this->hmPluginTypeManager->updateHierarchy($target_position, $all_siblings, $updated_terms);
+      $new_hierarchy = $this->hmPluginTypeManager->updateHierarchy($target_position, $all_siblings, $updated_terms, $old_position);
       $tids = array_keys($new_hierarchy);
 
       // Load all terms needed to update.
diff --git a/src/PluginTypeManager.php b/src/PluginTypeManager.php
index 9ededb1fc8dc41477b793258dbd8d52c076ebfad..aed468c511f742854837be95412ba1c64fe000d7 100644
--- a/src/PluginTypeManager.php
+++ b/src/PluginTypeManager.php
@@ -111,78 +111,96 @@ class PluginTypeManager {
    *   All siblings of the new items in an array[$item_id => (int)$weight]
    * @param array $updated_items
    *   IDs of new items inserted.
-   * @param int|bool $after
-   *   Indicator if new items are inserted after target position.
-   * @param int $weight
-   *   The initial weight.
+   * @param int $old_position
+   *   The old position of moving items.
    *   
    * @return array
    *   All siblings needed to move and their new weights.
    */
-  public function updateHierarchy(int $target_position, array $all_siblings, array $updated_items, $after = true, int $weight = 0) {
+  public function updateHierarchy(int $target_position, array $all_siblings, array $updated_items, int $old_position) {
     $new_hierarchy = [];
     
     if (!empty($all_siblings)) {
       $total = count($all_siblings);
       $num_new = count(array_diff($updated_items, array_keys($all_siblings)));
-      // If there is new child,
-      // the insert position should move backward.
-      $insert_position = $target_position - $num_new;
       if ($target_position <= 0) {
         // The insert postion is the first position.
         // we don't need to move any siblings.
         $weight = reset($all_siblings) - 1;
       }
-      elseif ($insert_position >= $total - 1) {
+      elseif ($target_position >= $total - 1) {
         // The insert postion is the end,
         // we don't need to move any siblings.
         $last_item= array_slice($all_siblings, -1, 1, TRUE);
         $weight = reset($last_item) + 1;
       }
       else {
-        $target_item = array_slice($all_siblings, $insert_position, 2);
-        $pre_weight = $target_item[0];
-        $next_weight = $target_item[1];
-        $weight = $pre_weight + 1;
+        $target_item = array_slice($all_siblings, $target_position, 1);
+        $weight = $target_item[0];
         $total_insert = count($updated_items);
+        // Figure out if the target element should move forward.
+        if ($num_new || ($old_position > $target_position)) {
+          $move_forward = true;
+        }
+        else {
+          $move_forward = false;
+        }
 
-        if ($next_weight - $pre_weight <= $total_insert) {
-          // The gap is not big enough.
-          // Need to make some room.
-          // If the target position is in the second half,
-          // we will move all siblings
-          // after the target position forward.
-          // Otherwise, we will move siblings
-          // before the target position backwards.
-          if ($insert_position >= $total / 2) {
-            $moving_siblings = array_slice($all_siblings, $insert_position + 1, NULL, TRUE);
-            $after = true;
-            $expected_weight = $next_weight + 1 + $total_insert;
+        // If the target position is in the second half,
+        // we will move all siblings
+        // after the target position forward.
+        // Otherwise, we will move siblings
+        // before the target position backwards.
+        if ($target_position - $num_new >= $total / 2) {
+          if ($move_forward) {
+            $moving_siblings = array_slice($all_siblings, $target_position, NULL, TRUE);
+            $weight = $target_item[0];
+          }
+          else {
+            $moving_siblings = array_slice($all_siblings, $target_position + 1, NULL, TRUE);
+            $weight = $target_item[0] + 1;
+          }
+          $after = true;
+          $expected_weight = $weight + $total_insert;
+        }
+        // Move the first bundle.
+        else {
+          if ($move_forward) {
+            $moving_siblings = array_slice($all_siblings, 0, $target_position, TRUE);
+            $weight = $target_item[0] - 1;
           }
           else {
-            $moving_siblings = array_slice($all_siblings, 0, $insert_position + 1, TRUE);
-            $after = false;
-            $weight = $next_weight - 1;
-            $expected_weight = $pre_weight - count($moving_siblings);
+            $moving_siblings = array_slice($all_siblings, 0, $target_position + 1, TRUE);
+            $weight = $target_item[0];
           }
+          $after = false;
+          $expected_weight = $weight - count($moving_siblings) - $total_insert + 1;
+        }
 
-          // Move all siblings that need to move.
-          foreach($moving_siblings as $item_id => $item_weight) {
-            // Skip all links in the updated array. They will be moved later.
-            if (in_array($item_id, $updated_items)) {
-              continue;
-            }
-            if ($after) {
-              if ($item_weight < $expected_weight) {
-                $new_hierarchy[$item_id] = $expected_weight++;
-              }
+        // Move all siblings that need to move.
+        foreach($moving_siblings as $item_id => $item_weight) {
+          // Skip all items that are in the updated array.
+          // They will be moved later.
+          if (in_array($item_id, $updated_items)) {
+            continue;
+          }
+          if ($after) {
+            if ($item_weight < $expected_weight) {
+              $new_hierarchy[$item_id] = $expected_weight;
             }
             else {
-              if ($item_weight > $expected_weight) {
-                $new_hierarchy[$item_id] = $expected_weight++;
-              }
+              // The weight is bigger than expected.
+              // No need to move the rest of siblings.
+              break;
+            }
+          }
+          else {
+            if ($item_weight > $expected_weight) {
+              $new_hierarchy[$item_id] = $expected_weight;
             }
           }
+          // Move to next sibling.
+          $expected_weight++;
         }
       }
     }