Manipulating the DOM, Structurally

HTML5 may be created, removed, or altered dynamically after the page has been rendered by your browser. This is done with DOM functionality in JavaScript. But before we start here's the base:

Example 8.7. styles.css
body {
    background-color: black;
    border: 1px solid black;
    color: white;
    height: 700px;
    width: 750px;
    margin: 1em auto;
}
h1 {
    color: pink;
}
.fl {
    background-color: silver;
    border: 1px solid blue;
    color: black;
    float: left;
    height: 27%;
    margin: 1%;
    width: 31%;
}
#ttt {
}

Example 8.8. index.html
<!doctype html>
<html>
    <head>
        <title>What Is This?</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link href="styles.css" rel="stylesheet"/>
    </head>
    <body>
        <div class='fl'></div>
        <div class='fl'></div>
        <div class='fl'></div>
        <div class='fl'></div>
        <div class='fl'></div>
        <div class='fl'></div>
        <div class='fl' id="ttt"></div>
        <div class='fl'></div>
        <div class='fl'></div>
    </body>
</html>

Try it!


A page with many divs of content, you see nine, but one is free for an ad, a game, or whatever entertaining element you can squeeze in there. Let's see what we need

Example 8.9. A Framework, Yeehah! nQuery.js
'use strict';
/**
 * nQuery, *the* JS Framework
 */
export const $ = function (foo) {
    return document.getElementById(foo);    // save keystrokes
}

An HTML5 page is basically HTML5 elements containing text. Therefore, in dynamic HTML5 we must be able to create and insert or update HTML5 elements as well as their content text nodes into the DOM

Example 8.10. index1.html
<!doctype html>
<html>
    <head>
        <title>What Is This?</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link href="styles.css" rel="stylesheet"/>
        <script src="nQuery.js"></script>
        <script src="thePage.js"></script>
        <script src="buildTttBoard.js"></script>
    </head>
    <body id='bod'>
    </body>
</html>

Try it!


Example 8.11. thePage.js

This code is an example of a page almost exclusively built by JavaScript. It might in some cases be a good choice, and in others not so. Use your commen sense and let the keyword be code readability, the programmer's viewpoint. The user's concern is what he experiences, not how it was created.

'use strict';
/**
 * createPage - Creates page content on load of minimal html
 */
let createPage = function() {
    let b = $('bod');                               // points to body
    let h1 = document.createElement('h1');
    let h1t = document.createTextNode('Some Page');
    h1.appendChild(h1t);
    h1.setAttribute('style', 'color: yellow');        // style one way
    b.appendChild(h1);
    let j = Math.floor(Math.random() * 9 + 1);
    for (let i = 0; i < 9; i++) {
        let d = document.createElement('div');
        d.style.border = '1px solid blue';          // style another way
        d.style.backgroundColor = 'silver'
        d.setAttribute('class', 'fl');              // ref to stylesheet
        if (i === j) {
            d.style.backgroundColor = 'yellow';
            d.setAttribute('id', 'ttt');
        }
        b.appendChild(d);
    }
    buildTttBoard();
}
window.addEventListener('load', createPage);

Example 8.12. buildTttBoard.js

This code exemplifies how we construct content for whatever real estate we have been given to fill in. The philosophy is creating it as plugin along the lines of "I give you an area of a page, fill it in. Ah yes, it's name is ttt.

This in my eyes means that you create some independent markup for this id. Independent means self contained, not relying on anything from the page, not CSS not HTML5 except the id.

'use strict';
/**
 * buildTttBoard - Creates TicTacToe Board
 */
let buildTttBoard = function() {
    const DIM = 3;
    let div = $('ttt');                               // points to id: ttt
    let t = document.createElement('table');
    t.style.border = '1px solid black';
    t.style.height = '96%';
    t.style.width = '96%';
    t.style.margin = '1% auto';
    for (let i = 0; i < DIM; i++) {
        let r = document.createElement('tr');
        for (let j = 0; j < DIM; j++) {
            let c = document.createElement('td');
            c.style.textAlign = 'center';
            c.style.verticalAlign = 'middle';
            c.style.fontWeight = '900';
            c.style.border = '1px solid black';
            c.style.height = '32%';
            c.style.width = '32%';
            c.addEventListener('click', function() {});
            r.appendChild(c);
        }
        t.appendChild(r);
    }
    div.appendChild(t);
}

In the latest example, you may have noticed an event listener suggested in every square on the TicTacToe board. Let us now focus on what the associated handler should do, and on how the code is structured. Take the structure first. Normally and event listener looks as follows:

let ehandler = function(x, y, z) {
    // &hellip;
}

document.getElementById('qpr').addEventListener('click', ehandler);

The last statement has no syntax that allows us to send arguments through to the handler. This means that the handler is oblivious as to the environment it was called from. It knows no variables/values, What to do? Well you use the coding style show in Example 8.12. You build the handler into the event listener thus creating closure with respect to the calling environment. In other words, the handler will know the variables of the buildTttBoard function, and their values. The latter is demonstrated here:

Example 8.13. buildTttBoard1a.js
'use strict';
/**
 * buildTttBoard - Creates TicTacToe Board
 */
let helper = function(id) {
    $(id).appendChild(document.createTextNode('x'));
}

let buildTttBoard = function() {
    const DIM = 3;
    let div = $('ttt');                               // points to id: ttt
    let t = document.createElement('table');
    t.style.border = '1px solid black';
    t.style.height = '96%';
    t.style.width = '96%';
    t.style.margin = '1% auto';
    for (let i = 0; i < DIM; i++) {
        let r = document.createElement('tr');
        for (let j = 0; j < DIM; j++) {
            let c = document.createElement('td');
            let did = 's' + i + j;
            c.setAttribute('id', did);
            c.style.textAlign = 'center';
            c.style.verticalAlign = 'middle';
            c.style.fontWeight = '900';
            c.style.fontSize = '40px';
            c.style.border = '1px solid black';
            c.style.height = '32%';
            c.style.width = '32%';
            c.addEventListener('click', function() {
                helper(did);    // closure, eventlistener knows did 
            });
            r.appendChild(c);
        }
        t.appendChild(r);
    }
    div.appendChild(t);
}

Try it!


The event object gives you a structure where you don't even need id's on the individual table cells.

Example 8.14. buildTttBoard1.js
'use strict';
/**
 * buildTttBoard - Creates TicTacToe Board
 */
let buildTttBoard = function() {
    const DIM = 3;
    let div = $('ttt');                               // points to id: ttt
    let t = document.createElement('table');
    t.style.border = '1px solid black';
    t.style.height = '96%';
    t.style.width = '96%';
    t.style.margin = '1% auto';
    for (let i = 0; i < DIM; i++) {
        let r = document.createElement('tr');
        for (let j = 0; j < DIM; j++) {
            let c = document.createElement('td');
            c.style.textAlign = 'center';
            c.style.verticalAlign = 'middle';
            c.style.fontWeight = '900';
            c.style.fontSize = '40px';
            c.style.border = '1px solid black';
            c.style.height = '32%';
            c.style.width = '32%';
            c.addEventListener('click', function(e) {
                e.target.appendChild(document.createTextNode('X'));
            });
            r.appendChild(c);
        }
        t.appendChild(r);
    }
    div.appendChild(t);
}

Try it!


Example 8.15. Best Practice, IMHO. buildTttBoard2.js

With a final refinement to avoid more than on X in a table cell.

'use strict';
/**
 * buildTttBoard - Creates TicTacToe Board
 */
let buildTttBoard = function() {
    const DIM = 3;
    let div = $('ttt');                               // points to id: ttt
    let t = document.createElement('table');
    t.style.border = '1px solid black';
    t.style.height = '96%';
    t.style.width = '96%';
    t.style.margin = '1% auto';
    for (let i = 0; i < DIM; i++) {
        let r = document.createElement('tr');
        for (let j = 0; j < DIM; j++) {
            let c = document.createElement('td');
            c.style.textAlign = 'center';
            c.style.verticalAlign = 'middle';
            c.style.fontWeight = '900';
            c.style.fontSize = '40px';
            c.style.border = '1px solid black';
            c.style.height = '32%';
            c.style.width = '32%';
            c.addEventListener('click', function eh(e) {
                e.target.appendChild(document.createTextNode('X'));
                e.target.removeEventListener('click', eh);
            });
            r.appendChild(c);
        }
        t.appendChild(r);
    }
    div.appendChild(t);
}

Try it!