diff --git a/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/ActionDispatcher.ts b/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/ActionDispatcher.ts
index 3c35d23c9c9def07fd40b7f843b05e22daa218e5..c311607226394e1be44c5a15b5b4bc4059a8e6f2 100644
--- a/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/ActionDispatcher.ts
+++ b/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/ActionDispatcher.ts
@@ -16,11 +16,6 @@ import RegularEvent = require('TYPO3/CMS/Core/Event/RegularEvent');
 import shortcutMenu = require('TYPO3/CMS/Backend/Toolbar/ShortcutMenu');
 import documentService = require('TYPO3/CMS/Core/DocumentService');
 
-const delegates: {[key: string]: Function} = {
-  'TYPO3.InfoWindow.showItem': InfoWindow.showItem.bind(null),
-  'TYPO3.ShortcutMenu.createShortcut': shortcutMenu.createShortcut.bind(shortcutMenu),
-};
-
 /**
  * Module: TYPO3/CMS/Backend/ActionDispatcher
  *
@@ -32,9 +27,14 @@ const delegates: {[key: string]: Function} = {
  *  data-dispatch-args="[$quot;tt_content",123]"
  */
 class ActionDispatcher {
+  private delegates: {[key: string]: Function} = {};
+
   private static resolveArguments(element: HTMLElement): null | string[] {
     if (element.dataset.dispatchArgs) {
-      const args = JSON.parse(element.dataset.dispatchArgs);
+      // `"` is the only literal of a PHP `json_encode` that needs to be substituted
+      // all other payload values are expected to be serialized to unicode literals
+      const json = element.dataset.dispatchArgs.replace(/"/g, '"');
+      const args = JSON.parse(json);
       return args instanceof Array ? ActionDispatcher.trimItems(args) : null;
     } else if (element.dataset.dispatchArgsList) {
       const args = element.dataset.dispatchArgsList.split(',');
@@ -67,9 +67,17 @@ class ActionDispatcher {
   }
 
   public constructor() {
+    this.createDelegates();
     documentService.ready().then((): void => this.registerEvents());
   }
 
+  private createDelegates(): void {
+    this.delegates = {
+      'TYPO3.InfoWindow.showItem': InfoWindow.showItem.bind(null),
+      'TYPO3.ShortcutMenu.createShortcut': shortcutMenu.createShortcut.bind(shortcutMenu),
+    };
+  }
+
   private registerEvents(): void {
     new RegularEvent('click', this.handleClickEvent.bind(this))
       .delegateTo(document, '[data-dispatch-action]:not([data-dispatch-immediately])');
@@ -85,8 +93,8 @@ class ActionDispatcher {
   private delegateTo(target: HTMLElement): void {
     const action = target.dataset.dispatchAction;
     const args = ActionDispatcher.resolveArguments(target);
-    if (delegates[action]) {
-      delegates[action].apply(null, args || []);
+    if (this.delegates[action]) {
+      this.delegates[action].apply(null, args || []);
     }
   }
 }
diff --git a/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/GlobalEventHandler.ts b/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/GlobalEventHandler.ts
index 25188a5839dc2d6f9ed925ccf33e79b2d8c079b6..4676ee345bb9550fc28fbd884e9bbfe557c4d48d 100644
--- a/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/GlobalEventHandler.ts
+++ b/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/GlobalEventHandler.ts
@@ -12,6 +12,7 @@
  */
 
 import documentService = require('TYPO3/CMS/Core/DocumentService');
+import RegularEvent = require('TYPO3/CMS/Core/Event/RegularEvent');
 
 type HTMLFormChildElement = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
 
@@ -45,22 +46,20 @@ class GlobalEventHandler {
   };
 
   private registerEvents(): void {
-    document.querySelectorAll(this.options.onChangeSelector).forEach((element: HTMLElement) => {
-      document.addEventListener('change', this.handleChangeEvent.bind(this));
-    });
-    document.querySelectorAll(this.options.onClickSelector).forEach((element: HTMLElement) => {
-      document.addEventListener('click', this.handleClickEvent.bind(this));
-    });
+    new RegularEvent('change', this.handleChangeEvent.bind(this))
+      .delegateTo(document, this.options.onChangeSelector);
+    new RegularEvent('click', this.handleClickEvent.bind(this))
+      .delegateTo(document, this.options.onClickSelector);
   }
 
-  private handleChangeEvent(evt: Event): void {
-    const resolvedTarget = evt.target as HTMLElement;
+  private handleChangeEvent(evt: Event, resolvedTarget: HTMLElement): void {
+    evt.preventDefault();
     this.handleSubmitAction(evt, resolvedTarget)
       || this.handleNavigateAction(evt, resolvedTarget);
   }
 
-  private handleClickEvent(evt: Event): void {
-    const resolvedTarget = evt.currentTarget as HTMLElement;
+  private handleClickEvent(evt: Event, resolvedTarget: HTMLElement): void {
+    evt.preventDefault();
   }
 
   private handleSubmitAction(evt: Event, resolvedTarget: HTMLElement): boolean {
@@ -88,7 +87,7 @@ class GlobalEventHandler {
     }
     const value = this.resolveHTMLFormChildElementValue(resolvedTarget);
     const navigateValue = resolvedTarget.dataset.navigateValue;
-    if (action === '$data=~s/$value/' && value && navigateValue) {
+    if (action === '$data=~s/$value/' && navigateValue && value !== null) {
       // replacing `${value}` and its URL encoded representation
       window.location.href = navigateValue.replace(/(\$\{value\}|%24%7Bvalue%7D)/gi, value);
       return true;
@@ -111,8 +110,11 @@ class GlobalEventHandler {
   }
 
   private resolveHTMLFormChildElementValue(element: HTMLElement): string | null {
+    const type: string = element.getAttribute('type');
     if (element instanceof HTMLSelectElement) {
       return element.options[element.selectedIndex].value;
+    } else if (element instanceof HTMLInputElement && type === 'checkbox') {
+      return element.checked ? element.value : '';
     } else if (element instanceof HTMLInputElement) {
       return element.value;
     }
diff --git a/typo3/sysext/backend/Classes/Clipboard/Clipboard.php b/typo3/sysext/backend/Classes/Clipboard/Clipboard.php
index 87aeb9fd0ef9707b8af1b0ee44f3df466fca43f4..70dc61e11f491592652a4c253b3319c9b847f817 100644
--- a/typo3/sysext/backend/Classes/Clipboard/Clipboard.php
+++ b/typo3/sysext/backend/Classes/Clipboard/Clipboard.php
@@ -404,7 +404,10 @@ class Clipboard
                                     $this->getBackendUser()->uc['titleLen']
                                 )), $fileObject->getName()),
                                 'thumb' => $thumb,
-                                'infoLink' => htmlspecialchars('top.TYPO3.InfoWindow.showItem(' . GeneralUtility::quoteJSvalue($table) . ', ' . GeneralUtility::quoteJSvalue($v) . '); return false;'),
+                                'infoDataDispatch' => [
+                                    'action' => 'TYPO3.InfoWindow.showItem',
+                                    'args' => GeneralUtility::jsonEncodeForHtmlAttribute([$table, $v], false),
+                                ],
                                 'removeLink' => $this->removeUrl('_FILE', GeneralUtility::shortMD5($v))
                             ];
                         } else {
@@ -426,7 +429,10 @@ class Clipboard
                                     $table,
                                     $rec
                                 ), $this->getBackendUser()->uc['titleLen'])), $rec, $table),
-                                'infoLink' => htmlspecialchars('top.TYPO3.InfoWindow.showItem(' . GeneralUtility::quoteJSvalue($table) . ', \'' . (int)$uid . '\'); return false;'),
+                                'infoDataDispatch' => [
+                                    'action' => 'TYPO3.InfoWindow.showItem',
+                                    'args' => GeneralUtility::jsonEncodeForHtmlAttribute([$table, (int)$uid], false),
+                                ],
                                 'removeLink' => $this->removeUrl($table, $uid)
                             ];
 
diff --git a/typo3/sysext/backend/Classes/Controller/Wizard/TableController.php b/typo3/sysext/backend/Classes/Controller/Wizard/TableController.php
index 896d3bccd04022b128ea5e23d560350a6f0ba936..73aef3f5b380ee090322956c2217f30ef9852eb8 100644
--- a/typo3/sysext/backend/Classes/Controller/Wizard/TableController.php
+++ b/typo3/sysext/backend/Classes/Controller/Wizard/TableController.php
@@ -411,7 +411,6 @@ class TableController extends AbstractWizardController
 				</tfoot>';
         }
         $content = '';
-        $addSubmitOnClick = 'onclick="document.getElementById(\'TableController\').submit();"';
         // Implode all table rows into a string, wrapped in table tags.
         $content .= '
 
@@ -428,7 +427,7 @@ class TableController extends AbstractWizardController
 			<div class="checkbox">
 				<input type="hidden" name="TABLE[textFields]" value="0" />
 				<label for="textFields">
-					<input type="checkbox" ' . $addSubmitOnClick . ' name="TABLE[textFields]" id="textFields" value="1"' . ($this->inputStyle ? ' checked="checked"' : '') . ' />
+					<input type="checkbox" data-global-event="change" data-action-submit="$form" name="TABLE[textFields]" id="textFields" value="1"' . ($this->inputStyle ? ' checked="checked"' : '') . ' />
 					' . $this->getLanguageService()->getLL('table_smallFields') . '
 				</label>
 			</div>';
diff --git a/typo3/sysext/backend/Classes/Template/DocumentTemplate.php b/typo3/sysext/backend/Classes/Template/DocumentTemplate.php
index 8fce986b738d1a7adf4aa8bf99341aa23516c17f..db36014aff15cee4a05d91d3694b6e83c23bb10f 100644
--- a/typo3/sysext/backend/Classes/Template/DocumentTemplate.php
+++ b/typo3/sysext/backend/Classes/Template/DocumentTemplate.php
@@ -280,6 +280,8 @@ function jumpToUrl(URL) {
         $this->pageRenderer->enableConcatenateJavascript();
         $this->pageRenderer->enableCompressCss();
         $this->pageRenderer->enableCompressJavascript();
+        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/GlobalEventHandler');
+        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ActionDispatcher');
         if ($GLOBALS['TYPO3_CONF_VARS']['BE']['debug']) {
             $this->pageRenderer->enableDebugMode();
         }
diff --git a/typo3/sysext/backend/Classes/Utility/BackendUtility.php b/typo3/sysext/backend/Classes/Utility/BackendUtility.php
index d1c1b53972b43a04fc71ea037fefd41fb98fe018..76531fc4786019584bbabd8382e9e2902adae67b 100644
--- a/typo3/sysext/backend/Classes/Utility/BackendUtility.php
+++ b/typo3/sysext/backend/Classes/Utility/BackendUtility.php
@@ -1196,8 +1196,13 @@ class BackendUtility
                         . '</span>';
                 }
                 if ($linkInfoPopup) {
-                    $onClick = 'top.TYPO3.InfoWindow.showItem(\'_FILE\',\'' . (int)$fileObject->getUid() . '\'); return false;';
-                    $thumbData .= '<a href="#" onclick="' . htmlspecialchars($onClick) . '">' . $imgTag . '</a> ';
+                    // @todo Should we add requireJsModule again (should be loaded in most/all cases)
+                    // loadRequireJsModule('TYPO3/CMS/Backend/ActionDispatcher');
+                    $attributes = GeneralUtility::implodeAttributes([
+                        'data-dispatch-action' => 'TYPO3.InfoWindow.showItem',
+                        'data-dispatch-args-list' => '_FILE,' . (int)$fileObject->getUid(),
+                    ], true);
+                    $thumbData .= '<a href="#" ' . $attributes . '>' . $imgTag . '</a> ';
                 } else {
                     $thumbData .= $imgTag;
                 }
@@ -2616,15 +2621,21 @@ class BackendUtility
         $dataMenuIdentifier = GeneralUtility::camelCaseToLowerCaseUnderscored($dataMenuIdentifier);
         $dataMenuIdentifier = str_replace('_', '-', $dataMenuIdentifier);
         if (!empty($options)) {
-            $onChange = 'window.location.href = ' . GeneralUtility::quoteJSvalue($scriptUrl . '&' . $elementName . '=') . '+this.options[this.selectedIndex].value;';
-            return '
-
-				<!-- Function Menu of module -->
-				<select class="form-control" name="' . $elementName . '" onchange="' . htmlspecialchars($onChange) . '" data-menu-identifier="' . htmlspecialchars($dataMenuIdentifier) . '">
-					' . implode('
-					', $options) . '
-				</select>
-						';
+            // @todo Should we add requireJsModule again (should be loaded in most/all cases)
+            // loadRequireJsModule('TYPO3/CMS/Backend/GlobalEventHandler');
+            $attributes = GeneralUtility::implodeAttributes([
+                'name' => $elementName,
+                'class' => 'form-control',
+                'data-menu-identifier' => $dataMenuIdentifier,
+                'data-global-event' => 'change',
+                'data-action-navigate' => '$data=~s/$value/',
+                'data-navigate-value' => $scriptUrl . '&' . $elementName . '=${value}',
+            ], true);
+            return sprintf(
+                '<select %s>%s</select>select>',
+                $attributes,
+                implode('', $options)
+            );
         }
         return '';
     }
@@ -2665,11 +2676,20 @@ class BackendUtility
         $dataMenuIdentifier = GeneralUtility::camelCaseToLowerCaseUnderscored($dataMenuIdentifier);
         $dataMenuIdentifier = str_replace('_', '-', $dataMenuIdentifier);
         if (!empty($options)) {
+            // @todo Should we add requireJsModule again (should be loaded in most/all cases)
+            // loadRequireJsModule('TYPO3/CMS/Backend/GlobalEventHandler');
             $onChange = 'window.location.href = ' . GeneralUtility::quoteJSvalue($scriptUrl . '&' . $elementName . '=') . '+this.options[this.selectedIndex].value;';
+            $attributes = GeneralUtility::implodeAttributes([
+                'name' => $elementName,
+                'data-menu-identifier' => $dataMenuIdentifier,
+                'data-global-event' => 'change',
+                'data-action-navigate' => '$data=~s/$value/',
+                'data-navigate-value' => $scriptUrl . '&' . $elementName . '=${value}',
+            ], true);
             return '
 			<div class="form-group">
 				<!-- Function Menu of module -->
-				<select class="form-control input-sm" name="' . htmlspecialchars($elementName) . '" onchange="' . htmlspecialchars($onChange) . '" data-menu-identifier="' . htmlspecialchars($dataMenuIdentifier) . '">
+				<select class="form-control input-sm" ' . $attributes . '>
 					' . implode(LF, $options) . '
 				</select>
 			</div>
@@ -2699,18 +2719,22 @@ class BackendUtility
         $addParams = '',
         $tagParams = ''
     ) {
+        // @todo Should we add requireJsModule again (should be loaded in most/all cases)
+        // loadRequireJsModule('TYPO3/CMS/Backend/GlobalEventHandler');
         $scriptUrl = self::buildScriptUrl($mainParams, $addParams, $script);
-        $onClick = 'window.location.href = ' . GeneralUtility::quoteJSvalue($scriptUrl . '&' . $elementName . '=') . '+(this.checked?1:0);';
-
+        $attributes = GeneralUtility::implodeAttributes([
+            'type' => 'checkbox',
+            'class' => 'checkbox',
+            'name' => $elementName,
+            'value' => 1,
+            'data-global-event' => 'change',
+            'data-action-navigate' => '$data=~s/$value/',
+            'data-navigate-value' => sprintf('%s&%s=${value}', $scriptUrl, $elementName),
+        ], true);
         return
-            '<input' .
-            ' type="checkbox"' .
-            ' class="checkbox"' .
-            ' name="' . $elementName . '"' .
+            '<input ' . $attributes .
             ($currentValue ? ' checked="checked"' : '') .
-            ' onclick="' . htmlspecialchars($onClick) . '"' .
             ($tagParams ? ' ' . $tagParams : '') .
-            ' value="1"' .
             ' />';
     }
 
diff --git a/typo3/sysext/backend/Classes/View/PageLayoutView.php b/typo3/sysext/backend/Classes/View/PageLayoutView.php
index cd1c81dd52defdb82a2287aebfe27eb8c4fc9dad..d8c6143140f6c1fe967de579004229f59a19c605 100644
--- a/typo3/sysext/backend/Classes/View/PageLayoutView.php
+++ b/typo3/sysext/backend/Classes/View/PageLayoutView.php
@@ -1603,7 +1603,7 @@ class PageLayoutView implements LoggerAwareInterface
 
             return '<div class="form-inline form-inline-spaced">'
                 . '<div class="form-group">'
-                . '<select class="form-control input-sm" name="createNewLanguage" onchange="window.location.href=this.options[this.selectedIndex].value">'
+                . '<select class="form-control input-sm" name="createNewLanguage" data-global-event="change" data-action-navigate="$value">'
                 . $output
                 . '</select></div></div>';
         }
