From ff739d54dd395a25dd23283dd85d6804720c3481 Mon Sep 17 00:00:00 2001 From: Andreas Fernandez <a.fernandez@scripting-base.de> Date: Wed, 5 Jul 2023 11:31:57 +0200 Subject: [PATCH] [BUGFIX] Use event delegation for frontend links TYPO3 ships some default handling for encrypted email links and popup windows. Unfortunately, only links that were available on initial document load were handled, links that were added after couldn't get handled. To solve the issue, the events are not directly bound to such links anymore, but handled by an event delegation approach. Resolves: #101228 Releases: main, 12.4, 11.5 Change-Id: I8ec5d5736d9f9cfe8496cf69cf1128f1af4c0eb8 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/79782 Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de> Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de> Tested-by: core-ci <typo3@b13.com> --- .../Public/JavaScript/default_frontend.js | 75 ++++++++----------- 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/typo3/sysext/frontend/Resources/Public/JavaScript/default_frontend.js b/typo3/sysext/frontend/Resources/Public/JavaScript/default_frontend.js index 06e2a58b91b8..1e360c88d769 100644 --- a/typo3/sysext/frontend/Resources/Public/JavaScript/default_frontend.js +++ b/typo3/sysext/frontend/Resources/Public/JavaScript/default_frontend.js @@ -1,23 +1,4 @@ (function() { - /** - * @param {Function} callback - */ - function ready(callback) { - if (document.readyState === 'complete') { - callback.call(null); - return; - } - var clearListeners = function() { - window.removeEventListener('load', delegate); - document.removeEventListener('DOMContentLoaded', delegate); - }; - var delegate = function() { - clearListeners(); - callback.call(null); - }; - window.addEventListener('load', delegate); - document.addEventListener('DOMContentLoaded', delegate); - } /** * Decoding helper function * @@ -76,29 +57,37 @@ return windowRef; } - ready(function() { - var mailtoElements = document.querySelectorAll('a[data-mailto-token][data-mailto-vector]'); - // `Array.from` for IE compatibility - Array.from(mailtoElements).forEach(function(element) { - element.addEventListener('click', function(evt) { - evt.preventDefault(); - var dataset = evt.currentTarget.dataset; - var value = dataset.mailtoToken; - var offset = parseInt(dataset.mailtoVector, 10) * -1; - document.location.href = decryptString(value, offset); - }); - }); - var openElements = document.querySelectorAll('a[data-window-url]'); - // `Array.from` for IE compatibility - Array.from(openElements).forEach(function(element) { - element.addEventListener('click', function(evt) { - evt.preventDefault(); - var dataset = evt.currentTarget.dataset; - var url = dataset.windowUrl; - var target = dataset.windowTarget || null; - var features = dataset.windowFeatures || null; - windowOpen(url, target, features); - }); - }); + /** + * Delegates event handling to elements + * + * @param {string} event + * @param {string} selector + * @param {function} callback + */ + function delegateEvent(event, selector, callback) { + document.addEventListener(event, function(evt) { + for (var targetElement = evt.target; targetElement && targetElement !== document; targetElement = targetElement.parentNode) { + if (targetElement.matches(selector)) { + callback.call(targetElement, evt, targetElement); + } + } + }); + } + + delegateEvent('click', 'a[data-mailto-token][data-mailto-vector]', function(evt, evtTarget) { + evt.preventDefault(); + var dataset = evtTarget.dataset; + var value = dataset.mailtoToken; + var offset = parseInt(dataset.mailtoVector, 10) * -1; + document.location.href = decryptString(value, offset); + }); + + delegateEvent('click', 'a[data-window-url]', function(evt, evtTarget) { + evt.preventDefault(); + var dataset = evtTarget.dataset; + var url = dataset.windowUrl; + var target = dataset.windowTarget || null; + var features = dataset.windowFeatures || null; + windowOpen(url, target, features); }); })(); -- GitLab