Skip to main content

Ivan Teoh

Something personal yet public

Javascript: 101 Week 5 Track 2

On week 5 track 2, we continue study chapter 12 and 13 on Eloquent Javascript. Both chapters are related to document-object model and browser events.

Reflection

  1. What factors make accessing nodes according to their structural order (by using the 'firstChild' and 'lastChild' properties, for example) an inefficient and potentially unreliable approach? What alternatives are available? We have been accessing nodes by going through a series of firstChild and lastChild properties. This can work, but it is verbose and easy to break ― if we add another node at the start of our document, document.body.firstChild no longer refers to the h1 element, and code which assumes it does will go wrong. On top of that, some browsers will add text-nodes for things like spaces and newlines between tags, while others do not, so that the exact layout of the DOM tree can vary. An alternative to this is to give elements that you need to have access to an id attribute. We can get the element by calling getElementById function.
  2. How does CSS play a role in JavaScript's interactive possibilities with the DOM? What are a few of the techniques related to this? Closely tied to HTML and the document-object model is the topic of style-sheets. It is a big topic, and I will not discuss it entirely, but some understanding of style-sheets is necessary for a lot of interesting JavaScript techniques, so we will go over the basics. In old-fashioned HTML, the only way to change the appearance of elements in a document was to give them extra attributes or to wrap them in extra tags, such as center to center them horizontally, or font to change the font style or colour. Most of the time, this meant that if you wanted the paragraphs or the tables in your document to look a certain way, you had to add a bunch of attributes and tags to every single one of them. This quickly adds a lot of noise to such documents, and makes them very painful to write or change by hand. Style-sheets are a way to make statements like 'in this document, all paragraphs use the Comic Sans font, and are purple, and all tables have a thick green border'. You specify them once, at the top of the document or in a separate file, and they affect the whole document. Classes are a concept related to styles. If you have different kinds of paragraphs, ugly ones and nice ones for example, setting the style for all p elements is not what you want, so classes can be used to distinguish between them. And this is also the meaning of the className property which was briefly mentioned for the setNodeAttribute function. The style attribute can be used to add a piece of style directly to an element. There is much more to styles: Some styles are inherited by child nodes from parent nodes, and interfere with each other in complex and interesting ways, but for the purpose of DOM programming, the most important thing to know is that each DOM node has a style property, which can be used to manipulate the style of that node, and that there are a few kinds of styles that can be used to make nodes do extraordinary things.
  3. Identify some of the browser events JavaScript is capable of interpreting. What might the practical applications be? There are many browser events that JavaScript is capable of interpreting. First one that we can think of straight away is button click. This does in fact work across browsers, but it has an important drawback ― you can only attach one handler to an element. Most of the time, one is enough, but there are cases, especially when a program has to be able to work together with other programs (which might also be adding handlers), that this is annoying.