diff --git a/typo3/sysext/backend/Resources/Private/Partials/Clipboard/TabContent.html b/typo3/sysext/backend/Resources/Private/Partials/Clipboard/TabContent.html
index 02f460ee7e4d9b64fb47b438047f59b9bed48de8..40ddc3ab1f5f168d61525ef55a697aed1c5ad6eb 100644
--- a/typo3/sysext/backend/Resources/Private/Partials/Clipboard/TabContent.html
+++ b/typo3/sysext/backend/Resources/Private/Partials/Clipboard/TabContent.html
@@ -22,9 +22,11 @@
         </f:if>
     </td>
     <td class="col-control nowrap">
-        <f:if condition="{content.infoLink}">
+        <f:if condition="{content.infoDataDispatch}">
             <div class="btn-group">
-                <a class="btn btn-default" href="#" onclick="{content.infoLink}"
+                <a class="btn btn-default" href="#"
+                    data-dispatch-action="{content.infoDataDispatch.action}"
+                    data-dispatch-args="{content.infoDataDispatch.args}"
                     title="{f:translate(key: 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.info')}">
                     <f:format.raw>
                         <core:icon identifier="actions-document-info" alternativeMarkupIdentifier="inline"/>
diff --git a/typo3/sysext/backend/Resources/Private/Partials/PageLayout/LanguageColumns.html b/typo3/sysext/backend/Resources/Private/Partials/PageLayout/LanguageColumns.html
index 172d073b07bb5ed4dbb098aade23da40ac8681c6..335b46096949dad98940e009fc67aa38a4e384e9 100644
--- a/typo3/sysext/backend/Resources/Private/Partials/PageLayout/LanguageColumns.html
+++ b/typo3/sysext/backend/Resources/Private/Partials/PageLayout/LanguageColumns.html
@@ -1,7 +1,7 @@
 <f:if condition="{context.newLanguageOptions}">
     <div class="form-inline form-inline-spaced">
         <div class="form-group">
-            <select class="form-control input-sm" name="createNewLanguage" onchange="window.location.href=this.options[this.selectedIndex].value">'
+            <select class="form-control input-sm" name="createNewLanguage" data-global-event="change" data-action-navigate="$value">'
                 <f:for each="{context.newLanguageOptions}" as="languageName" key="url">
                     <option value="{url}">{languageName}</option>
                 </f:for>
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/ActionDispatcher.js b/typo3/sysext/backend/Resources/Public/JavaScript/ActionDispatcher.js
index b8199599761d8b64943410beb00898d656fb5682..2ce07a873ad137a50770a345eba15e45425ed6a0 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/ActionDispatcher.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/ActionDispatcher.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-define(["require","exports","TYPO3/CMS/Backend/InfoWindow","TYPO3/CMS/Core/Event/RegularEvent","TYPO3/CMS/Backend/Toolbar/ShortcutMenu","TYPO3/CMS/Core/DocumentService"],(function(t,e,n,a,s,i){"use strict";const r={"TYPO3.InfoWindow.showItem":n.showItem.bind(null),"TYPO3.ShortcutMenu.createShortcut":s.createShortcut.bind(s)};class c{static resolveArguments(t){if(t.dataset.dispatchArgs){const e=JSON.parse(t.dataset.dispatchArgs);return e instanceof Array?c.trimItems(e):null}if(t.dataset.dispatchArgsList){const e=t.dataset.dispatchArgsList.split(",");return c.trimItems(e)}return null}static trimItems(t){return t.map(t=>t instanceof String?t.trim():t)}static enrichItems(t,e,n){return t.map(t=>t instanceof Object&&t.$event?t.$target?n:t.$event?e:void 0:t)}constructor(){i.ready().then(()=>this.registerEvents())}registerEvents(){new a("click",this.handleClickEvent.bind(this)).delegateTo(document,"[data-dispatch-action]:not([data-dispatch-immediately])"),document.querySelectorAll("[data-dispatch-action][data-dispatch-immediately]").forEach(this.delegateTo.bind(this))}handleClickEvent(t,e){t.preventDefault(),this.delegateTo(e)}delegateTo(t){const e=t.dataset.dispatchAction,n=c.resolveArguments(t);r[e]&&r[e].apply(null,n||[])}}return new c}));
\ No newline at end of file
+define(["require","exports","TYPO3/CMS/Backend/InfoWindow","TYPO3/CMS/Core/Event/RegularEvent","TYPO3/CMS/Backend/Toolbar/ShortcutMenu","TYPO3/CMS/Core/DocumentService"],(function(t,e,a,s,i,n){"use strict";class r{constructor(){this.delegates={},this.createDelegates(),n.ready().then(()=>this.registerEvents())}static resolveArguments(t){if(t.dataset.dispatchArgs){const e=t.dataset.dispatchArgs.replace(/&quot;/g,'"'),a=JSON.parse(e);return a instanceof Array?r.trimItems(a):null}if(t.dataset.dispatchArgsList){const e=t.dataset.dispatchArgsList.split(",");return r.trimItems(e)}return null}static trimItems(t){return t.map(t=>t instanceof String?t.trim():t)}static enrichItems(t,e,a){return t.map(t=>t instanceof Object&&t.$event?t.$target?a:t.$event?e:void 0:t)}createDelegates(){this.delegates={"TYPO3.InfoWindow.showItem":a.showItem.bind(null),"TYPO3.ShortcutMenu.createShortcut":i.createShortcut.bind(i)}}registerEvents(){new s("click",this.handleClickEvent.bind(this)).delegateTo(document,"[data-dispatch-action]:not([data-dispatch-immediately])"),document.querySelectorAll("[data-dispatch-action][data-dispatch-immediately]").forEach(this.delegateTo.bind(this))}handleClickEvent(t,e){t.preventDefault(),this.delegateTo(e)}delegateTo(t){const e=t.dataset.dispatchAction,a=r.resolveArguments(t);this.delegates[e]&&this.delegates[e].apply(null,a||[])}}return new r}));
\ No newline at end of file
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/GlobalEventHandler.js b/typo3/sysext/backend/Resources/Public/JavaScript/GlobalEventHandler.js
index 326c2c9de5ad0b7e164180f162d936f76124c286..f7c769d996ced87cc60978300affc7245a5a7093 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/GlobalEventHandler.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/GlobalEventHandler.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-define(["require","exports","TYPO3/CMS/Core/DocumentService"],(function(e,t,n){"use strict";return new class{constructor(){this.options={onChangeSelector:'[data-global-event="change"]',onClickSelector:'[data-global-event="click"]'},n.ready().then(()=>this.registerEvents())}registerEvents(){document.querySelectorAll(this.options.onChangeSelector).forEach(e=>{document.addEventListener("change",this.handleChangeEvent.bind(this))}),document.querySelectorAll(this.options.onClickSelector).forEach(e=>{document.addEventListener("click",this.handleClickEvent.bind(this))})}handleChangeEvent(e){const t=e.target;this.handleSubmitAction(e,t)||this.handleNavigateAction(e,t)}handleClickEvent(e){e.currentTarget}handleSubmitAction(e,t){const n=t.dataset.actionSubmit;if(!n)return!1;if("$form"===n&&this.isHTMLFormChildElement(t))return t.form.submit(),!0;const i=document.querySelector(n);return i instanceof HTMLFormElement&&(i.submit(),!0)}handleNavigateAction(e,t){const n=t.dataset.actionNavigate;if(!n)return!1;const i=this.resolveHTMLFormChildElementValue(t),o=t.dataset.navigateValue;return"$data=~s/$value/"===n&&i&&o?(window.location.href=o.replace(/(\$\{value\}|%24%7Bvalue%7D)/gi,i),!0):"$data"===n&&o?(window.location.href=o,!0):!("$value"!==n||!i)&&(window.location.href=i,!0)}isHTMLFormChildElement(e){return e instanceof HTMLSelectElement||e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement}resolveHTMLFormChildElementValue(e){return e instanceof HTMLSelectElement?e.options[e.selectedIndex].value:e instanceof HTMLInputElement?e.value:null}}}));
\ No newline at end of file
+define(["require","exports","TYPO3/CMS/Core/DocumentService","TYPO3/CMS/Core/Event/RegularEvent"],(function(e,t,n,i){"use strict";return new class{constructor(){this.options={onChangeSelector:'[data-global-event="change"]',onClickSelector:'[data-global-event="click"]'},n.ready().then(()=>this.registerEvents())}registerEvents(){new i("change",this.handleChangeEvent.bind(this)).delegateTo(document,this.options.onChangeSelector),new i("click",this.handleClickEvent.bind(this)).delegateTo(document,this.options.onClickSelector)}handleChangeEvent(e,t){e.preventDefault(),this.handleSubmitAction(e,t)||this.handleNavigateAction(e,t)}handleClickEvent(e,t){e.preventDefault()}handleSubmitAction(e,t){const n=t.dataset.actionSubmit;if(!n)return!1;if("$form"===n&&this.isHTMLFormChildElement(t))return t.form.submit(),!0;const i=document.querySelector(n);return i instanceof HTMLFormElement&&(i.submit(),!0)}handleNavigateAction(e,t){const n=t.dataset.actionNavigate;if(!n)return!1;const i=this.resolveHTMLFormChildElementValue(t),a=t.dataset.navigateValue;return"$data=~s/$value/"===n&&a&&null!==i?(window.location.href=a.replace(/(\$\{value\}|%24%7Bvalue%7D)/gi,i),!0):"$data"===n&&a?(window.location.href=a,!0):!("$value"!==n||!i)&&(window.location.href=i,!0)}isHTMLFormChildElement(e){return e instanceof HTMLSelectElement||e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement}resolveHTMLFormChildElementValue(e){const t=e.getAttribute("type");return e instanceof HTMLSelectElement?e.options[e.selectedIndex].value:e instanceof HTMLInputElement&&"checkbox"===t?e.checked?e.value:"":e instanceof HTMLInputElement?e.value:null}}}));
\ No newline at end of file
diff --git a/typo3/sysext/core/Classes/Database/QueryView.php b/typo3/sysext/core/Classes/Database/QueryView.php
index 690008c0bfd842eb086dada13f4fd9d508051073..4cda41cde72179267564ec9a7103ec3da0a55525 100644
--- a/typo3/sysext/core/Classes/Database/QueryView.php
+++ b/typo3/sysext/core/Classes/Database/QueryView.php
@@ -702,9 +702,12 @@ class QueryView
             $out .= '<a class="btn btn-default" href="' . htmlspecialchars($url) . '">'
                 . $this->iconFactory->getIcon('actions-open', Icon::SIZE_SMALL)->render() . '</a>';
             $out .= '</div><div class="btn-group" role="group">';
