diff --git a/resources/assets/javascripts/bootstrap/actionmenu.js b/resources/assets/javascripts/bootstrap/actionmenu.js
index 5cc6021404035999e7702965f95ad59be0fdaac7..bb6371dff39530d2edf6af7d7c6672116be2adb2 100644
--- a/resources/assets/javascripts/bootstrap/actionmenu.js
+++ b/resources/assets/javascripts/bootstrap/actionmenu.js
@@ -34,19 +34,4 @@
             STUDIP.ActionMenu.closeAll();
         }
     });
-
-    // Close all action menus when the escape key is pressed and rotate through all its items
-    // when TAB or SHIFT + TAB is pressed.
-    $(document).on('keydown', function(event) {
-        if (event.key === 'Escape') {
-            STUDIP.ActionMenu.closeAll();
-        } else if (event.key === 'Tab') {
-            //Check if the focus is inside an action menu:
-            let menu = $(event.target).closest('.action-menu');
-            if (menu.hasClass('is-open') && STUDIP.ActionMenu.tabThroughItems(menu, event.shiftKey)) {
-                event.preventDefault();
-            }
-        }
-    });
-
 }(jQuery));
diff --git a/resources/assets/javascripts/lib/actionmenu.js b/resources/assets/javascripts/lib/actionmenu.js
index 228d6c93b440b70b9fef57720c8578d301700bf1..a81f78abddbdf7cc4b9de13183221a12a6bd6ce4 100644
--- a/resources/assets/javascripts/lib/actionmenu.js
+++ b/resources/assets/javascripts/lib/actionmenu.js
@@ -100,7 +100,7 @@ class ActionMenu
         const additionalClasses = Object.values({ ...this.element[0].classList }).filter((item) => item != 'action-menu');
         const menu_width  = this.content.width();
         const menu_height = this.content.height();
-        
+
         // Reposition the menu?
         if (position) {
             let parents = getScrollableParents(this.element, menu_width, menu_height);
@@ -122,9 +122,28 @@ class ActionMenu
                 this.position = false;
             }
         }
+
+        this.attachEventHandlers();
+
         this.update();
     }
 
+    // Close all action menus when the escape key is pressed and rotate through all its items
+    // when TAB or SHIFT + TAB is pressed.
+    attachEventHandlers() {
+        this.menu[0].addEventListener('keydown', (event) => {
+            if (event.key === 'Escape') {
+                this.close(true);
+            } else if (event.key === 'Tab' && this.is_open) {
+                this.tabThroughItems(event.shiftKey);
+                event.preventDefault();
+            } else if (event.key === 'Enter' && event.target.matches('label')) {
+                event.target.querySelector('button,input').click();
+                event.preventDefault();
+            }
+        });
+    }
+
     toggleScrollHandler(active) {
         if (ActionMenu.scrollHandlerState === active) {
             return;
@@ -170,19 +189,23 @@ class ActionMenu
     /**
      * Toggle the menus display state. Pass a state to enforce it.
      */
-    toggle(state = null) {
+    toggle(state = null, focus = false) {
         this.is_open = state === null ? !this.is_open : state;
 
         this.update();
 
         if (this.is_open) {
             this.reposition();
-            this.menu.find('.action-menu-icon').focus();
             ActionMenu.openMenus.push(this);
         } else {
             ActionMenu.openMenus = ActionMenu.openMenus.filter(menu => menu !== this);
         }
 
+        // Always focus the toggle element
+        if (this.is_open || focus) {
+            this.menu.find('.action-menu-icon').focus();
+        }
+
         this.toggleScrollHandler(ActionMenu.openMenus.filter(menu => menu.position).length > 0);
     }
 
@@ -237,36 +260,28 @@ class ActionMenu
      * Handles the rotation through the action menu items when the first
      * or last element of the menu has been reached.
      *
-     * @param menu The menu whose items shall be rotated through.
-     *
      * @param reverse Whether to rotate in reverse (true) or not (false).
      *     Defaults to false.
      */
-    static tabThroughItems(menu, reverse = false) {
-        if (reverse) {
-            //Put the focus on the last link in the menu, if the first link has the focus:
-            if (jQuery(menu).find('a:first:focus').length > 0) {
-                //Put the focus on the action menu button:
-                jQuery(menu).find('button.action-menu-icon').focus();
-                return true;
-            } else if (jQuery(menu).find('button.action-menu-icon:focus').length > 0) {
-                //Put the focus on the last action menu item:
-                jQuery(menu).find('a:last').focus();
-                return true;
-            }
-        } else {
-            //Put the focus on the first link in the menu, if the last link has the focus:
-            if (jQuery(menu).find('a:last:focus').length > 0) {
-                //Put the focus on the action menu button:
-                jQuery(menu).find('button.action-menu-icon').focus();
-                return true;
-            }  else if (jQuery(menu).find('button.action-menu-icon:focus').length > 0) {
-                //Put the focus on the first action menu item:
-                jQuery(menu).find('a:first').focus();
-                return true;
-            }
+    tabThroughItems(reverse = false) {
+        const items = Array.from(this.menu[0].querySelectorAll([
+            '.action-menu-icon',
+            '.action-menu-item:not(.action-menu-item-disabled) a',
+            '.action-menu-item:not(.action-menu-item-disabled) button',
+            '.action-menu-item:not(.action-menu-item-disabled) label',
+        ].join(',')));
+
+        // Get index of currently focussed element
+        let index = items.findIndex(element => element === document.activeElement);
+        if (index === -1) {
+            index = 0;
         }
-        return false;
+
+        // Get new index based on direction
+        index = (index + (reverse ? -1 : 1) + items.length) % items.length;
+
+        // Focus element
+        items[index].focus();
     }
 }
 
diff --git a/resources/vue/components/StudipActionMenu.vue b/resources/vue/components/StudipActionMenu.vue
index 918814cf5c4c2fec893f5447789189d4c86f2a12..9bce949d3b7ca3d0b233c315bd964142b7e09b13 100644
--- a/resources/vue/components/StudipActionMenu.vue
+++ b/resources/vue/components/StudipActionMenu.vue
@@ -33,7 +33,7 @@
                         <span v-else class="action-menu-no-icon"></span>
                         {{ item.label }}
                     </a>
-                    <label v-else-if="item.icon" class="undecorated" v-on="linkEvents(item)">
+                    <label v-else-if="item.icon" class="undecorated" v-on="linkEvents(item)" tabindex="0">
                         <studip-icon :shape="item.icon"
                                      :name="item.name"
                                      class="action-menu-item-icon"
diff --git a/templates/shared/action-menu.php b/templates/shared/action-menu.php
index e95b92dec0f95377f213774ae17d32df39cfaded..ebec6d9ccff3ca10514a9595b6379ce1fc24d1d4 100644
--- a/templates/shared/action-menu.php
+++ b/templates/shared/action-menu.php
@@ -49,7 +49,7 @@
                 </a>
             <? elseif ($action['type'] === 'button'): ?>
                 <? if ($action['icon']): ?>
-                    <label class="undecorated">
+                    <label class="undecorated" tabindex="0">
                         <?= $action['icon']->asInput(false, $action['attributes'] + [
                             'class' => 'action-menu-item-icon',
                             'name'  => $action['name'],