Homework

  • Write a function asHTML which, when given a DOM node, produces a string representing the HTML text for that node and its children. You may ignore attributes, just show nodes as <nodename>. The escapeHTML function from chapter 10 is available to properly escape the content of text nodes. Hint: Recursion!

    174-homework1.html (Source)

    <!DOCTYPE HTML>
    <html lang="en">
    <head>
        <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
        <title>JavaScript 101</title>
        <script type="text/javascript">
            function load()
            {
    
                function forEach(array, action) {
                    var i;
                    for (i = 0; i < array.length; i++) {
                        action(array[i]);
                    }
                }
    
                function escapeHTML(text) {
                    var replacements = [["&", "&amp;"], ["\"", "&quot;"],
                                       ["<", "&lt;"], [">", "&gt;"]];
                    forEach(replacements, function(replace) {
                        while (text.indexOf(replace[0]) != -1) {
                            text = text.replace(replace[0], replace[1]);
                        }
                    });
                    return text;
                }
    
                function isTextNode(node) {
                    return node.nodeType == 3;
                }
    
                function map(func, array) {
                    var result = [];
                    forEach(array, function (element) {
                        result.push(func(element));
                    });
                    return result;
                }
                var first = true;
                function asHTML(node) {
                    if (first) {
                        node.innerHTML = node.innerHTML.replace(
                          /\B\s\B|[\n\r\t]/g,'');
                        first = false;
                    }
    
                    if (isTextNode(node))
                        return escapeHTML(node.nodeValue);
                    else if (node.childNodes.length == 0)
                        return "<" + node.nodeName + "/>";
                    else
                        return "<" + node.nodeName + ">" +
                            map(asHTML, node.childNodes).join("") +
                            "</" + node.nodeName + ">";
                }
    
                console.log(asHTML(document.body));
    
            }
    
        </script>
    </head>
    <body onload="load()">
        <h1>This is a header!</h1>
        <p id="excitingText">
            This is a paragraph! <em>Excitement</em>!
        </p>
        <p>
            This is also a paragraph, but it's not nearly as exciting as the
            last one.
        </p>
    </body>
    </html>
    
  • Write the convenient function removeElement which removes the DOM node it is given as an argument from its parent node.

    174-homework2.html (Source)

    <!DOCTYPE HTML>
    <html lang="en">
    <head>
        <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
        <title>JavaScript 101</title>
        <script type="text/javascript">
            function load()
            {
    
                function removeElement(node) {
                    parent = node.parentNode;
                    if (parent) {
                        parent.removeChild(node);
                    }
                }
                console.log(removeElement(document.body.childNodes[1]));
    
            }
        </script>
    </head>
    <body onload="load()">
        <h1>This is a header!</h1>
        <p id="excitingText">
            This is a paragraph! <em>Excitement</em>!
        </p>
        <p>
            This is also a paragraph, but it's not nearly as exciting as the
            last one.
        </p>
    </body>
    </html>
    
  • Write a function called registerEventHandler to wrap the incompatibilities of these two models. It takes three arguments: first a DOM node that the handler should be attached to, then the name of the event type, such as "click" or "keypress", and finally the handler function. To determine which method should be called, look for the methods themselves ― if the DOM node has a method called attachEvent, you may assume that this is the correct method. Note that this is much preferable to directly checking whether the browser is Internet Explorer. If a new browser arrives which uses Internet Explorer's model, or Internet Explorer suddenly switches to the standard model, the code will still work. Both are rather unlikely, of course, but doing something in a smart way never hurts.

    174-homework3.js (Source)

    function registerEventHandler(node, eventType, handler) {
        if (typeof node.attachEvent === 'function') {
            node.attachEvent("on" + eventType, handler);
        } else {
            node.addEventListener(eventType, handler, false);
        }
    }
    
  • Create an HTML page and some Javascript to allow a user to add n numbers. First display a simple form with the question "How many numbers do you want to add (max is 10)". The user should enter a number between 2 to 10 and click on a button in the form. You have to validate the answer. If the user has entered a correct value (between 2 and 10), then dynamically create a form with n text input fields and an "Add" button. Once the form is displayed the user will enter n numbers in the n input fields and when they click on the "Add" button, dynamically create a span element with the result. You will have to perform validation on the values entered in the input fields to make sure that they are numbers. If they are not numbers, display an alert dialogue with an error message.

    174-homework4.html (Source)

    <!DOCTYPE HTML>
    <html lang="en">
    <head>
        <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
        <title>JavaScript 101</title>
        <script type="text/javascript">
    
        function createFieldset(id, parent){
            var element = document.createElement('fieldset');
            element.setAttribute('id', id);
            parent.appendChild(element);
            return element;
        }
    
        function createInput(id, parent){
            var element = document.createElement('input');
            element.setAttribute('id', id);
            element.setAttribute('type', 'text');
            parent.appendChild(element);
            return element;
        }
    
        function createButton(id, parent, name, funcName) {
            var element = document.createElement('input');
            element.setAttribute('id', id);
            element.setAttribute('type', 'button');
            element.setAttribute('value', name);
            element.setAttribute('onclick', funcName);
            parent.appendChild(element);
            return element;
        }
    
        function createSpan(id, parent, text){
            var element = document.createElement('span');
            element.setAttribute('id', id);
            var child = document.createTextNode(text);
            element.appendChild(child);
            parent.appendChild(element);
            return element;
        }
    
        function adding(){
            var total = parseInt(document.getElementById('total').value);
            if (total && total < 11 && total > 1) {
                var grandTotal = 0;
                for (var i = 0; i < total; i++) {
                        var number = document.getElementById('input' + i);
                        if (!number) {
                            continue;
                        }
                        var numberValue = parseInt(number.value);
                        if (numberValue) {
                            grandTotal += numberValue;
                        } else {
                            alert('"' + number.value + '" is not an integer');
                        }
                }
                var result = createFieldset('result', document.body);
                createSpan('span', result, grandTotal);
            } else {
                alert('Have you changed the total?');
            }
        }
    
        function submit(){
            var total = parseInt(document.getElementById('total').value);
            if (total && total < 11 && total > 1) {
                var fieldset = createFieldset('fieldsetlists', document.body);
                for (var i = 0; i < total; i++) {
                    createInput('input' + i, fieldset);
                }
                createButton('addButton', fieldset, 'add', 'adding();');
            } else {
                alert('Please enter a number between 2 to 10!');
            }
        }
        </script>
    </head>
    <body>
    <fieldset>
        <p>How many numbers do you want to add (max is 10)</p>
        <input type='text' id='total' />
        <input type='button' value='submit' onclick='submit();'/>
    </fieldset>
    </body>
    </html>
    

Comments

Comments powered by Disqus