-            $out .= '<a class="btn btn-default" href="#" onClick="top.TYPO3.InfoWindow.showItem(\'' . $table . '\',' . $row['uid']
-                . ');return false;">' . $this->iconFactory->getIcon('actions-document-info', Icon::SIZE_SMALL)->render()
-                . '</a>';
+            $out .= sprintf(
+                '<a class="btn btn-default" href="#" data-dispatch-action="%s" data-dispatch-args-list="%s">%s</a>',
+                'TYPO3.InfoWindow.showItem',
+                htmlspecialchars($table . ',' . $row['uid']),
+                $this->iconFactory->getIcon('actions-document-info', Icon::SIZE_SMALL)->render()
+            );
             $out .= '</div>';
         } else {
             $out .= '<div class="btn-group" role="group">';
diff --git a/typo3/sysext/core/Classes/Utility/GeneralUtility.php b/typo3/sysext/core/Classes/Utility/GeneralUtility.php
index de4216ad7ad1c8cf650bfedd1d500c0f8fd30f31..34f2f633ad7a0775c47f1f9dd2cb9c00a1fd85cd 100644
--- a/typo3/sysext/core/Classes/Utility/GeneralUtility.php
+++ b/typo3/sysext/core/Classes/Utility/GeneralUtility.php
@@ -3742,6 +3742,17 @@ class GeneralUtility
         );
     }
 
+    /**
+     * @param mixed $value
+     * @param bool $useHtmlEntities
+     * @return string
+     */
+    public static function jsonEncodeForHtmlAttribute($value, bool $useHtmlEntities = true): string
+    {
+        $json = json_encode($value, JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG);
+        return $useHtmlEntities ? htmlspecialchars($json) : $json;
+    }
+
     /**
      * Set the ApplicationContext
      *
diff --git a/typo3/sysext/core/Documentation/Changelog/10.4.x/Important-91117-UseGlobalEventHandlerAndActionDispatcherInsteadOfInlineJS.rst b/typo3/sysext/core/Documentation/Changelog/10.4.x/Important-91117-UseGlobalEventHandlerAndActionDispatcherInsteadOfInlineJS.rst
new file mode 100644
index 0000000000000000000000000000000000000000..f3fb631aaa494becdb66aa74f31c95b7a53a90a0
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/10.4.x/Important-91117-UseGlobalEventHandlerAndActionDispatcherInsteadOfInlineJS.rst
@@ -0,0 +1,76 @@
+.. include:: ../../Includes.txt
+
+====================================================================================
+Important: #91117 - Use GlobalEventHandler and ActionDispatcher instead of inline JS
+====================================================================================
+
+See :issue:`91117`
+
+Description
+===========
+
+In order to reduce the amount of inline JavaScript (with the goal to pave the
+way towards stronger Content-Security-Policy assignments) lots of inline JavaScript
+code parts have been substituted by a declarative syntax - basically using HTML
+:html:`data-*` attributes.
+
+The following list collects an overview of common JavaScript snippets and their
+corresponding substitute using modules :js:`TYPO3/CMS/Backend/GlobalEventHandler`
+and :js:`TYPO3/CMS/Backend/ActionDispatcher`.
+
+
+`TYPO3/CMS/Backend/GlobalEventHandler`
+--------------------------------------
+
+.. code-block:: html
+
+   <select onchange="window.location.href=this.options[this.selectedIndex].value;">'
+   <!-- ... changed to ... -->
+   <select data-global-event="change" data-action-navigate="$value">'
+
+Navigates to URL once selected drop-down was changed
+(`$value` refers to selected value)
+
+
+.. code-block:: html
+
+   <select value="0" name="depth"
+      onchange="window.location.href='https://example.org/__VAL__'.replace(/__VAL__/, this.options[this.selectedIndex].value);">
+   <!-- ... changed to ... -->
+   <select value="0" name="depth" data-global-event="change"
+      data-action-navigate="$data=~s/$value/" data-navigate-value="https://example.org/${value}">
+
+Navigates to URL once selected drop-down was changed, including selected value
+(`$data` refers to value of :html:`data-navigate-value`, `$value` to selected value,
+`$data=~s/$value/` replaces literal `${value}` with selected value in `:html:`data-navigate-value`)
+
+
+.. code-block:: html
+
+   <input type="checkbox" onclick="document.getElementById('formIdentifier').submit();">
+   <!-- ... changed to ... -->
+   <input type="checkbox" data-global-event="change" data-action-submit="$form">
+   <!-- ... or (using CSS selector) ... -->
+   <input type="checkbox" data-global-event="change" data-action-submit="#formIdentifier">
+
+Submits a form once a value has been changed
+(`$form` refers to paren form element, using CSS selectors like `#formIdentifier`
+is possible as well)
+
+
+`TYPO3/CMS/Backend/ActionDispatcher`
+------------------------------------
+
+.. code-block:: html
+
+   <a href="#" onclick="top.TYPO3.InfoWindow.showItem('tt_content', 123); return false;">
+   <!-- ... changed to ... -->
+   data-dispatch-action="TYPO3.InfoWindow.showItem" data-dispatch-args-list="be_users,123">
+   <!-- ... or (using JSON arguments) ... -->
+   data-dispatch-action="TYPO3.InfoWindow.showItem" data-dispatch-args="[&quot;tt_content&quot;,123]">
+
+Invokes :js:`TYPO3.InfoWindow.showItem` module function to display details for a given
+record (of database table `tt_content`, having `uid=123` in the example above)
+
+
+.. index:: Backend, JavaScript, ext:backend
diff --git a/typo3/sysext/fluid/Classes/ViewHelpers/Be/TableListViewHelper.php b/typo3/sysext/fluid/Classes/ViewHelpers/Be/TableListViewHelper.php
index a889b62cb85fb7067fcada76648ebc88e5837deb..6ca9b3f07ada02d9f48a7ba114ecffba963b5532 100644
--- a/typo3/sysext/fluid/Classes/ViewHelpers/Be/TableListViewHelper.php
+++ b/typo3/sysext/fluid/Classes/ViewHelpers/Be/TableListViewHelper.php
@@ -123,6 +123,7 @@ class TableListViewHelper extends AbstractBackendViewHelper
         $clickTitleMode = $this->arguments['clickTitleMode'];
 
         $this->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Recordlist/Recordlist');
+        $this->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ActionDispatcher');
 
         $pageinfo = BackendUtility::readPageAccess(GeneralUtility::_GP('id'), $GLOBALS['BE_USER']->getPagePermsClause(Permission::PAGE_SHOW));
         /** @var \TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList $dblist */
diff --git a/typo3/sysext/info/Classes/Controller/InfoPageTyposcriptConfigController.php b/typo3/sysext/info/Classes/Controller/InfoPageTyposcriptConfigController.php
index 3193de31515b0c7416e8fa01bff563b9e62fd34b..a1582fcedcf62311eaf5fca01aadeef34bbf58d7 100644
--- a/typo3/sysext/info/Classes/Controller/InfoPageTyposcriptConfigController.php
+++ b/typo3/sysext/info/Classes/Controller/InfoPageTyposcriptConfigController.php
@@ -65,8 +65,7 @@ class InfoPageTyposcriptConfigController
     public function init($pObj)
     {
         $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
-        $languageService = $this->getLanguageService();
-        $languageService->includeLLFile('EXT:info/Resources/Private/Language/InfoPageTsConfig.xlf');
+        $this->getLanguageService()->includeLLFile('EXT:info/Resources/Private/Language/InfoPageTsConfig.xlf');
         $this->view = $this->getFluidTemplateObject();
         $this->pObj = $pObj;
         $this->id = (int)GeneralUtility::_GP('id');
diff --git a/typo3/sysext/recordlist/Classes/Controller/RecordListController.php b/typo3/sysext/recordlist/Classes/Controller/RecordListController.php
index 19fb1924baf198931bd2344e3010297e361e74bf..e1d2e63a6ef5aaef8bbbd46807a9c11bdff8bd34 100644
--- a/typo3/sysext/recordlist/Classes/Controller/RecordListController.php
+++ b/typo3/sysext/recordlist/Classes/Controller/RecordListController.php
@@ -616,7 +616,7 @@ class RecordListController
 
             return '<div class="form-inline form-inline-spaced">'
                 . '<div class="form-group">'
-                . '<select class="form-control input-sm" name="createNewLanguage" onchange="window.location.href=this.options[this.selectedIndex].value">'
+                . '<select class="form-control input-sm" name="createNewLanguage" data-global-event="change" data-action-navigate="$value">'
                 . $output
                 . '</select></div></div>';
         }
