Blog of Jeff A blog about programming and random other things.

22Nov/09link

“Brief” Guide to JavaScript – Part 11

You should check out the previous parts before continuing with this final part:

Ajax

It's part of the "Web 2.0" trend. Ajax, or Asynchronous JavaScript and XML provides a background-styled http request to a URL. This is different from the traditional, "load a new page to perform an action" style. With the ability to essentially load a page in the background, you can let the user interact with the rest of your site while performing an operation by the user.


Pros & Cons

"With great power, comes great responsibility."

Ajax provides some very nice benefits, but there are some issues you should consider before determining to use ajax. A lighter-weight response from the server (instead of a full page reload) which can improve the user experience with ajax. But keep in mind:

  • The user's back button won't "undo" the last action. This has to be implemented manually.
  • Browsers without JavaScript or Ajax can't perform these actions.
  • Search engines & bots usually don't run JavaScript, so content provided via ajax is inaccessible to them.
  • Ajax doesn't provide an accessible way for blind people to figure out changes on a page (if any).

Providing a graceful alternative, the traditional page-reload mechanisms, is a good way to combat most of the issues above. Although, it's a good practice to avoid using Ajax for simply displaying content.

XMLHttpRequest

The core behind Ajax is the XMLHttpRequest object in JavaScript. XMLHttpRequest is a functional object that needs to be instantiated, so we do this:

 


