Posts Tagged ‘DOM’

Replacing text in the DOM… it’s not that simple!

Posted in 'JavaScript' by James on July 7th, 2010

Finding and replacing strings of text in a DOM document is very tricky, especially if you’re looking to do it properly and entirely unobtrusively.

In this context, “unobtrusive” means affecting the page in a minimally invasive manner — minimal DOM destruction, no un-called-for normalizing of text nodes (i.e. joining separate text-nodes together) etc.

Essentially, what I’m talking about is finding a specific piece of text in a page or a piece of text that matches a pattern and then doing something with it — possibly wrapping it in an element or changing it in some other way.

Imagine that we have the following paragraph:

<p>This order's reference number is RF83297.</p>

We want to locate the reference number and wrap it in an anchor that leads to the order’s page (pretend that we’re making a bookmarklet to enhance the admin panel of some online store). Given what we know about the HTML structure this can be quite easy:

(Using jQuery)

jQuery('p').each(function(){
 
    var p = $(this);
 
    p.html(
        p.text().replace(
            /\bRF\d{5}/g,
            '<a href="/order/$&">$&</a>'
        )
    )
 
});

The problem with this approach is that it breaks once you add any elements to any of the paragraphs on the page. It sets the content of the the element to the (replaced) text of that very element, by definition destroying any non-text nodes in the process, or, to put it poetically, it flattens the DOM structure of any given paragraph.

Additionally, the above technique does not care to avoid paragraphs that do not contain a match for the pattern — it’ll blindly flatten all elements within the paragraphs…

Questioning non UI events

Posted in 'General, JavaScript' by James on April 29th, 2010

Without UI events we’d be totally lost on the client-side — it is only via User Interface Events that we can know what the user wants to do. User-initiated events such as “click”, “mouseover” and “mousemove” are absolutely essential to upholding a user-centred experience and I want to make it clear that my quarrel is not with these types of events. Any event that gives us information about users’ intentions is a good event.

I feel that there are bad events too though — events that we feel we must utilise just because they exist — and we feel we must build into our APIs just because event-driven design dictates absolute submission to this way of thinking.

DOM mutation events

Every single time I’ve ever considered using a DOM-mutation event I’ve subsequently discovered that the problem can be better solved by re-architecting the codebase. The DOM2 mutation events aren’t well supported in less capable browsers but offer an interesting opportunity — to listen for document mutation such as attribute changes, node insertion and node removal.

This sounds cool and I’m sure your mind was buzzing with ideas when you first heard about these events, but have you actually encountered a situation where utilising these events is the only viable solution? If so, I’d love to hear it.

Library foundation code

Posted in 'Code Snippets, JavaScript' by James on April 26th, 2010

It should now be universally accepted that extending the DOM directly (via Element.prototype, Node.prototype etc.) is a bad idea. To combat the inherent problems with doing this, quite a few libraries (jQuery, BBC Glow, etc.) employ the “wrapper” technique which involves taking a bunch of DOM elements and stuffing them into an array-like object that inherits from a safely-extendible prototype.

If you want to have a go at creating your own jQuery-like DOM library, here’s something to get you started:

window.myWrapper = (function(){
 
    // "cache" useful methods, so they don't
    // have to be resolved every time they're used
    var push = Array.prototype.push,
        slice = Array.prototype.slice,
        toString = Object.prototype.toString,
 
        isArray = function(o) {
            toString.call(o) === '[object Array]';
        },
 
        toArray = (function(){
 
            try {
 
                // Return a basic slice() if the environment
                // is okay with converting NodeLists to
                // arrays using slice()
 
                slice.call(document.childNodes);
 
                return function(arrayLike) {
                    return slice.call(arrayLike);
                };
 
            } catch(e) {}
 
            // Otherwise return the slower approach
 
            return function(arrayLike) {
 
                var ret = [], i = -1, len = arrayLike.length;
 
                while (++i < len) {
                    ret[i] = arrayLike[i];
                }
 
                return ret;
 
            };
 
        })();
 
    function NodeList(elems) {
 
        this.length = 0;
 
        push.apply(
            this,
            elems.nodeType ?
                [elems] // Single node
                : isArray(elems) ?
                    elems // Real array
                    : toArray(elems) // NodeList/Array-like-object
        );
 
    }
 
    function myWrapper(elems) {
        // Instantiate and return new NodeList
        return new NodeList(elems);
    }
 
    myWrapper.NodeList = NodeList;
 
    NodeList.prototype = {
 
        each: function(fn) {
 
            for (var i = -1, l = this.length; ++i < l;) {
                fn.call(this[i], this[i], i, l, this);
            }
 
            return this;
 
        }
 
    };
 
    return myWrapper;
 
})();

There’s no selector engine, but it’s easy enough to slot one in:

function myWrapper(elems) {
    // E.g. using Sizzle as the selector engine
    return new NodeList( typeof elems === 'string' ? Sizzle(elems) : elems );
}

Example of usage:

myWrapper(document.getElementsByTagName('div')).each(function(){
    alert(this.id);
});
// Etc.

Extending NodeList:

myWrapper.NodeList.prototype.applyCSS = (function(){
 
    var hasOwn = Object.prototype.hasOwnProperty;
 
    return function(props) {
 
        for (var i = -1, l = this.length; ++i < l;) {
            for (var p in props) {
                if (hasOwn.call(props, p)) {
                    this[i].style[p] = props[p];
                }
            }
        }
 
        return this;
 
    };
 
}());
 
// Usage:
myWrapper(document.body).applyCSS({
    background: 'red',
    color: 'yellow'
});

76 bytes for faster jQuery

Posted in 'JavaScript' by James on March 31st, 2010

When jQuery fires a callback function, whether it is an event handler, an each iterator, or a filter function, it will normally give you a DOM element as the function’s context, accessible via the this keyword. It’s common practice to subsequently wrap this in jQuery(...) resulting in a newly constructed jQuery instance.

This is no fault of jQuery’s but this practice of redundant “wrapping” generally sucks.

I “tweeted” a while ago, complaining of this:

Constructing a new jq obj on each iteration just to access some text seems wrong. jQuery(‘a’).map(function(){ return $(this).text(); });

Paul Irish suggested that I use jQuery.text instead of jQuery.fn.text, meaning that I wouldn’t have to bother with constructing a new jQuery object every single time my function was called. This was a good suggestion but unfortunately not all of jQuery’s methods have corresponding single-node functions, and it would mean not being able to chain methods.

This is a growing problem, and is only worsened by developers’ insistence on constructing multiple jQuery objects with the same element! -

jQuery('a').click(function(){
 
    if (jQuery(this).attr('href') === 'blah') {
        jQuery(this).next('.whatever').slideToggle();
    }
 
    jQuery(this).fadeTo('slow', 0.4);
 
    return false;
 
});

Eew! Not only are there multiple pointless constructors, but Mr. Uber Cool jQuery Developer isn’t accustomed to the DOM, so has absolutely no idea that, in most situations, this.href would suffice for getting the href property value. This kind of misuse has been discussed in step #3 here: http://encosia.com/2010/03/30/5-steps-toward-jquery-mastery/.

The real problem remains that there are three jQuery objects being constructed, — I feel that it is a library’s obligation to protect against misuse like this. Even if you’re only constructing one jQuery object in a callback it’s still somewhat pointless, in that you’re only doing so to call a couple of methods…

Contextual SCRIPT tags

Posted in 'Code Snippets, JavaScript' by James on August 31st, 2009

Here’s a nifty little trick that allows you to write “contextual” JavaScript. It’s incredibly obtrusive and probably shouldn’t be used at all but it’s still a pretty cool idea:

<div id="some-div">
    <script type=":contextual">
        alert(this.id); // "some-div" is alerted
    </script>
</div>

Instead of the this keyword referencing the global object (window) we can make it reference the parentNode of the script element. Here’s the code that makes it work:

(function(){
 
    var scripts = document.getElementsByTagName('script'),
        script, i = 0;
 
    while ( (script = scripts[i++]) ) {
        if ( /:contextual$/.test(script.type) ) {
            (new Function(script.innerHTML)).call(script.parentNode);
        }
    }
 
})();

Take it or leave it! – I’m indifferent either way – yes, it’s obtrusive but you’ve got to admit, contextual JavaScript looks pretty neat!

JavaScript: Bad Practices

Posted in 'JavaScript' by James on August 17th, 2009
JavaScript: Bad Practices