diff --git a/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php b/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php
index 50226a621f70e11533e4eb152fde2f2a9a7b7265..26cd4161b32d809d64dec82cde00e3259885e2bc 100644
--- a/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php
+++ b/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php
@@ -1874,8 +1874,7 @@ class DatabaseRecordList
         }
         $this->addActionToCellGroup($cells, $editAction, 'edit');
         // "Info": (All records)
-        $onClick = 'top.TYPO3.InfoWindow.showItem(' . GeneralUtility::quoteJSvalue($table) . ', ' . (int)$row['uid'] . '); return false;';
-        $viewBigAction = '<a class="btn btn-default" href="#" onclick="' . htmlspecialchars($onClick) . '" title="' . htmlspecialchars($this->getLanguageService()->getLL('showInfo')) . '">'
+        $viewBigAction = '<a class="btn btn-default" href="#" ' . $this->createShowItemTagAttributes($table . ',' . (int)$row['uid']) . ' title="' . htmlspecialchars($this->getLanguageService()->getLL('showInfo')) . '">'
             . $this->iconFactory->getIcon('actions-document-info', Icon::SIZE_SMALL)->render() . '</a>';
         $this->addActionToCellGroup($cells, $viewBigAction, 'viewBig');
         // "Move" wizard link for pages/tt_content elements:
