Within a browser, JavaScript has a permanent access to the
window
object, which itself points to the DOM:window.document
.At loading time, two key events occur: the fact that the DOM (i.e., the HTML “tree of content”) is ready for processing and the fact that the browser window is itself ready (i.e., all resources as images, sounds, videos… are ready for processing). Typically, any
img
DOM element leads to aHTMLImageElement
object (Image
interface in JavaScript). Accessing this DOM element strictly requires that the DOM has finished loading. However, this image cannot be processed (e.g., grayed) until the browser has not yet finished loading all resources including this image.Example Theo_is_crying.ts.zip
const Sounds: Array<string> = new Array; // Available sounds... createjs.Sound.on('fileload', (event: { id: string }) => { // This is fired for each sound that is registered... Sounds.push(event.id); }); // No need to wait for either DOM or Window loaded: createjs.Sound.registerSound("./sounds/Baby_crying.mp3", "Baby_crying_sound_ID"); // createjs.Sound.registerSound("./sounds/Baby_laughing.mp3", "Baby_laughing_sound_ID"); let my_image: HTMLImageElement | null = null; window.document.onreadystatechange = function () { // Called *TWO TIMES*: when 'interactive' and later on... when 'complete' if (window.document.readyState === 'interactive') { window.console.log("DOM just loaded..."); my_image = window.document.getElementById("my_image") as HTMLImageElement; my_image.addEventListener('mouseover', function () { if (Sounds.indexOf("Baby_crying_sound_ID") !== -1) { const sound = createjs.Sound.play("Baby_crying_sound_ID"); sound.on('complete', () => window.console.log("Baby_crying_sound_ID" + " just completed...")); } }); } else { if (window.document.readyState === 'complete') { window.console.log("Window just loaded..."); if (my_image === null) { // We missed 'interactive'... my_image = window.document.getElementById("my_image") as HTMLImageElement; my_image.addEventListener('mouseover', function () { if (Sounds.indexOf("Baby_crying_sound_ID") !== -1) { const sound = createjs.Sound.play("Baby_crying_sound_ID"); sound.on('complete', () => window.console.log("Baby_crying_sound_ID" + " just completed...")); } }); } let image = new Image(); image.onload = function () { window.console.log("Image ready for processing? " + image.width, 'x', image.height); }; image.src = my_image.src; // Image load starts... } } };
window
DOM
window.addEventListener('DOMContentLoaded', (event: Event) => { window.console.assert(window.document.readyState === 'interactive'); window.console.log("DOM just loaded..."); // Main_first_part(); // Call 'Main_first_part' program: access DOM resources... });
window
window.addEventListener('load', (event: Event) => { window.console.assert(window.document.readyState === 'complete'); window.console.log("Window just loaded..."); // Main_second_part(); // Call 'Main_second_part' program: access *ALL* resources... });
window
(alternative)window.onload = (event: Event) => { window.console.assert(window.document.readyState === 'complete'); window.console.log("Window just loaded..."); // Main_second_part(); // Call 'Main_second_part' program: access *ALL* resources... };
The complexity of the DOM as a tree of data items requires powerful querying facilities.
Example Shopping.ts.zip
<body oncontextmenu="return false;"> <div id="Shopping"></div> <!-- The '<div>' element is a block-level element: --> <div class="info"> Shopping... </div> <p class="info" id="Query">Type '20023966'></p> </body>
const div = window.document.querySelector('div'); // Get the first '<div>' element... window.alert(div!.constructor); // 'function HTMLDivElement()...' const div_info = window.document.querySelector('div.info'); // Get the first '<div>' element with class 'info'... window.alert(div_info!.constructor); // 'function HTMLDivElement()...'
The DOM embodies a tree of data items transformable by JavaScript in terms of style (CSS); Other modifications apply on content itself. Beyond, JavaScript may add or remove content items: the switch from static Web to dynamical Web.
const canvas = window.document.createElement('CANVAS') as HTMLCanvasElement; canvas.width = 600; canvas.height = 400; const image_data = canvas.getContext('2d')!.getImageData(0, 0, 600, 400); let pixel_number = 600 * 400; while (--pixel_number >= 0) image_data.data[pixel_number] = buffer[pixel_number]; // Create image content from 'buffer'... canvas.getContext('2d')!.putImageData(image_data, 0, 0); window.document.getElementById("Somewhere_in_the_DOM")!.appendChild(canvas);
window.document.getElementById("Somewhere_in_the_DOM")!.insertBefore(something_to_insert, window.document.getElementById("Somewhere_in_the_DOM")!.childNodes[0]);
Example Shopping.ts.zip
const shopping = window.document.getElementById('Shopping') as HTMLDivElement; window.console.assert(shopping !== null, "'shopping === null', why?"); shopping.style.display = 'grid'; const style: CSSStyleDeclaration = shopping.style; style.gridTemplateColumns = "repeat(" + Shopping._Number_of_columns + ", 1fr)"; style.gridTemplateRows = "fit-content(" + (100 / Shopping._Number_of_rows) + "%)"; style.alignItems = 'center'; /* Value of 'align-self' for children */ style.justifyItems = 'flex-start'; /* Value of 'justify-self' for children */ for (let i = 0; i < Shopping._Number_of_columns * Shopping._Number_of_rows; i++) { let image = new Image; image.style.height = (100 / (Shopping._Number_of_rows)) + "vh"; image.setAttribute("id", "Shopping_image" + i); image.setAttribute("index", i.toString()); // Number is transformed into string! image.style.opacity = "0.5"; image.setAttribute("time-out", "0"); image.style.width = (100 / Shopping._Number_of_columns) + "vw"; image.onload = () => { shopping.appendChild(image); // Images are stored in a random way... }; image.src = './img/Franck.jpg'; }
Check content
Filtering on a CSS style value is impossible in a declarative way using
querySelector
orquerySelectorAll
; it may be done in an algorithmic way. Beyond, while jQuery has support for, JavaScript puts forward the introduction of a class, which matches the targeted CSS value.// Caution: must wait for all 'shopping.appendChild(image);' finished: window.console.assert(shopping.childNodes.length === Shopping._Number_of_columns * Shopping._Number_of_rows); // Filtering by CSS style value is impossible... // Get all 'HTMLImageElement' objects that have a 'style' attribute: const images = shopping.querySelectorAll('img[style]'); window.console.assert(images.length === Shopping._Number_of_columns * Shopping._Number_of_rows);
Rule(s)
- In JavaScript, the notion of form is embodied by the
FormData
type: bothXMLHttpRequest
andfetch
may processFormData
objects.- Trickier cases are the use in
FormData
objects of file objects (FileReader
type) and media objects (Blob
type).Example Form.js.zip
<form id="my_form" name="my_form" method="post" action="https://reqres.in/api/users"> <p><label>Name: <input name="name" required></label></p> <p><label>Job: <input name="job" required></label></p> <p><label>Phone: <input type=tel name="phone" pattern="[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}"></label> <small>(format: 12-34-45-67)</small></p> <p><label>E-mail: <input type=email name="e_mail"></label></p> <fieldset> <legend>Humor</legend> <p><label><input type=radio name=good value="good"> Good</label></p> <p><label><input type=radio name=between value="between"> Not so good, not too bad</label></p> <p><label><input type=radio name=bad value="bad"> Bad</label></p> </fieldset> <p><label>Available from: <input type="date" list="my_availabilities" name="availability"></label></p> <p><label>Description: <textarea name="description"></textarea></label></p> </form> <p><button form='my_form'>Send form...</button></p>
Example (
FormData
inXMLHttpRequest
) Form.js.zipclass ReqRes { static URL() { return 'https://reqres.in'; } static Path() { return '/api/users'; } … static Run2(name = "FranckBarbier", job = "Trainer") { const form_data = new FormData(); // Empty... form_data.append("name", name); form_data.append("job", job); const request = new XMLHttpRequest(); request.onreadystatechange = function () { if (request.readyState === XMLHttpRequest.DONE) { window.console.assert(request.status === 201); // '201' -> Created if (request.responseType === "") // An empty 'responseType' string is treated the same as "text", the default type... window.alert("'ReqRes' ('XMLHttpRequest') RUN2: " + request.responseText); if (request.responseType === "json") window.alert(Object.getOwnPropertyNames(JSON.parse(request)).join(" - ")); } }; request.open('POST', ReqRes.URL() + ReqRes.Path()); request.setRequestHeader('accept', 'application/json; charset=UTF-8'); request.setRequestHeader('content-type', 'application/x-www-form-urlencoded'); // Mandatory to avoid CORS... request.send(form_data); } …
Example (
FormData
infetch
) Form.js.zipclass ReqRes { static URL() { return 'https://reqres.in'; } static Path() { return '/api/users'; } … static Run3(name = "FranckBarbier", job = "Trainer") { const my_form = window.document.getElementById('my_form'); // Not empty... const form_data = new FormData(my_form); // Default content type: 'multipart/form-data' window.console.assert(form_data.has("name") && form_data.has("job")); form_data.set("name", name); form_data.set("job", job); fetch(ReqRes.URL() + ReqRes.Path(), { body: form_data, /*new URLSearchParams("name=" + name + "&job=" + job),*/ headers: { 'accept': 'application/json; charset=UTF-8', // -> response 'content-type': /*'application/x-www-form-urlencoded; charset=UTF-8'*/'multipart/form-data; charset=UTF-8' // Mandatory to avoid CORS... }, method: 'POST' }).then(response => { window.console.assert(response.ok && response.status === 201); // '201' -> Created response.json().then(result => { // window.console.assert(result.name === name); // It fails... window.alert("'ReqRes' ('fetch') RUN3: " + JSON.stringify(result)); }); }); } …
Example (Form,
submit
event) Form.js.zipwindow.document.onreadystatechange = function () { if (window.document.readyState === "interactive") { window.document.forms['my_form'].onsubmit = async(event) => { // Subscription... event.preventDefault(); // window.alert("You just press 'Send form...'"); const body = new URLSearchParams([...new FormData(event.target).entries()]); // <form id="my_form" name="my_form" method="post" action="https://reqres.in/api/users"> const response = await new Response(body).text(); window.alert(response); }; } };