I’ve seen a lot of curious (bordering on horrific) code in my life; and I’d say about half of it was written by me. If you don’t attest to the fact that you once wrote crap code then you’re either a liar or perhaps, have omnipotent powers!

Here’s a relatively small collection of what I see as bad JavaScript practices; in other words – a list of how to write really really ugly JavaScript that will break! I know it’s quite a negative angle to take but it’s easier than writing a post on best practices, plus it gives me a chance to vent! Many of these are things I’ve done in the past; I’ve since discovered the errors in my ways.

Non-constructor identifiers beginning with a capital letter

Variable names should only begin with a capital if they’re pointing to constructor functions. Doug Crockford has more:

There is a convention that all constructor functions are named with an initial capital, and that nothing else is spelled with an initial capital. This gives us a prayer that visual inspection can find a missing new.

Not following conventions will only get you in trouble! So, to be clear, this is all wrong:

var Color = 'red';
var SomeNumber = 234;
var AnArray = [1,2,3];
var Foo = { bar: 123 };
// None of them should start with a capital letter!

Only functions that are constructors should be identified with an initial captial letter, not regular functions. E.g.

/* This is a regular function. */
function sum() {
    var total = 0, i = 0, len = arguments.length;
    for ( ; i < len; ++i ) {
        total += arguments[i];
    }
    return total;
}
 
/* This is a constructor function */
function Widget(innerText, styles) {
    this.innerText = innerText;
    this.cssStyles = styles;
}

Not using the var statement to define variables

Not using the var statement when defining variables for the first time is a very bad idea. These variables will become part of the global scope. This sucks because you won’t get any warning. Not only is it a bad practice but it can have a negative affect on performance, after-all, the further away a scope is the longer it takes to access. So, always use the var statement:

foo = 3; // NO!
var foo = 3; // YES

Prefixing every new variable in a given scope with var

There's no point in having several var statements if you only need one:

var someVar1 = 'a';
var someVar2 = 'b';
var someVar3 = 'c';
 
// Much easier:
var someVar1 = 'a',
    someVar2 = 'b',
    someVar3 = 'c';

Using non-strict comparison operators, and then comparing across different types

There are two classes of comparison operators; those that type-cast (==/!=) when required and those that don't (===/!==). You don't ever want to use the former!

1 == "1"; // true
false == " \n\t "; // true
[[],[]] == true; // true
 
// ... Confused?

See? It's obviously a bad idea! Please only ever use strict-equality comparison operators; they'll check that the two operands are identical, not just "equal":

1 === "1"; // false
false === " \n\t "; // false
[[],[]] === true; // false
 
// Just as expected!

Not "caching" non-varying complex objects

This is vitally important; if you're going to be repeatedly using an object you should name it and save it! A basic example:

Third party JavaScript – an improvement

Posted in 'JavaScript' by James on August 11th, 2009

If you’ve ever released a JavaScript widget, one which people can include within their site or even if you’re one of those people that has had to copy one of those ugly chunks of code and somehow come to terms with placing it in your previously clean HTML, I’m sure you’ll appreciate a cleaner and less scarring alternative. After all, anything’s got to be better than this:

<div id="widget_133332_g_foobar"></div>
<script>
    widget_133332_v_height = '233px';
    widget_133332_v_width = '200px';
    widget_133332_h_align = true;
    widget_133332_v_align = false;
    widget_133332__debug = false;
    widget_133332_id = 'Foo';
</script>
<script src="http://some-widget-url.com/script?id=widget_133332"></script>

And within script?id=widget_133332 there will be something like this:

document.write('<div id="widget_133332">...</div>');
document.write('<div id="widget_133332_a_sec">...</div>');
document.write('<div id="widget_133332_b_sec">...</div>');
document.write('<div id="widget_133332_c_sec">...</div>');

I’m not complaining; it’s not all that bad. However it could be done better; much better!

I decided to have a proper go at creating one of these. There were a couple of barriers to completion that required some extra work along the way; I’m releasing that extra work as a small stand-alone library that you can use within your 3rd party scripts. Here’s an example chunk-of-code:

<script src="http://some-service.com/widget.js?id=2323">
({
    width: 200,
    height: 400,
    id: 'my-widget',
    keywords: ['apple', 'orange', 'banana']
})
</script>