@@ -3507,9 +3506,8 @@ class DatabaseRecordList
                 break;
             case 'info':
                 // "Info": (All records)
-                $code = '<a href="#" onclick="' . htmlspecialchars(
-                    'top.TYPO3.InfoWindow.showItem(' . GeneralUtility::quoteJSvalue($table) . ', ' . (int)$row['uid'] . '); return false;'
-                ) . '" title="' . htmlspecialchars($lang->getLL('showInfo')) . '">' . $code . '</a>';
+                $code = '<a href="#" ' . $this->createShowItemTagAttributes($table . ',' . (int)$row['uid'])
+                    . ' title="' . htmlspecialchars($lang->getLL('showInfo')) . '">' . $code . '</a>';
                 break;
             default:
                 // Output the label now:
@@ -4044,9 +4042,7 @@ class DatabaseRecordList
         } else {
             $htmlCode = '<a href="#"';
             if ($launchViewParameter !== '') {
-                $htmlCode .= ' onclick="' . htmlspecialchars(
-                    'top.TYPO3.InfoWindow.showItem(' . $launchViewParameter . '); return false;'
-                ) . '"';
+                $htmlCode .= ' ' . $this->createShowItemTagAttributes($launchViewParameter);
             }
             $htmlCode .= ' title="' . htmlspecialchars(
                 $this->getLanguageService()->sL(
@@ -4070,6 +4066,20 @@ class DatabaseRecordList
         $this->showOnlyTranslatedRecords = $showOnlyTranslatedRecords;
     }
 
+    /**
+     * Creates data attributes to be handles in moddule `TYPO3/CMS/Backend/ActionDispatcher`
+     *
+     * @param string $arguments
+     * @return string
+     */
+    protected function createShowItemTagAttributes(string $arguments): string
+    {
+        return GeneralUtility::implodeAttributes([
+            'data-dispatch-action' => 'TYPO3.InfoWindow.showItem',
+            'data-dispatch-args-list' => $arguments,
+        ], true);
+    }
+
     /**
      * Flatten palettes into types showitem
      *