Document Object Model (DOM)

Document Object Model (DOM), access

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 a HTMLImageElement 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 window has not yet finished loading all resources including this image.

// No need to wait for either the DOM or -all resources- are fully loaded:
createjs.Sound.registerSound("./sounds/Baby_crying.mp3", "Baby_crying_sound_ID");
let my_image /* : HTMLImageElement */ = null; // Checked by TypeScript: 'HTMLImageElement'
window.addEventListener("DOMContentLoaded", function () { // DOM just loaded...
    window.console.assert(window.document.readyState !== "loading");
    my_image = window.document.getElementById("my_image");
    …
});

Theo_is_crying.js.zip 

DOM, alternative access

In rare cases, this function handler may miss "interactive".

// No need to wait for either the DOM or -all resources- are fully loaded:
createjs.Sound.registerSound("./sounds/Baby_crying.mp3", "Baby_crying_sound_ID");
let my_image /* : HTMLImageElement */ = null; // Checked by TypeScript: 'HTMLImageElement'window.document.onreadystatechange = function () { // Called **A PRIORI** *TWO TIMES*: when "interactive" and later on... when "complete"
window.document.onreadystatechange = function () { // Called *A PRIORI 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");
        my_image.addEventListener('mouseover', function () {
            createjs.Sound.play("Baby_crying_sound_ID"); // Potential bug: later, at playing time, the sound may not yet be loaded...
        });
    } else {
        window.console.assert(window.document.readyState === "complete");
        window.console.log("-all resources- just loaded...");
    }
}

Theo_is_crying.js.zip 

Catching “DOM is ready” as a promise

let DOM_ready = null;
Object.defineProperty(window, "DOM_ready", {value: new Promise(function_launched_when_DOM_ready => {
    DOM_ready = function_launched_when_DOM_ready;
}), enumerable: false, configurable: false, writable: false});
window.document.onreadystatechange = DOM_ready;
…
// In many other places, one may check whether the DOM is ready:
window.DOM_ready.then(value => { // Anonymous function as parameter of 'then'. This function has itself 'value' as parameter...
    window.document.body.innerWidth = 800; // Access to 'body' content piece and modification...
    window.document.body.innerHeight = 600; // Access to 'body' content piece and modification...
    // Etc.
});

Synchronization models for the browser window (inside HTML)

<script>
    window.addEventListener("load", function(event) {
        window.console.assert(window.document.readyState === "complete");
        window.console.log("-all resources- just loaded...");
        main(); // Call main program...
    });
</script>
<script>
    window.onload = (event) => {
        window.console.assert(window.document.readyState === "complete");
        window.console.log("-all resources- just loaded...");
        main(); // Call main program...
    };
</script>
<script>
    window.addEventListener("beforeunload", function(event) {
        window.console.log("-all resources- are still there, but...");
    });
</script>

Non optimal practices

<script src="./js/main.js"></script>
…
<script>
    window.onload = (event) => {
        window.console.assert(window.document.readyState === "complete");
        window.console.log("-all resources- just loaded...");
        main(); // Call main program...
    };
</script>
const main = function () {
    let image = new Image();
    image.onload = function () {
        window.console.assert(image && image.complete, "Image *IS NOT* ready for processing, why?");
        window.console.log("Image is ready for processing: " + image.width, 'x', image.height);
    };
    image.src = "https://www.snopes.com/tachyon/2015/04/crybaby.jpg"; // Image load starts...
};

Catching “browser window is ready” as a promise

let Window_loaded = null;
Object.defineProperty(window, "Window_loaded", {value: new Promise(function_launched_when_Window_loaded => {
    Window_loaded = function_launched_when_Window_loaded;
}), enumerable: false, configurable: false, writable: false});
window.addEventListener("load", Window_loaded);
…
// In many other places, one may check whether both the DOM is ready and the browser window (images, sounds, videos…) is loaded:
Promise.all([window.DOM_ready, window.Window_loaded]).then(value => { // 'value' is an array of results provided by 'window.DOM_ready' and 'window.Window_loaded'
    window.alert("Everything is now ready for the Web!");
});

Writing the DOM

The DOM embodies a tree of content 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');
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) { // Create image content from 'buffer'...
    image_data.data[pixel_number] = buffer[pixel_number];
}
canvas.getContext('2d').putImageData(image_data, 0, 0);
window.document.getElementById('Somewhere_in_the_DOM').appendChild(canvas);
window.document.getElementById('parent').insertBefore(
    something_to_insert,
    window.document.getElementById('parent').childNodes[0]
);

Reading (querying) the DOM (see also: here…)

The complexity of the DOM as a tree of content items requires powerful querying facilities.

<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 '777'></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()...'

Shopping.js.zip 

Create content and CSS

const shopping = window.document.getElementById('Shopping');
shopping.style.display = "grid";
shopping.style['grid-template-columns'] = "repeat(" + Shopping._Number_of_columns + ", 1fr)";
shopping.style['grid-template-rows'] = "fit-content(" + (100 / Shopping._Number_of_rows) + "%)";
shopping.style['align-items'] = "center"; /* Value of 'align-self' for children */
shopping.style['justify-items'] = "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); // Number is transformed into string!
    image.style.opacity = "0.5";
    image.setAttribute("time-out", 0); // Number is transformed into string!
    image.style.width = (100 / Shopping._Number_of_columns) + "vw";
    image.onload = () => { shopping.appendChild(image); /* Images are stored in a random way...*/ };
    image.src = './img/Common/Franck.jpg';
}

Shopping.js.zip 

Shopping

Check content

Filtering on a CSS style value is impossible in a declarative way using querySelector or querySelectorAll. Anyway, it may be done in an algorithmic way. Beyond, while jQuery has support for, common JavaScript puts forward the introduction of a class, which matches the targeted CSS value.

// Must wait for all 'shopping.appendChild(image);' finished:
window.console.assert(shopping.childNodes.length ===
    Shopping._Number_of_columns * Shopping._Number_of_rows);
// 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);

© Franck Barbier