Nicer to look at and much easier to use than the status quo! The JSON-like syntax is very easy-to-follow, even if you’ve never seen anything like it. The library used to enable this unique technique is called embedHelper.

Introducing “mini”

Posted in 'Cool Stuff, JavaScript' by James on August 9th, 2009
Introducing “mini”

Today I’m releasing my first attempt at creating a “selector engine”; it’s called mini! I’m not sure if this really qualifies as a “selector engine” though; it does not entirely support any particular CSS specification; instead, I’ve built it to work with only the most commonly used selectors.

While the work done on engines such as Peppy, Sizzle and Sly (and others…) is commendable the CSS3-support aspect is totally lost on some users. Resig’s post on “Selectors that People Actually Use” attests to this.

mini has been built to support only the most frequently used CSS selectors. If you prefer CSS3-selector wizardry then you won’t like this, at all!

mini supports the following selectors (and variations):

  • div
  • .example
  • body div
  • div, p
  • div, p, .example
  • div p
  • div > p
  • div.example
  • ul .example
  • #title
  • h1#title
  • div #title
  • ul.foo > * span

Don’t try and do anything fancy with it! It doesn’t support pseudo-selectors, type selectors or any combinators (other than >).

The entire script weighs in at about 1.5kb (minified) and only 850 bytes (minified and gzipped). It’s quite fast too:

Zakas’ JavaScript performance tips

Posted in 'JavaScript' by James on August 8th, 2009
Zakas’ JavaScript performance tips

Nicholad Zakas, who wrote this book, recently spoke about JavaScript performance at Google; his presentation was called “Speed Up Your JavaScript” and it’s available to view on Youtube.

In the presentation Nicholas covers areas rarely talked about; he takes a low-level approach by explaining what’s happening behind the scenes when you do something as simple as creating or requesting a variable in JavaScript. It’s not all conjecture though; he has a number of graphs comparing performance across browsers and then details the steps you can take to optimise your code, in order to protect yourself from less-than-satisfactory JavaScript implementations (IE comes to mind).

I’m not obsessed about performance by any means but I do think that the practices shared in his presentation should be followed to the letter. Speed and performance are hot topics on the client-side; our users demand fast and responsive applications/websites.

His advice:

Five key points emerged during the presentation:

  • Store out-of-scope variables in local variables.
  • Minimise property access.
  • Do as little as possible on each iteration of a loop.
  • Minimise document reflow by only changing the DOM when absolutely necessary.
  • Don’t use inline styles unless you’re animating.

Store out-of-scope variables in local variables

The idea behind this technique is to minimise the amount of work required to get at the variable you want. The further away it is (in the scope chain) the longer it’s going to take to retrieve it. The performance cost is normally marginal but it’s still a good practice. Here’s an example:

Avoiding DOM flickering

Posted in 'Code Snippets, JavaScript' by James on July 23rd, 2009

DOM flickering occurs when you hide elements using JavaScript and there’s a noticeable delay between the page being loaded and the element actually hiding. This usually happens because you’re handling the removal upon the onload event; this is generally a bad idea – this event will not fire until everything has loaded, including images!

It’s considered better practice to use one of the various domReady abstractions available. Unfortunately, this won’t always work either, especially with large DOM structures! One method I’ve been using recently to avoid the flicker is to add a class to the body element as soon as it exists, thereby making it possible to affect the styling of these elements before they even exist within the DOM! An example:

<!-- SIMPLIFIED -->
<html>
    <head>
        <style type="text/css">
            body.js .nojs { display: none; }
        </style>
    </head>
    <body>
 
        <!-- BODY element exists! -->
        <script>
            document.body.className += ' js';
        </script>
 
        <div class="nojs">
            ... No flicker!
        </div>
 
    </body>
</html>

Or, more cleanly:

<!-- SIMPLIFIED -->
<html>
    <head>
        <style type="text/css">
            html.js .nojs { display: none; }
        </style>
        <script>
            // Add class to <html> element
            document.documentElement.className += ' js';
        </script>
    </head>
    <body>
 
        <div class="nojs">
            ... No flicker!
        </div>
 
    </body>
</html>

How do you solve the “flicker” problem?