Skip to content
Snippets Groups Projects
Commit a5b512fd authored by Oliver Hader's avatar Oliver Hader Committed by Oliver Hader
Browse files

[FEATURE] Introduce DocumentService as JQuery.ready substitute

Module TYPO3/CMS/Core/DocumentService provides native JavaScript
functions to detect DOM ready-state returning a Promise<Document>.

`$(document).ready(() => {...});` can be replaced by
`documentService.ready().then(() => {...});`

Resolves: #91122
Releases: master
Change-Id: Id812f786430f1ced6265493dd0bae472b8144588
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/64241


Tested-by: default avatarAndreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: default avatarOliver Hader <oliver.hader@typo3.org>
Reviewed-by: default avatarAndreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: default avatarOliver Hader <oliver.hader@typo3.org>
parent 3a337aa4
Branches
Tags
No related merge requests found
......@@ -11,6 +11,8 @@
* The TYPO3 project - inspiring people to share!
*/
import documentService = require('TYPO3/CMS/Core/DocumentService');
type HTMLFormChildElement = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
/**
......@@ -37,25 +39,9 @@ class GlobalEventHandler {
};
constructor() {
this.onReady(() => {
this.registerEvents();
});
documentService.ready().then((): void => this.registerEvents());
};
private onReady(callback: Function): void {
if (document.readyState === 'complete') {
callback.call(this);
} else {
const delegate = () => {
window.removeEventListener('load', delegate);
document.removeEventListener('DOMContentLoaded', delegate);
callback.call(this);
};
window.addEventListener('load', delegate);
document.addEventListener('DOMContentLoaded', delegate);
}
}
private registerEvents(): void {
document.querySelectorAll(this.options.onChangeSelector).forEach((element: HTMLElement) => {
document.addEventListener('change', this.handleChangeEvent.bind(this));
......
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Core/DocumentService
* @exports TYPO3/CMS/Core/DocumentService
*/
class DocumentService {
private readonly windowRef: Window;
private readonly documentRef: Document;
/**
* @param {Window} windowRef
* @param {Document} documentRef
*/
constructor(windowRef: Window = window, documentRef: Document = document) {
this.windowRef = windowRef;
this.documentRef = documentRef;
}
ready(): Promise<Document> {
return new Promise<Document>((resolve: Function, reject: Function) => {
if (this.documentRef.readyState === 'complete') {
resolve(this.documentRef);
} else {
// timeout & reject after 30 seconds
const timer = setTimeout((): void => {
clearListeners();
reject(this.documentRef);
}, 30000);
const clearListeners = (): void => {
this.windowRef.removeEventListener('load', delegate);
this.documentRef.removeEventListener('DOMContentLoaded', delegate);
};
const delegate = (): void => {
clearListeners();
clearTimeout(timer);
resolve(this.documentRef);
};
this.windowRef.addEventListener('load', delegate);
this.documentRef.addEventListener('DOMContentLoaded', delegate);
}
});
}
}
const documentService = new DocumentService();
export = documentService;
......@@ -10,4 +10,4 @@
*
* The TYPO3 project - inspiring people to share!
*/
define(["require","exports"],(function(e,t){"use strict";return new class{constructor(){this.options={onChangeSelector:'[data-global-event="change"]',onClickSelector:'[data-global-event="click"]'},this.onReady(()=>{this.registerEvents()})}onReady(e){if("complete"===document.readyState)e.call(this);else{const t=()=>{window.removeEventListener("load",t),document.removeEventListener("DOMContentLoaded",t),e.call(this)};window.addEventListener("load",t),document.addEventListener("DOMContentLoaded",t)}}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 o=document.querySelector(n);return o instanceof HTMLFormElement&&(o.submit(),!0)}handleNavigateAction(e,t){const n=t.dataset.actionNavigate;if(!n)return!1;const o=this.resolveHTMLFormChildElementValue(t);return!("$value"!==n||!o)&&(window.location.href=o,!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"],(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);return!("$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
.. include:: ../../Includes.txt
======================================================================
Feature: #91122 - Introduce DocumentService as JQuery.ready substitute
======================================================================
See :issue:`91122`
Description
===========
The module :js:`TYPO3/CMS/Core/DocumentService` provides native JavaScript
functions to detect DOM ready-state returning a :js:`Promise<Document>`.
Internally the Promise is resolved when native :js:`DOMContentLoaded` event has
been emitted or when :js:`document.readyState` is defined already. It means
that initial HTML document has been completely loaded and parsed, without
waiting for stylesheets, images, and subframes to finish loading.
Impact
======
.. code-block:: javascript
$(document).ready(() => {
// your application code
});
Above JQuery code can be transformed into the following using :js:`DocumentService`:
.. code-block:: javascript
require(['TYPO3/CMS/Core/DocumentService'], function (DocumentService) {
DocumentService.ready().then(() => {
// your application code
});
});
.. index:: Backend, JavaScript, ext:core
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
define(["require","exports"],(function(e,t){"use strict";return new class{constructor(e=window,t=document){this.windowRef=e,this.documentRef=t}ready(){return new Promise((e,t)=>{if("complete"===this.documentRef.readyState)e(this.documentRef);else{const n=setTimeout(()=>{o(),t(this.documentRef)},3e4),o=()=>{this.windowRef.removeEventListener("load",i),this.documentRef.removeEventListener("DOMContentLoaded",i)},i=()=>{o(),clearTimeout(n),e(this.documentRef)};this.windowRef.addEventListener("load",i),this.documentRef.addEventListener("DOMContentLoaded",i)}})}}}));
\ No newline at end of file
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment