• Official jQuery Templating Plugin

    Oct 11 2010

    jQuery announced that they are now officially supporting jQuery Templates, thanks in big part to Microsoft.

    Templating cleans up the job of generating HTML with jQuery. It also gives you the opportunity of keeping HTML code out of your JavaScript completely, if you wish.

    Let's say you have a block of data like this:

    var fruits = [
        { name: 'apples', color: 'green' },
        { name: 'oranges', color: 'orange' },
        { name: 'bananas', color: 'yellow' },
        { name: 'tomatoes', color: 'red' }
    ];
    

    You want to display the data in a nice table with some color effects:

    namecolor
    apples green
    oranges orange
    bananas yellow
    tomatoes red

    Without templates, your code might look like this:

    // create a table
    var $table = $('<table class="fruit-table">');
    
    // append a header to the table
    $('<tr><th>name</th><th>color</th></tr>').appendTo( $table );
    
    // append a row for each fruit
    for (var i in fruits) {
        // create a row, and set the background to the color of the fruit
        var $row = $('<tr/>', { css: { background: fruits[i].color } });
    
        // create a column and append to the row
        // we use text here so all HTML is escaped, to prevent hacking
        $('<td/>', { text: fruits[i].name }).appendTo( $row );
    
        // do the same for the color
        $('<td/>', { text: fruits[i].color }).appendTo( $row );
    
        // append the row to the table
        $row.appendTo( $table );
    }
    
    // all done, stick the table on the page
    $table.appendTo('body');
    

    Unfortunately, code like this usually ends up looking kludgy, and it's often hard to visualize what the final HTML will look like.

    Templates let us turn the HTML/JavaScript relationship inside-out by putting the looping right in the HTML:

    // define a template for the fruit table
    // we'll use slashes at the end of each line to escape the line break
    // this way we don't have to concatenate strings.
    $.template('fruit-table', '\
        <table class="fruit-table"> \
            <tr><th>name</th><th>color</th></tr> \
            {{each rows}} \
                <tr style="background: ${color}"> \
                    <td>${name}</td> \
                    <td>${color}</td> \
                </tr> \
            {{/each}} \
        </table> \
    ');
    
    // instantiate the template with the fruit array passed in as 'rows'
    var $table = $.tmpl('fruit-table', { rows: fruits });
    
    // that's it. stick it on the page.
    $table.appendTo('body');
    

    Now there's no question what the HTML will look like. It's in plain view.

    If you'd like to jump on the chance to take the HTML out of your JavaScript completely, you can stick the same block on the page, in a special <script> block:

    <script id="fruit-table" type="text/x-jquery-tmpl">
    
        <table class="fruit-table">
            <tr><th>name</th><th>color</th></tr>
            {{each rows}}
                <tr style="background: ${color}">
                    <td>${name}</td>
                    <td>${color}</td>
                </tr>
            {{/each}}
        </table>
    
    </script>
    

    Now we're down to a single line of code. Beautiful, isn't it?

    $('#fruit-table').tmpl({ rows: fruits }).appendTo('body');
    

    Now that we've gotten that taken care of, let's take it to the next level, and make a template that will dump any tabular data we give it:

    <script id="table" type="text/x-jquery-tmpl">
    
    {{if !data || data.length == 0 }}
        <p>No data.</p>
    {{else}}
        <table class="${className}">
            <tr>
                {{each(key) data[0]}}
                   <th>${key}</th>
                {{/each}}
            </tr>
            {{each(i, row) data}}
                <tr>
                    {{each(key, value) row}}
                        <td>${value}</td>
                    {{/each}}
                </tr>
            {{/each}}
        </table>
    {{/if}}
    
    </script>
    

    $('#table').tmpl({
        data: fruits,
        className: 'fruits-table'
    }).appendTo('body');
    

    Want more? Check out the documentation and official announcements for lots more information:

  • jQuery Live Events

    Feb 16 2009

    jQuery 1.3 came out on January 14th, jQuery 1.3.1 on the 21st, and with them we now have live events built into jQuery.

    Live events are pretty magical at first glance. They allow you to set events only once, and they work forever in the future, even as you're creating new elements and adding them to the page.

    Normally if you ran:

    $('a.wizard').click(function(){
        // do some wizardry
    });
    

    and then later you added wanted to add some more <a class="wizard">s to the page, you would have to re-attach this event handler over and over.

    Live events allow you to add an event that will work forever. This means you only have to add each type of event once. You would only have to write:

    $('a.wizard').live('click', function(){
        // do some wizardry
    });
    

    And your wizard links will work forever, even after you add 100 new wizard links to the page dynamically.

    This magic trick works by attaching the click event to the document. Whenever you click anywhere on the page, the document click event gets called. jQuery compares the target element to your wizard links and triggers your click event if the click came from inside the link.

    You can also do this yourself using the new closest() function. It allows you to do something like this:

    // listen for clicks on the document
    $(document).click(function(e){
        // look for a possible parent element matching a.wizard
        $(e.target).closest('a.wizard').each(function(){
            // wizard it up
        });
    });
    

    These live events can really help with performance. If you're attaching events to 100s of similar elements, like photos in an album, you can also save a lot of memory and speed things up by using live events, or using the example above and checking for events on a common parent element, either document or any element.

    If you're used to using closures to use data within click handlers, you will find they won't work anymore which is probably a good thing. Instead, you can use data() to store any amount of data with that element and get it out later:

    // maybe this is some JSON data you got using Ajax or something
    var wizards = [
       { name: 'Merlin', skill: 'magic' },
       { name: 'Mr. Wizard', skill: 'science' }
    ];
    
    $.each(wizards, function(i, wizard){
        // create a new link, change the text, add the data and append to the body
        $('<a class="wizard"/>')
            .text(wizard.name)
            .data('wizard', wizard)
            .appendTo(document.body);
    });
    

    then you only need to attach the click handler once:

    $(document).ready(function(){
    
        $('a.wizard').live('click', function(){
            // fetch the data back out
            var wizard = $(this).data('wizard');
    
            // get the stuff you need out of the object
            var name = wizard.name;
            var skill = wizard.skill;
    
            // do your thing
            alert(
                "Hello my name is "
                + name
                + "and I'm better than you at "
                + skill
                + "!!!"
            );
        });
    
    });
    

    And you can actually do these things in reverse, the order doesn't matter because of the magic and universality of live events.

    Pretty cool, eh? This way of developing has always been possible using JavaScript, but after learning about this with jQuery 1.3, it changed the way I look at programming with data and events.

    What do you think? Any questions, corrections or suggestions? Leave a comment.

  • buttons need type="submit" to submit in IE

    Dec 26 2008

    In a typical round of doing Internet Explorer clean up at the end of a project, I had to figure out why my <button>Submit</button> wasn't submitting a form in IE.

    I did a search on "html button" and went to the w3c HTML 4.01 specifications:

    type = submit|button|reset [CI]

    This attribute declares the type of the button. Possible values:

    submit: Creates a submit button. This is the default value.
    reset: Creates a reset button.
    button: Creates a push button.

    So the default is submit. But Internet Explorer has obviously forgotten this in IE6 and IE7. I found it worked without type="submit" in Firefox, Safari, Chrome and Opera. I haven't tested in IE8 because I don't have it installed. Maybe someone wants to check it out? Here is a demo page.

    So I guess we should get in the habit of using:

    <button type="submit">Submit</button>
  • Use Arrays in HTML Form Variables

    Nov 3 2008

    When you're dealing with processing forms, a lot of the time you have a one-to-one mapping of form fields and database columns, with perhaps an extra submit button or some fields that need special processing (eg. passwords).

    Although many frameworks like CodeIgniter make this easier, you can still easily come up with code like this:

    $this->db->insert('accounts', array(
        'first_name' => $this->input->post('first_name'),
        'last_name' => $this->input->post('last_name'),
        'email' => $this->input->post('email'),
        'address1' => $this->input->post('address1'),
        'address2' => $this->input->post('address2'),
        'city' => $this->input->post('city'),
        'state' => $this->input->post('state'),
        'zip' => $this->input->post('zip'),
        'phone' => $this->input->post('phone'),
        'fax' => $this->input->post('fax')
    ));
    

    See all that repetition? Whenever you see a group of lines that look almost the same, you know there is probably an opportunity to clean things up. Well luckily, there's a really neat one.

    You can create arrays in form fields using square brackets. That means you can name fields like account[first_name] and account[last_name] and in most server-side languages, you will end up with an 'account' array.

    In HTML, PHP and CodeIgniter, that might look something like this:

    <input type="text" name="account[first_name]"/>
    <input type="text" name="account[last_name]"/>
    <!-- etc... -->
    <input type="text" name="account[fax]"/>
    <input type="submit" name="submit"/> <!-- note the lack of 'account' -->
    
    // meanwhile, on the server...
    $account = $this->input->post('account');
    
    // VERY IMPORTANT: unset any fields that shouldn't be edited
    unset($account['id']);
    
    $this->db->insert('accounts', $account);
    

    See? Much cleaner. Yes, you do open a slight security hole potential, so be very careful when doing this on tables that have security implications, ie. user data. If you have an 'admin' column on your 'users' table, someone could maliciously add <input type="hidden" name="users[admin]" value="1"/> to your form using Firebug and grant themselves administration access. You can solve this by adding something like unset($user['admin']); to the incoming data. If you wanted to be really safe, you could have an array of the keys you want to allow and filter against that using something like PHP's array_intersect_key with array_flip:

    $user = $this->input->post('user');
    
    // only allow keys in $user to match the values in $columns
    $columns = array('first_name', 'last_name', /* etc.. */ );
    $user = array_intersect_key($user, array_flip($columns)));
    

    You can even do this with checkboxes and multiple select boxes, and alternatively leave out the key in the array to create a regular array (ie. not associative) of values:

    <label><input type="checkbox" name="choice[]" value="1"/> 1</label>
    <label><input type="checkbox" name="choice[]" value="2"/> 2</label>
    <!-- etc... -->
    
    // meanwhile, on the server...
    $choice = $this->input->post('choice');
    
    print_r($choice); // Array ( [0] => 1 [1] => 2 )
    

    This "trick" can really clean your code up. I used it recently to drastically simplify a form that had a shipping address and billing address. If the user checked a box saying "My shipping and billing are the same", I was able to simply do something like this:

    $shipping = $_POST['shipping'];
    $shipping_billing_same = $_POST['shipping_billing_same'];
    
    if ($shipping_billing_same) {
        $billing = $shipping;
    } else {
        $billing = $_POST['billing'];
    }
    

    Much nicer than that 24 form fields that were hard-coded previously.

    I've given examples here in PHP and CodeIgniter. Does anyone else want to give examples in other server-side languages and frameworks?

  • 5 Reasons Freelancers Can Succeed in a Shrinking Economy

    Oct 24 2008

    Like many people, I've been a bit obsessed about the economy lately. I'm wasn't sure whether or not to be scared, and to be honest I still don't. But I did think of some reasons freelancing might be a safe place to be.

    1. You have a diversified clientele. Your clients can be anywhere in the world, and you have many of them. And if you're lucky, you have more than enough work to fill your time. No matter how bad the economy gets, there should still be some business available.
    2. You have very little overhead. For most freelancers, all you need to work is a laptop and the Internet, both of which you'd probably have even if you weren't freelancing. You probably pay $10/month for your web site hosting, but otherwise you don't have a reason to borrow money. The gears of the credit crunch don't touch your business.
    3. You can drop your prices whenever you want. If you find less people can afford what you're charging, you'll be able to adjust accordingly. If you have a job and they decide they can't afford you, then you get laid off.
    4. The Internet is the place to be for self-employed professionals. With the considerably low cost of having the Internet, I would assume that a lot more business will happen online as more people become self-employed and work from home. The number of people you can potentially connect with online feels infinite.
    5. Businesses who don't want employees might turn to freelancers. Who knows, maybe in uncertain times, more business will want to hire freelancers on a contract basis rather than dedicate to hiring an employee they don't know if they'll be able to keep.

    For those of you who don't freelance, I'm not suggesting you quit your job and start freelancing tomorrow. You'll have to decide that for yourself. If you want to start freelancing, I would suggest is to get the ball rolling on the side while you have a job. Get that website up and get some presence on the Internet. That way you'll have something to fall back on if you lose your job.

    So things don't look too bad for us freelancers. How about you? Have you noticed the shrinking economy having a direct effect on your business or job? What do you think we can expect?

<< older posts newer posts >>