diff --git a/library/Icingadb/Common/DetailActions.php b/library/Icingadb/Common/DetailActions.php
index b182b1f26..3358458d3 100644
--- a/library/Icingadb/Common/DetailActions.php
+++ b/library/Icingadb/Common/DetailActions.php
@@ -46,6 +46,9 @@ public function getDetailActionsDisabled(): bool
     public function initializeDetailActions(): self
     {
         $this->getAttributes()
+            ->registerAttributeCallback('data-interactable-action-list', function () {
+                return $this->getDetailActionsDisabled() ? null : true;
+            })
             ->registerAttributeCallback('class', function () {
                 return $this->getDetailActionsDisabled() ? null : 'action-list';
             })
diff --git a/public/js/action-list.js b/public/js/action-list.js
index c176a0db7..66d8abf5c 100644
--- a/public/js/action-list.js
+++ b/public/js/action-list.js
@@ -1,789 +1,190 @@
-/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
-
-;(function (Icinga) {
+(function (Icinga) {
 
     "use strict";
 
     try {
-        var notjQuery = require('icinga/icinga-php-library/notjQuery');
+        var ActionList = require('icinga/icinga-php-library/widget/ActionList');
     } catch (e) {
-        console.warn('Unable to provide input enrichments. Libraries not available:', e);
+        console.warn('Unable to provide action list feature. Libraries not available:', e);
         return;
     }
 
-    Icinga.Behaviors = Icinga.Behaviors || {};
-
-    class ActionList extends Icinga.EventListener {
+    class ActionListBehavior extends Icinga.EventListener {
         constructor(icinga) {
             super(icinga);
 
-            this.on('click', '.action-list [data-action-item]:not(.page-separator), .action-list [data-action-item] a[href]', this.onClick, this);
+            this.on('beforerender', '#main > .container', this.onBeforeRender, this);
+            this.on('rendered', '#main > .container', this.onRendered, this);
             this.on('close-column', '#main > #col2', this.onColumnClose, this);
             this.on('column-moved', this.onColumnMoved, this);
-
-            this.on('rendered', '#main .container', this.onRendered, this);
-            this.on('keydown', '#body', this.onKeyDown, this);
-
-            this.on('click', '.load-more[data-no-icinga-ajax] a', this.onLoadMoreClick, this);
-            this.on('keypress', '.load-more[data-no-icinga-ajax] a', this.onKeyPress, this);
-
-            this.lastActivatedItemUrl = null;
-            this.lastTimeoutId = null;
-            this.isProcessingLoadMore = false;
-            this.activeRequests = {};
-        }
-
-        /**
-         * Parse the filter query contained in the given URL query string
-         *
-         * @param {string} queryString
-         *
-         * @returns {array}
-         */
-        parseSelectionQuery(queryString) {
-            return queryString.split('|');
-        }
-
-        /**
-         * Suspend auto refresh for the given item's container
-         *
-         * @param {Element} item
-         *
-         * @return {string} The container's id
-         */
-        suspendAutoRefresh(item) {
-            const container = item.closest('.container');
-            container.dataset.suspendAutorefresh = '';
-
-            return container.id;
+            this.on('selection-start', this.onSelectionStart, this);
+            this.on('selection-end', this.onSelectionEnd, this);
+            this.on('all-deselected', this.allDeselected, this);
+
+            /**
+             * Action lists
+             *
+             * @type {WeakMap<object, ActionList>}
+             * @private
+             */
+            this._actionLists = new WeakMap();
+
+            /**
+             * Cached action lists
+             *
+             * Holds values only during the time between `beforerender` and `rendered`
+             *
+             * @type {{}}
+             * @private
+             */
+            this._cachedActionLists = {};
         }
 
         /**
-         * Enable auto refresh on the given container
-         *
-         * @param {string} containerId
-         */
-        enableAutoRefresh(containerId) {
-            delete document.getElementById(containerId).dataset.suspendAutorefresh;
-        }
-
-        onClick(event) {
-            let _this = event.data.self;
-            let target = event.currentTarget;
-
-            if (target.matches('a') && (! target.matches('.subject') || event.ctrlKey || event.metaKey)) {
-                return true;
-            }
-
-            event.preventDefault();
-            event.stopImmediatePropagation();
-            event.stopPropagation();
-
-            let item = target.closest('[data-action-item]');
-            let list = target.closest('.action-list');
-            let activeItems = _this.getActiveItems(list);
-            let toActiveItems = [],
-                toDeactivateItems = [];
-
-            const isBeingMultiSelected = list.matches('[data-icinga-multiselect-url]')
-                && (event.ctrlKey || event.metaKey || event.shiftKey);
-
-            if (isBeingMultiSelected) {
-                if (event.ctrlKey || event.metaKey) {
-                    if (item.classList.contains('active')) {
-                        toDeactivateItems.push(item);
-                    } else {
-                        toActiveItems.push(item);
-                    }
-                } else {
-                    document.getSelection().removeAllRanges();
-
-                    let allItems = _this.getAllItems(list);
-
-                    let startIndex = allItems.indexOf(item);
-                    if(startIndex < 0) {
-                        startIndex = 0;
-                    }
-
-                    let endIndex = activeItems.length ? allItems.indexOf(activeItems[0]) : 0;
-                    if (startIndex > endIndex) {
-                        toActiveItems = allItems.slice(endIndex, startIndex + 1);
-                    } else {
-                        endIndex = activeItems.length ? allItems.indexOf(activeItems[activeItems.length - 1]) : 0;
-                        toActiveItems = allItems.slice(startIndex, endIndex + 1);
-                    }
-
-                    toDeactivateItems = activeItems.filter(item => ! toActiveItems.includes(item));
-                    toActiveItems = toActiveItems.filter(item => ! activeItems.includes(item));
-                }
-            } else {
-                toDeactivateItems = activeItems;
-                toActiveItems.push(item);
-            }
-
-            if (activeItems.length === 1
-                && toActiveItems.length === 0
-                && _this.icinga.loader.getLinkTargetFor($(target)).attr('id') === 'col2'
-            ) {
-                _this.icinga.ui.layout1col();
-                _this.icinga.history.pushCurrentState();
-                _this.enableAutoRefresh('col1');
-                return;
-            }
-
-            let dashboard = list.closest('.dashboard');
-            if (dashboard) {
-                dashboard.querySelectorAll('.action-list').forEach(otherList => {
-                    if (otherList !== list) {
-                        toDeactivateItems.push(..._this.getAllItems(otherList));
-                    }
-                })
-            }
-
-            let lastActivatedUrl = null;
-            if (toActiveItems.includes(item)) {
-                lastActivatedUrl = item.dataset.icingaDetailFilter;
-            } else if (activeItems.length > 1) {
-                lastActivatedUrl = activeItems[activeItems.length - 1] === item
-                    ? activeItems[activeItems.length - 2].dataset.icingaDetailFilter
-                    : activeItems[activeItems.length - 1].dataset.icingaDetailFilter;
-            }
-
-            _this.clearSelection(toDeactivateItems);
-            _this.setActive(toActiveItems);
-
-            if (! dashboard) {
-                _this.addSelectionCountToFooter(list);
-            }
-
-            _this.setLastActivatedItemUrl(lastActivatedUrl);
-            _this.loadDetailUrl(list, target.matches('a') ? target.getAttribute('href') : null);
-        }
-
-        /**
-         * Add the selection count to footer if list allow multi selection
-         *
-         * @param list
+         * @param event
+         * @param content
+         * @param action
+         * @param autorefresh
+         * @param scripted
          */
-        addSelectionCountToFooter(list) {
-            if (! list.matches('[data-icinga-multiselect-url]')) {
+        onBeforeRender(event, content, action, autorefresh, scripted) {
+            if (! autorefresh) {
                 return;
             }
 
-            let activeItemCount = this.getActiveItems(list).length;
-            let footer = list.closest('.container').querySelector('.footer');
-
-            // For items that do not have a bottom status bar like Downtimes, Comments...
-            if (footer === null) {
-                footer = notjQuery.render(
-                    '<div class="footer" data-action-list-automatically-added>' +
-                            '<div class="selection-count"><span class="selected-items"></span></div>' +
-                        '</div>'
-                )
-
-                list.closest('.container').appendChild(footer);
-            }
-
-            let selectionCount = footer.querySelector('.selection-count');
-            if (selectionCount === null) {
-                selectionCount = notjQuery.render(
-                    '<div class="selection-count"><span class="selected-items"></span></div>'
-                );
-
-                footer.prepend(selectionCount);
-            }
-
-            let selectedItems = selectionCount.querySelector('.selected-items');
-            selectedItems.innerText = activeItemCount
-                ? list.dataset.icingaMultiselectCountLabel.replace('%d', activeItemCount)
-                : list.dataset.icingaMultiselectHintLabel;
+            let _this = event.data.self;
+            let lists = event.target.querySelectorAll('[data-interactable-action-list]');
 
-            if (activeItemCount === 0) {
-                selectedItems.classList.add('hint');
-            } else {
-                selectedItems.classList.remove('hint');
-            }
+            // Remember current instances
+            lists.forEach((list) => {
+                let actionList = _this._actionLists.get(list);
+                if (actionList) {
+                    _this._cachedActionLists[_this.icinga.utils.getDomPath(list).join(' > ')] = actionList;
+                }
+            });
         }
 
         /**
-         * Key navigation for .action-list
-         *
-         * - `Shift + ArrowUp|ArrowDown` = Multiselect
-         * - `ArrowUp|ArrowDown` = Select next/previous
-         * - `Ctrl|cmd + A` = Select all on currect page
-         *
          * @param event
+         * @param autorefresh
+         * @param scripted
          */
-        onKeyDown(event) {
+        onRendered(event, autorefresh, scripted) {
             let _this = event.data.self;
-            let list = null;
-            let pressedArrowDownKey = event.key === 'ArrowDown';
-            let pressedArrowUpKey = event.key === 'ArrowUp';
-            let focusedElement = document.activeElement;
-
-            if (
-                _this.isProcessingLoadMore
-                || ! event.key // input auto-completion is triggered
-                || (event.key.toLowerCase() !== 'a' && ! pressedArrowDownKey && ! pressedArrowUpKey)
-            ) {
-                return;
-            }
-
-            if (focusedElement && (
-                focusedElement.matches('#main > :scope')
-                || focusedElement.matches('#body'))
-            ) {
-                let activeItem = document.querySelector(
-                    '#main > .container > .content > .action-list [data-action-item].active'
-                );
-                if (activeItem) {
-                    list = activeItem.closest('.action-list');
-                } else {
-                    list = focusedElement.querySelector('#main > .container > .content > .action-list');
-                }
-            } else if (focusedElement) {
-                list = focusedElement.closest('.content > .action-list');
-            }
-
-            if (! list) {
-                return;
-            }
-
-            let isMultiSelectableList = list.matches('[data-icinga-multiselect-url]');
-
-            if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'a') {
-                if (! isMultiSelectableList) {
-                    return;
-                }
-
-                event.preventDefault();
-                _this.selectAll(list);
-                return;
-            }
-
-            event.preventDefault();
-
-            let allItems = _this.getAllItems(list);
-            let firstListItem = allItems[0];
-            let lastListItem = allItems[allItems.length -1];
-            let activeItems = _this.getActiveItems(list);
-            let markAsLastActive = null; // initialized only if it is different from toActiveItem
-            let toActiveItem = null;
-            let wasAllSelected = activeItems.length === allItems.length;
-            let lastActivatedItem = list.querySelector(
-                `[data-icinga-detail-filter="${ _this.lastActivatedItemUrl }"]`
-            );
+            let container = event.target;
+            let detailUrl = _this.getDetailUrl();
 
-            if (! lastActivatedItem && activeItems.length) {
-                lastActivatedItem = activeItems[activeItems.length - 1];
+            if (event.target.id === 'col2') {
+                console.log(event, autorefresh, scripted, detailUrl);
             }
 
-            let directionalNextItem = _this.getDirectionalNext(lastActivatedItem, event.key);
-
-            if (activeItems.length === 0) {
-                toActiveItem = pressedArrowDownKey ? firstListItem : lastListItem;
-                // reset all on manual page refresh
-                _this.clearSelection(activeItems);
-                if (toActiveItem.classList.contains('load-more')) {
-                    toActiveItem = toActiveItem.previousElementSibling;
-                }
-            } else if (isMultiSelectableList && event.shiftKey) {
-                if (activeItems.length === 1) {
-                    toActiveItem = directionalNextItem;
-                } else if (wasAllSelected && (
-                    (lastActivatedItem !== firstListItem && pressedArrowDownKey)
-                    || (lastActivatedItem !== lastListItem && pressedArrowUpKey)
-                )) {
-                    if (pressedArrowDownKey) {
-                        toActiveItem = lastActivatedItem === lastListItem ? null : lastListItem;
+            if (autorefresh) {
+                // Apply remembered instances
+                for (let listPath in _this._cachedActionLists) {
+                    let actionList = _this._cachedActionLists[listPath];
+                    let list = container.querySelector(listPath);
+                    if (list !== null) {
+                        actionList.refresh(list, detailUrl);
+                        _this._actionLists.set(list, actionList);
                     } else {
-                        toActiveItem = lastActivatedItem === firstListItem ? null : lastListItem;
-                    }
-
-                } else if (directionalNextItem && directionalNextItem.classList.contains('active')) {
-                    // deactivate last activated by down to up select
-                    _this.clearSelection([lastActivatedItem]);
-                    if (wasAllSelected) {
-                        _this.scrollItemIntoView(lastActivatedItem, event.key);
-                    }
-
-                    toActiveItem = directionalNextItem;
-                } else {
-                    [toActiveItem, markAsLastActive] = _this.findToActiveItem(lastActivatedItem, event.key);
-                }
-            } else {
-                toActiveItem = directionalNextItem ?? lastActivatedItem;
-
-                if (toActiveItem) {
-                    if (toActiveItem.classList.contains('load-more')) {
-                        clearTimeout(_this.lastTimeoutId);
-                        _this.handleLoadMoreNavigate(toActiveItem, lastActivatedItem, event.key);
-                        return;
-                    }
-
-                    _this.clearSelection(activeItems);
-                    if (toActiveItem.classList.contains('page-separator')) {
-                        toActiveItem = _this.getDirectionalNext(toActiveItem, event.key);
+                        actionList.destroy();
                     }
-                }
-            }
-
-            if (! toActiveItem) {
-                return;
-            }
-
-            _this.setActive(toActiveItem);
-            _this.setLastActivatedItemUrl(
-                markAsLastActive ? markAsLastActive.dataset.icingaDetailFilter : toActiveItem.dataset.icingaDetailFilter
-            );
-            _this.scrollItemIntoView(toActiveItem, event.key);
-            _this.addSelectionCountToFooter(list);
-            _this.loadDetailUrl(list);
-        }
-
-        /**
-         * Get the next list item according to the pressed key (`ArrowUp` or `ArrowDown`)
-         *
-         * @param item The list item from which we want the next item
-         * @param eventKey Pressed key (`ArrowUp` or `ArrowDown`)
-         *
-         * @returns {Element|null}
-         */
-        getDirectionalNext(item, eventKey) {
-            if (! item) {
-                return null;
-            }
 
-            return eventKey === 'ArrowUp' ? item.previousElementSibling : item.nextElementSibling;
-        }
-
-        /**
-         * Find the list item that should be activated next
-         *
-         * @param lastActivatedItem
-         * @param eventKey Pressed key (`ArrowUp` or `ArrowDown`)
-         *
-         * @returns {Element[]}
-         */
-        findToActiveItem(lastActivatedItem, eventKey) {
-            let toActiveItem;
-            let markAsLastActive;
-
-            toActiveItem = this.getDirectionalNext(lastActivatedItem, eventKey);
-
-            while (toActiveItem) {
-                if (! toActiveItem.classList.contains('active')) {
-                    break;
+                    delete _this._cachedActionLists[listPath];
                 }
-
-                toActiveItem = this.getDirectionalNext(toActiveItem, eventKey);
             }
 
-            markAsLastActive = toActiveItem;
-            // if the next/previous sibling element is already active,
-            // mark the last/first active element in list as last active
-            while (markAsLastActive && this.getDirectionalNext(markAsLastActive, eventKey)) {
-                if (! this.getDirectionalNext(markAsLastActive, eventKey).classList.contains('active')) {
-                    break;
-                }
-
-                markAsLastActive = this.getDirectionalNext(markAsLastActive, eventKey);
-            }
+            event.currentTarget.querySelectorAll('[data-interactable-action-list]').forEach(list => {
+                let actionList = _this._actionLists.get(list);
 
-            return [toActiveItem, markAsLastActive];
-        }
+                if (! actionList) {
+                    actionList = (new ActionList(list)).bind();
+                    actionList.load(detailUrl);
 
-        /**
-         * Select All list items
-         *
-         * @param list The action list
-         */
-        selectAll(list) {
-            let allItems = this.getAllItems(list);
-            let activeItems = this.getActiveItems(list);
-            this.setActive(allItems.filter(item => ! activeItems.includes(item)));
-            this.setLastActivatedItemUrl(allItems[allItems.length -1].dataset.icingaDetailFilter);
-            this.addSelectionCountToFooter(list);
-            this.loadDetailUrl(list);
-        }
-
-        /**
-         * Clear the selection by removing .active class
-         *
-         * @param selectedItems The items with class active
-         */
-        clearSelection(selectedItems) {
-            selectedItems.forEach(item => item.classList.remove('active'));
-        }
-
-        /**
-         * Set the last activated item Url
-         *
-         * @param url
-         */
-        setLastActivatedItemUrl (url) {
-            this.lastActivatedItemUrl = url;
-        }
-
-        /**
-         * Scroll the given item into view
-         *
-         * @param item Item to scroll into view
-         * @param pressedKey Pressed key (`ArrowUp` or `ArrowDown`)
-         */
-        scrollItemIntoView(item, pressedKey) {
-            item.scrollIntoView({block: "nearest"});
-            let directionalNext = this.getDirectionalNext(item, pressedKey);
-
-            if (directionalNext) {
-                directionalNext.scrollIntoView({block: "nearest"});
-            }
-        }
-
-        /**
-         * Load the detail url with selected items
-         *
-         * @param list The action list
-         * @param anchorUrl If any anchor is clicked (e.g. host in service list)
-         */
-        loadDetailUrl(list, anchorUrl = null) {
-            let url = anchorUrl;
-            let activeItems = this.getActiveItems(list);
-
-            if (url === null) {
-                if (activeItems.length > 1) {
-                    url = this.createMultiSelectUrl(activeItems);
+                    _this._actionLists.set(list, actionList);
                 } else {
-                    let anchor = activeItems[0].querySelector('[href]');
-                    url = anchor ? anchor.getAttribute('href') : null;
+                    actionList.load(detailUrl); // navigated back to the same page
                 }
-            }
-
-            if (url === null) {
-                return;
-            }
-
-            const suspendedContainer = this.suspendAutoRefresh(list);
-
-            clearTimeout(this.lastTimeoutId);
-            this.lastTimeoutId = setTimeout(() => {
-                const requestNo = this.lastTimeoutId;
-                this.activeRequests[requestNo] = suspendedContainer;
-                this.lastTimeoutId = null;
-
-                let req = this.icinga.loader.loadUrl(
-                    url,
-                    this.icinga.loader.getLinkTargetFor($(activeItems[0]))
-                );
-
-                req.always((_, __, errorThrown) => {
-                    if (errorThrown !== 'abort') {
-                        this.enableAutoRefresh(this.activeRequests[requestNo]);
-                    }
-
-                    delete this.activeRequests[requestNo];
-                });
-            }, 250);
-        }
-
-        /**
-         * Add .active class to given list item
-         *
-         * @param toActiveItem The list item(s)
-         */
-        setActive(toActiveItem) {
-            if (toActiveItem instanceof HTMLElement) {
-                toActiveItem.classList.add('active');
-            } else {
-                toActiveItem.forEach(item => item.classList.add('active'));
-            }
-        }
-
-        /**
-         * Get the active items from given list
-         *
-         * @param list The action list
-         *
-         * @return array
-         */
-        getActiveItems(list)
-        {
-            let items;
-            if (list.tagName.toLowerCase() === 'table') {
-                items = list.querySelectorAll(':scope > tbody > [data-action-item].active');
-            } else {
-                items = list.querySelectorAll(':scope > [data-action-item].active');
-            }
-
-            return Array.from(items);
+            });
         }
 
-        /**
-         * Get all available items from given list
-         *
-         * @param list The action list
-         *
-         * @return array
-         */
-        getAllItems(list)
+        onColumnClose(event)
         {
-            let items;
-            if (list.tagName.toLowerCase() === 'table') {
-                items = list.querySelectorAll(':scope > tbody > [data-action-item]');
-            } else {
-                items = list.querySelectorAll(':scope > [data-action-item]');
-            }
-
-            return Array.from(items);
-        }
-
-        /**
-         * Handle the navigation on load-more button
-         *
-         * @param loadMoreElement
-         * @param lastActivatedItem
-         * @param pressedKey Pressed key (`ArrowUp` or `ArrowDown`)
-         */
-        handleLoadMoreNavigate(loadMoreElement, lastActivatedItem, pressedKey) {
-            let req = this.loadMore(loadMoreElement.firstChild);
-            this.isProcessingLoadMore = true;
-            req.done(() => {
-                this.isProcessingLoadMore = false;
-                // list has now new items, so select the lastActivatedItem and then move forward
-                let toActiveItem = lastActivatedItem.nextElementSibling;
-                while (toActiveItem) {
-                    if (toActiveItem.hasAttribute('data-action-item')) {
-                        this.clearSelection([lastActivatedItem]);
-                        this.setActive(toActiveItem);
-                        this.setLastActivatedItemUrl(toActiveItem.dataset.icingaDetailFilter);
-                        this.scrollItemIntoView(toActiveItem, pressedKey);
-                        this.addSelectionCountToFooter(toActiveItem.parentElement);
-                        this.loadDetailUrl(toActiveItem.parentElement);
-                        return;
-                    }
-
-                    toActiveItem = toActiveItem.nextElementSibling;
+            let _this = event.data.self;
+            let lists = document.querySelectorAll('[data-interactable-action-list]');
+            lists.forEach((list) => {
+                let actionList = _this._actionLists.get(list);
+                if (actionList) {
+                    actionList.load();
                 }
             });
         }
 
         /**
-         * Click on load-more button
+         * Triggers when column is moved to left or right
          *
          * @param event
-         *
-         * @returns {boolean}
-         */
-        onLoadMoreClick(event) {
-            event.stopPropagation();
-            event.preventDefault();
-
-            event.data.self.loadMore(event.target);
-
-            return false;
-        }
-
-        onKeyPress(event) {
-            if (event.key === ' ') { // space
-                event.data.self.onLoadMoreClick(event);
-            }
-        }
-
-        /**
-         * Load more list items based on the given anchor
-         *
-         * @param anchor
-         *
-         * @returns {*|{getAllResponseHeaders: function(): *|null, abort: function(*): this, setRequestHeader: function(*, *): this, readyState: number, getResponseHeader: function(*): null|*, overrideMimeType: function(*): this, statusCode: function(*): this}|jQuery|boolean}
+         * @param sourceId The content is moved from
          */
-        loadMore(anchor) {
-            let showMore = anchor.parentElement;
-            var progressTimer = this.icinga.timer.register(function () {
-                var label = anchor.innerText;
-
-                var dots = label.substr(-3);
-                if (dots.slice(0, 1) !== '.') {
-                    dots = '.  ';
-                } else {
-                    label = label.slice(0, -3);
-                    if (dots === '...') {
-                        dots = '.  ';
-                    } else if (dots === '.. ') {
-                        dots = '...';
-                    } else if (dots === '.  ') {
-                        dots = '.. ';
+         onColumnMoved(event, sourceId) {
+            if (event.target.id === 'col2' && sourceId === 'col1') { // only for browser-back (col1 shifted to col2)
+                let lists = event.target.querySelectorAll('[data-interactable-action-list]');
+                lists.forEach((list) => {
+                    let _this = event.data.self;
+                    let actionList = _this._actionLists.get(list);
+                    if (actionList) {
+                        actionList.load();
                     }
-                }
-
-                anchor.innerText = label + dots;
-            }, null, 250);
-
-            let url = anchor.getAttribute('href');
-            let req = this.icinga.loader.loadUrl(
-                // Add showCompact, we don't want controls in paged results
-                this.icinga.utils.addUrlFlag(url, 'showCompact'),
-                $(showMore.parentElement),
-                undefined,
-                undefined,
-                'append',
-                false,
-                progressTimer
-            );
-            req.addToHistory = false;
-            req.done(function () {
-                showMore.remove();
-
-                // Set data-icinga-url to make it available for Icinga.History.getCurrentState()
-                req.$target.closest('.container').data('icingaUrl', url);
-
-                this.icinga.history.replaceCurrentState();
-            });
-
-            return req;
-        }
-
-        /**
-         * Create the detail url for multi selectable list
-         *
-         * @param items List items
-         * @param withBaseUrl Default to true
-         *
-         * @returns {string} The url
-         */
-        createMultiSelectUrl(items, withBaseUrl = true) {
-            let filters = [];
-            items.forEach(item => {
-                filters.push(item.getAttribute('data-icinga-multiselect-filter'));
-            });
-
-            let url = '?' + filters.join('|');
-
-            if (withBaseUrl) {
-                return items[0].closest('.action-list').getAttribute('data-icinga-multiselect-url') + url;
-            }
-
-            return url;
-        }
-
-        onColumnClose(event) {
-            let _this = event.data.self;
-            let list = _this.findDetailUrlActionList(document.getElementById('col1'));
-            if (list && list.matches('[data-icinga-multiselect-url], [data-icinga-detail-url]')) {
-                _this.clearSelection(_this.getActiveItems(list));
-                _this.addSelectionCountToFooter(list);
+                });
             }
-        }
+         }
 
         /**
-         * Find the action list using the detail url
-         *
-         * @param {Element} container
+         * Selection started and in process
          *
-         * @return Element|null
+         * @param event
          */
-        findDetailUrlActionList(container) {
-            let detailUrl = this.icinga.utils.parseUrl(
-                this.icinga.history.getCol2State().replace(/^#!/, '')
-            );
-
-            let detailItem = container.querySelector(
-                '[data-icinga-detail-filter="'
-                + detailUrl.query.replace('?', '') + '"],' +
-                '[data-icinga-multiselect-filter="'
-                + detailUrl.query.split('|', 1).toString().replace('?', '') + '"]'
-            );
-
-            return detailItem ? detailItem.parentElement : null;
+        onSelectionStart(event) {
+            const container = event.target.closest('.container');
+            container.dataset.suspendAutorefresh = '';
         }
 
         /**
-         * Triggers when column is moved to left or right
-         *
+         * Triggers when selection ends, the url can be loaded now
          * @param event
-         * @param sourceId The content is moved from
          */
-        onColumnMoved(event, sourceId) {
+        onSelectionEnd(event) {
             let _this = event.data.self;
 
-            if (event.target.id === 'col2' && sourceId === 'col1') { // only for browser-back (col1 shifted to col2)
-                _this.clearSelection(event.target.querySelectorAll('.action-list .active'));
-            } else if (event.target.id === 'col1' && sourceId === 'col2') {
-                for (const requestNo of Object.keys(_this.activeRequests)) {
-                    if (_this.activeRequests[requestNo] === sourceId) {
-                        _this.enableAutoRefresh(_this.activeRequests[requestNo]);
-                        _this.activeRequests[requestNo] = _this.suspendAutoRefresh(event.target);
-                    }
+            let req = _this.icinga.loader.loadUrl(
+                event.detail.url,
+                _this.icinga.loader.getLinkTargetFor($(event.target.firstChild))
+            );
+
+            req.always((_, __, errorThrown) => {
+                if (errorThrown !== 'abort') {
+                    delete event.target.closest('.container').dataset.suspendAutorefresh;
                 }
-            }
+            });
         }
 
-        onRendered(event, isAutoRefresh) {
+        allDeselected(event) {
             let _this = event.data.self;
-            let container = event.target;
-            let isTopLevelContainer = container.matches('#main > :scope');
-
-            let list;
-            if (event.currentTarget !== container || Object.keys(_this.activeRequests).length) {
-                // Nested containers are not processed multiple times || still processing selection/navigation request
-                return;
-            } else if (isTopLevelContainer && container.id !== 'col1') {
-                if (isAutoRefresh) {
-                    return;
-                }
-
-                // only for browser back/forward navigation
-                list = _this.findDetailUrlActionList(document.getElementById('col1'));
-            } else {
-                list = _this.findDetailUrlActionList(container);
-            }
-
-            if (list && list.matches('[data-icinga-multiselect-url], [data-icinga-detail-url]')) {
-                let detailUrl = _this.icinga.utils.parseUrl(
-                    _this.icinga.history.getCol2State().replace(/^#!/, '')
-                );
-                let toActiveItems = [];
-                if (list.dataset.icingaMultiselectUrl === detailUrl.path) {
-                    for (const filter of _this.parseSelectionQuery(detailUrl.query.slice(1))) {
-                        let item = list.querySelector(
-                            '[data-icinga-multiselect-filter="' + filter + '"]'
-                        );
-
-                        if (item) {
-                            toActiveItems.push(item);
-                        }
-                    }
-                } else if (_this.matchesDetailUrl(list.dataset.icingaDetailUrl, detailUrl.path)) {
-                    let item = list.querySelector(
-                        '[data-icinga-detail-filter="' + detailUrl.query.slice(1) + '"]'
-                    );
-
-                    if (item) {
-                        toActiveItems.push(item);
-                    }
-                }
-
-                _this.clearSelection(_this.getAllItems(list).filter(item => !toActiveItems.includes(item)));
-                _this.setActive(toActiveItems);
-            }
-
-            if (isTopLevelContainer) {
-                let footerList = list ?? container.querySelector('.content > .action-list');
-                if (footerList) {
-                    _this.addSelectionCountToFooter(footerList);
-                }
+            if (_this.icinga.loader.getLinkTargetFor($(event.target), false).attr('id') === 'col2') {
+                _this.icinga.ui.layout1col();
+                _this.icinga.history.pushCurrentState();
+                delete event.target.closest('.container').dataset.suspendAutorefresh;
             }
         }
 
-        matchesDetailUrl(itemUrl, detailUrl) {
-            if (itemUrl === detailUrl) {
-                return true;
-            }
-
-            // The slash is used to avoid false positives (e.g. icingadb/hostgroup and icingadb/host)
-            return detailUrl.startsWith(itemUrl + '/');
+        getDetailUrl() {
+            return this.icinga.utils.parseUrl(
+                this.icinga.history.getCol2State().replace(/^#!/, '')
+            );
         }
     }
 
-    Icinga.Behaviors.ActionList = ActionList;
+    Icinga.Behaviors = Icinga.Behaviors || {};
 
-}(Icinga));
+    Icinga.Behaviors.ActionListBehavior = ActionListBehavior;
+})(Icinga);