var request;
try{
    if(XMLHttpRequest) // for all modern browsers
        request = new XMLHttpRequest();
    else // for IE prior to 8
        request = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(error){
    request = false;
}
if(request){
    // do ajax stuff here...
} else {
    // browser doesn't support ajax
}

It's worth noting the both the ActiveXObject & XMLHttpRequest object have the possibility of throwing an exception, so use a try/catch block and handle if the feature is not supported.

Next, we need to configure the request object, the basics:


request.open('get', 'http://google.com', true);

open interface is as follows:


open(method, url, async?, username, password)
  • method: usually a string of get or post which describes how additional data should be sent. Identical to the method attribute of form tags.
  • url: the url to fetch. Identical to the action attribute of form tags.
  • async?: You should usually set this to true. This tells the browser to perform the request asynchronously and not freeze up the browser. The advantage of synchronous is after you perform the request, you can immediately use the results in your next line of code. This cannot be guaranteed with an asynchronous request. (optional - defaults true)
  • username & password: is the simple http-supported username & password. This does not work for sites that have a username & password login form, but rather for urls that prompt a login dialog before displaying the page. (optional - defaults to no login credentials)

Of course, doing an async request is useless if we can't handle it when the request is completed, so we add an event handler.


request.onreadystatechanged = function(){
    // our code goes here.
};

There's a handful of different cases when this handler will be called. In order to differentiate between the requests, the XMLHttpRequest object has a readyState property which we can use to check the current state the request is in. The values are:

  • 0: The xmlhttp object hasn't been initialized
  • 1: Xmlhttp object has been initialized
  • 2: Xmlhttp object has sent request
  • 3: Xmlhttp object is being processed (performing request)
  • 4: Xmlhttp object is completed (the request is completed)

Most of the time, you'll be checking to see the readyState property is 4.

Also worth mentioning the status property, for the XMLHttpRequest object, which returns the server response code from the request: 200 for successful request, 404 for missing file, etc.

Finally, there's the responseText property which contains the content of the server request.

Now let's build our onreadystate function body:

 


request.onreadystatechanged = function(){
    // ajax request finished & server returned 200
    // (url resource found and returned)
    // it's probably a better idea to accept any
    // status in the 200's, but for simplicity, we'll
    // do this
    if(request.readyState == 4 && request.status == 200){
        var c = document.getElementById('ajax_content');
        c.innerHTML = request.responseText;
    }
};

Here we set the interior html of the element with id attribute of ajax_content to whatever the server gave us. After we configured our object, we need to perform the request:


// string contains POST data to send to the server. null indicates no POST data.
request.send(null);

It's worth noting the POST data string should be in a query-string format:


request.send('var1=value1&var2=value2');

That's it, JavaScript ajax in a nutshell. No fancy voodoo magic.

XML / HTML / Text data

Just pinging a URL is definitely not useful, we want to handle the data from the user. There are several typical server responses, but usually you have control over this since ajax is used to improve the user experience of your web app.

For text we simply utilize the responseText property:


request.onreadystatechanged = function(){
    if(request.readyState == 4 && request.status == 200){
        // request.responseText
    }
};

This behavior is similar for XML, except when you need to parse them for specific information, then responseXML will be your friend then.

 


request.onreadystatechanged = function(){
    if(request.readyState == 4 && request.status == 200){
        // request.responseXML is like a DOM object, supporting DOM
        // traversal techniques.
        alert(request.responseXML.getElementsByTagName(‘h1’).length);
    }
}

Alternatively, you can manually create the XML parser object:


request.onreadystatechanged = function(){
    if(request.readyState == 4 && request.status == 200){
        var contents = request.responseText;
        var xml = null;
        if(window.DOMParser){ // all browsers but IE
            var parser = new DOMParser();
            xml = parser.parseFromString(contents, ‘text/xml’);
        } else { // internet explorer
            xml = new ActiveXObject(‘Microsoft.XMLDOM’);
            xml.async=’false’;
            xml.loadXML(contents);
        }
        // use xml identically to request.responseXML
    }
}

JSON

Another common data-transfer format is JSON (JavaScript Object Notation), which is a subset of JavaScript syntax – specifically the data structures part. The only accepted types of JSON are:

  • Numbers – 1, 2.5, –4, –5.2, etc.
  • Booleans – true, false
  • Null value
  • Strings - ‘abc’, “defghi”
  • Arrays – [‘a’, 1, 2]
  • Objects – {‘key’ : ‘value’} where keys must be strings!

The main advantage is the simple ways to use JSON as true JavaScript values. Given that json_string contains json data – whether from ajax or somewhere else – we can simply do:


var json = eval(‘(‘ + json_string + ‘)’);

To convert it to it’s equivalent data structure in JavaScript. Using eval is not safe parsing unsafe JSON code (eg – from a third party), use the JSON parser at json.org. This is simply because eval interprets a given string as JavaScript: including function calls and prototype definitions.

The json.org parser is also bundled with a stringify function to convert JavaScript data structures into JSON.

The End

(no really)

I’m wrapping up my exhausting JavaScript coverage here. But there’s plenty more things to learn if you’re up to it:

  • Debugging your JavaScript in IE, Safari, Firefox (with Firebug), etc.
  • Gracefully degrading when the user doesn’t have JavaScript or certain JavaScript features
  • JavaScript optimization techniques
  • XPath for DOM traversal
  • AJAX
  • Supporting the back button with AJAX actions
  • Modifying/Reading HTTP Headers
  • JavaScript Frameworks (Prototype/Scriptaculous, Mootools, YUI, jQuery, dojo, ext, etc.)
  • With that, I’ll tilt my hat, and walk off. If only I had a cool hat.

    - Jeff

    • Share/Bookmark
    26Oct/09link

    “Brief” Guide to JavaScript – Part 10

    Yes, this is no longer becoming brief; so check out the growing list of previous parts:

    Objects Revisited

    Objects are the core of JavaScript, so I'm going to a bit further in-depth than previously mentioned. I didn't say explicitly, but everything is JavaScript is an object, booleans, strings, numbers, arrays, functions, etc. are all objects. Before we talk further, I want to quickly review some terminology of objects:

    
    
    myobj = {}; // constructor that creates an object;             // In this case, an empty object.
    
    myobj.property; // accessing a property
    
    myobj.method(); // calling a function which belongs to an object, aka a method.
    
    // we can also access properties, methods, and references using the// array-styled notation
    
    myobj['property']; // 'property' is called a key
    
    myobj['method'](); // 'method' is the key
    
    myobj['anotherobj']; // 'anotherobj' is the key
    
    
    

    References

    In contrast to what I initially explain, variables store references to objects. This is slightly different than simply storing data. I feel diagrams explain this the best. Say we do something like this:

    
    
    var myobj = {'hello': 'world'};
    
    var myobj2 = myobj;

    JavaScript engines store the data similar to this:

    This means if we change our object, the changes are visible through other variables:

    
    
    myobj.hello = 'jk';
    
    alert(myobj2.hello); // displays 'jk'

    To truly copy an object, we have to manually copy every property:

    
    
    var myobj2 = {};
    
    for(var p in myobj){
    
        myobj[p] = myobj[p];
    
    }

    This assumes that all the properties don't contain their own properties (like an array). Changing the property or variable changes where that property/variable points to. So:

    
    
    var myvar = true;
    
    var myvar2 = myobj;
    
    myvar = false;

    Which produces something like this:

    In simplest terms, you can change where the variable points to, but setting one equal to another will share the same data source. This behavior also apply to function parameters. The rule of thumb is: for objects (those you use {}), arrays, and anything created with a new keyword behave as references.

    Reflection Basics

    Reflection is a programming terminology which a program that is aware of itself (not in the skynet terms): knowing the existence of variables, functions, or properties. This is particularly easy in JavaScript due to array-styled notation:

    
    
    var css = ['borderColor', 'backgroundColor'];
    
    var el = document.getElementById('header');
    
    for(var style in css){
    
        el.styles[style] = 'transparent'; // remove colors.
    
    }

    A bit more practical example is when creating similar functions:

    
    
    var names = {
    
        'Height': 'height',
    
        'Width': 'width',
    
        'Bgcolor': 'backgroundColor',
    
        'Color': 'color',
    
        'Size': 'fontSize'
    
    };
    
    
    
    for(var fnName in names){
    
        (function(n){
    
        window['set'+n] = function(element, value){
    
            element.styles[names[n]] = value;
    
        };
    
        })(fnName);
    
    }
    
    // test it
    
    setColor(document.getElementById('header'), 'red');

    As you can see, we can use variables outside our anonymous functions, but you have to beware when the variable is updated in a loop. This is because the function will use the value of the variable at the time of the function call. So after the for loop finished, fnName will be 'Size' and all our functions would be change the 'fontSize' element.

    To quickly resolve this, we simply create an anonymous function that accepts the fnName variable. With function calls, non-referenced variables are duplicated (remember: arrays, objects, or things using new are references), so we can use that variable when it was during the function declaration, inside the loop.

    This outer scoping of variables is called dynamic scoping, where the values are based on when you call the functions, not when the function was defined.

    Classes

    Once you start writing larger and larger JavaScript-based applications, you'll may opt to use a more Object-Oriented approach. Classes help provide a "blackbox" approach to stateful widgets/systems. I'm not going to write a lengthy discussing the pros & cons, arguing isn’t my cup of tea ;)

    If you haven't used classes much, I'll quickly review some terminology:

    • Property: A variable "about” the object. Think of a characteristic/trait of a particular object: like the box’s color – color would be the property. Identical to properties in object definitions.
    • Method: A name for functions that belongs to a class. Other words, properties that are functions.
    • Class: A "blueprint." Classes define properties and methods. For example: a class would be the architecture's design for a house.
    • Instance: A representation of the "blueprint". Instances can have different values for properties. Continuing our example: An instance would be an actual house. One instance could be blue (property color = blue) and another instance could be red.
    • Static: Treats the class almost like a namespace. Static properties and methods can be access directly. Unlike regular properties and methods, the static variants can't access the currently operating instance because they don't exist in an instance.

    Since JavaScript is a bit unusual for classes, which we'll cover shortly.

    Instantiated Classes

    So we need to define some classes before we create instances of them. JavaScript classes can be created via functions:

    
    
    function Person(){
    
    }

    Seems like a useless function definition, but this is our bare-minimal class definition! We can instantiate the almost useless class:

    var john = new Person;

    If you've used Object-Oriented programming in other languages, this is absolutely useless. But in JavaScript, properties can be created after the class has been instantiated.

    john.name = "John Doe"; // works.

    Why? Class instantiations are identical to creating objects. See these:

    
    
    var myobj = {};
    
    var myobj2 = new Object; // identical
    
    var myobj3 = new Person; // our class is the same

    Although you should use the former over the other alternatives. To make our class a bit better before, let's redefine our person class:

    
    
    function Person(name, phone){
    
        this.name = name;
    
        this.phone = phone;
    
    }
    
    var john = new Person('john doe', '123 456 7890');
    
    alert(john.name);
    
    alert(john.phone);

    Here we utilize our handy this variable which allows us to set properties and methods which we can access when we instantiate the class. The function parameters act as a constructor, which provides some values we can use when instantiating a class.

    Of course we can make it more complex; let's add a method:

    
    
    function Person(name, phone){
    
        this.name = name;
    
        this.phone = phone;
    
        this.shout = function(){
    
            alert('hello! My name is ' + name);
    
        }
    
    }
    
    var john = new Person('john doe', '123 456 7890');
    
    john.shout(); // alerts ‘hello! My name is john doe’

    Just like any JavaScript object, just easier to replicate using instances.

    Static Classes

    Unlike instances, static classes don't require instantiation. Static classes are basically single-instanced classes. The easiest way is to simply define them like namespaces (see previously). But if you want a class to have both static and dynamic properties, simply create the dynamic first:

    
    
    function Person(name, phone){
    
        // dynamic-class code
    
        this.name = name;
    
        this.phone = phone;
    
        Person.count++; // modifying our static property
    
    }
    
    Person.count = 0; // static property

    Why does this work? Because everything are objects in JavaScript, including functions.

    Prototypes

    If you haven't noticed, there's one problem with the whole instantiated thing see:

    
    
    function Person(){}
    
    var john = new Person;
    
    john.walkSpeed = 100;

    If we wanted all person instances to have the walkSpeed property, we would need to add that property to all instantiated person classes. Not cool. To resolve this, we can use the special property in all objects: prototype. This allows us to assign properties to all instances of our class.

    
    
    function Person(){}
    
    var john = new Person;
    
    Person.prototype.walkSpeed = 100;
    
    alert(john.walkSpeed); // prints 100
    
    var joe = new Person;
    
    alert(joe.walkSpeed); // prints 100
    
    john.walkSpeed = 10; // only affects john, poor fellow.

    Using prototypes, we can implement like traditional object oriented inheritance.

    
    
    function Person(){ }
    
    function Employee() {}
    
    Employee.prototype = new Person();

    If you are observant, you'll notice we have a restriction: the constructor has to have no parameters. Unfortunately, achieving good prototype-based inheritance with constructor-supported inheritance is a tougher problem (out of scope of this article). I'll direct you here for a do-it-yourself implementation or JavaScript frameworks: prototype & mootools are good OO frameworks.


    This wraps up this lengthy week. Next up is the trendy AJAX stuff. Oh boy!

    • Share/Bookmark
    11Oct/09link

    “Brief” Guide to JavaScript – Part 9

    Finally, another part. If you haven’t already, check out the previous parts of this series:

    Trying and Catching

    Tries & catches are identical to exceptions in other programming languages. If you're not familiar with exceptions & handling them, let's review now. In general purpose programming, errors (aka, exceptions) are thrown for unreliable operations: the network may drop, insufficient permissions, a file is missing, etc. The programming language will generate these exceptions for us, but we can also create our own exceptions if need be.

    When an exception is thrown, it halts the executing code and bubbles up out of code blocks (for loops, while loops, if statements, functions, etc.) until it reaches a try block. If caught by a try block, the subsequent catch block is executed. If no try block exists, the exception terminates the execution of the program/script entirely.

    In a nutshell, try/catch blocks allow us to handle code that may potentially fail. This is made more useful in many dynamic languages, where exceptions are thrown from almost any error: from stack overflows to accessing a non-existent variable.

    But in JavaScript, exceptions are less useful since most of JavaScript relies on generated content: the DOM tree and browser-provided functions. Nonetheless, some browser functions will throw exceptions if they fail. Raised exceptions that are not in a try/catch block are handled by the browser (usually by being display to the JavaScript debugger/console) and the script halts execution.

    Let's see some concrete examples:

    
    
    // result: this code will always show the alert (since we always throw an exception) 
    
    // but normally it isn't guaranteed that exceptions are thrown. 
    
    try {
    
        throw "Oh noes!";
    
    }
    
    catch(error){ // error contains "Oh noes!" 
    
        alert(error);
    
    }


    Here we simply throw our own exception in our try/catch block. Throw accepts any data to pass to a catch block. Unlike custom exceptions, language-based exceptions' data varies from browser to browser and shouldn't be relied upon.

    DOM Ready Event

    After using the load event you may have noticed that your JavaScript executes only after everything finishes loaded (which includes images, flash, ads, etc.). We may have a 200-megabyte image on the page (just pretend at least), the JavaScript won't run until that image is finished downloaded by the browser: Not ideal.

    We can solve this using the DOM Ready Event. But we should talk a bit about the aforementioned solution I put earlier: simply placing your script tags at the bottom.

    The browser creates the DOM tree as the HTML is parsed by the browser. This means any JavaScript code is executed as soon as it is reached by the browser, and giving access to the partially-completed DOM tree it has built. If we place our scripts at the bottom of the page, the entire DOM tree has been built before our scripts execute: which is perfect.

    Putting scripts at the bottom also provides a nice side-effect as mentioned on Yahoo!. They speed up loading of your pages and allows the browser to render the page as it parses it (think of how a stereotypical page loads on 56k). This is opposed to freezing other downloading files and providing a blank page until the entire page downloaded and completely parsed when you place script tags in your head.

    But sometimes it's useful the place script in head, they don't interfere with your DOM tree manipulations (most of the time) if they're in head as opposed to body. Also, since the page isn't displayed yet, you can hide an element (in conjunction with DOM Ready) before it's displayed. If you placed the script at the bottom, the user could be trying to interact with some elements you didn't want visible while the page is loaded since the browser didn't get to the scripts which hides the element.

    For DOM Ready, most browsers support the DOMContentLoaded event which works as intended. But the pesky IE needs an alternative solution, via readystatechange, which can report when the status of an object changes, like when it finishes loading.

    But the timing of readystatechange isn't reliable, it can also occur after load. A better method constantly try to use certain functions that IE only provides when the DOM tree is ready.

    And here's the monstrosity, commented for understandability.

    
    
    var isReady = false;
    
    // list of all functions that should be called when DOMReady is achieved.
    
    var readyFunctions = [];
    
    // this is called when the DOM is ready and all functions that
    
    // want to execute when  the DOM is ready (stored in
    
    // readyFunctions) are called. 
    
    function ready(){
    
        // this will be called multiple times during a load, but we only
    
        // want its effects once 
    
        if(!isReady){
    
            isReady = true;
    
            for(var i=0; i<readyFunctions.length; i++){
    
                readyFunctions[i]();
    
            }
    
        }
    
    }
    
    // attach our event listeners for the DOM Ready event
    
    if(document.addEventListener){
    
        // covers all cases (frames & non-frames) for FF2+, Safari 3+, Chrome,
    
        // Opera 9+ 
    
        document.addEventListener('DOMContentLoaded', function(){
    
            document.removeEventListener('DOMContentLoaded',
    
                                         arguments.callee, false);
    
            ready();
    
        }, false);
    
    } else if (document.attachEvent){ // good old internet explorer 
    
        // works well with frames for IE, but sometimes maybe called *after*
    
        // the load event. 
    
        document.attachEvent('onreadystatechange', function(){
    
            // check it's the right state we're looking for 
    
            if(document.readyState === 'complete'){
    
                document.detachEvent('onreadystatechange',
    
                                     arguments.callee);
    
                ready();
    
            }
    
        });
    
        // do this only if IE (by checking for element.doScroll) and
    
        // is not a frame 
    
        if(document.documentElement.doScroll && window == window.top){
    
            // this is a recursive anonymous function that
    
            // is executed immediately 
    
            (function(){
    
                // if load got called before we detected it, terminate
    
                if(isReady) return;
    
                try {
    
                    // try to use doScroll, which throws an exception
    
                    // if DOM tree isn't ready yet. 
    
                    document.documentElement.doScroll('left');
    
                } catch (error){
    
                    // catch the exception and try again (recursive
    
                    // call to anonymous function again) 
    
                    setTimeout(arguments.callee, 0);
    
                    // ensures we don't call ready() below when the DOM
    
                    // tree isn't really ready.
    
                    return;
    
                }
    
                ready(); // we didn't get an exception, fire ready function.
    
            })();
    
        }
    
    }
    
    // if all else fails. For simplicity, the onload property
    
    // is used instead of attachEvent/addEventListener. 
    
    window.onload = ready;
    
    
    

    Note: I'm basing my example code from code from jQuery, which also utilizes the IE trick documented here.

    Theoretically, you could tidy the code a bit better, but it's usable. This should be before all your regular code. To make some code execute immediately after the DOM tree is loaded simply added your functions to the readyFunctions array:

    
    
    readyFunctions.push(function(){
    
        // usually DOM manipulation or attaching events go here. 
    
        alert('dom is ready!');
    
    }); 

    In this case, the alert box will be displayed when the DOM tree is finished, before all the images and flash are loaded.

    Namespaces

    Let's assume we have two JavaScript developers: Jim and Sammy. Jim writes a function addItem, which handles adding a new item to the inventory. Sammy writes a function, also called addItem, which handles adding a new item to a customer's shopping cart. There's a conflict since a program won't know the difference between addItem that Jimmy wrote versus Sammy.

    Namespaces is a solution to this problem by essentially prefixing a set of variables & functions.

    In JavaScript, namespaces are simply global objects that contain your functions and variables.

    
    
    var hui = {
    
        alert_value: 'hello world!',
    
        createElement: function(){
    
            var div = document.createElement('div');
    
            document.body.appendChild(div);
    
            alert(hui.alert_value);
    
        }
    
    };
    
    // call through our namespace: 
    
    hui.createElement(); 

    But this can be annoying since we have to refer to the namespace whenever we want to use a function or variable. In addition, any inter-function variables are publicly visible. A better alternative is to wrap everything in an anonymous function:

    
    
    (function(){
    
    var alert_value = 'hello world!';
    
    function createElement(){
    
            var div = document.createElement('div');
    
            document.body.appendChild(div);
    
            alert(alert_value); // local variable 
    
    };
    
    window.hui = {
    
        alert_value: alert_value,
    
        createElement: createElement
    
    };
    
    })();
    
    // call through our namespace: 
    
    hui.createElement();

    Like any regular functions, the anonymous function here provides its own scope which hides any local variables. After we have defined our functions and variables, we can then create an object in the window object (the global namespace) with the exact functions and variables we want publicly available.

    While you may not use namespaces when writing for a specific page, this is definitely more useful when writing your own framework or portable/reusable code.


    Yes, I’ve been slacking (but coursework is a good excuse! Honest!) Besides this, I was thinking of migrating towards screencasts (screenr / jing) instead of just text-based tutorials. I feel those would be easier to churn out, in theory.

    Next up: Objects Revisited.

    • Share/Bookmark

    Recent Posts

    Topics

    Archives

    Following

    Links