chemerisuk / better-dom

Live extension playground
http://chemerisuk.github.io/better-dom/
MIT License
545 stars 35 forks source link

DOM.ready is deprecated #19

Closed chemerisuk closed 10 years ago

chemerisuk commented 10 years ago

After some time of using the concept of live extensions I realised that existing well-known ready method is useless. Starting from 1.7.6 the DOM.ready method is deprecated, and it will be removed in the 1.8 release.

In this issue I'll try to explain the reasons of such decision.

What the ready method does

jQuery made the concept of the ready method to be popular. But notice: there is no such method in the standard. W3C document contains only the DOMContentLoaded event, that is fired as soon as browser parses body content on the current web page.

But an important feature of this event is that the event handler is not triggered if you register a it after browser parsers the document tree. To solve this limitation we have ready method, that triggers the callback as soon as DOMContentLoaded event is fired or if it was already triggerend.

As I said before there is no ready method in the existing version of the W3C standard.

Why ready method was useful

The main goal for the ready method is to garantie that the code inside of a callback handler will be executed when your document is ready. It allows to do safe DOM querying.

<!DOCTYPE html>
<html>
<head>
    <script>
    DOM.ready(function() {
        DOM.find("button").on("click", function() {
            alert("it works from <head>!");
        });
    });
    </script>
</head>
<body>
    <button>test</button>
    <script>
    DOM.ready(function() {
        DOM.find("button").on("click", function() {
            alert("it works from <body> as well!");
        });
    });
    </script>
</body>
</html>

Inside of the callback people usually do some initialization, DOM mutations, attach event handlers etc. This is kind of safe start point for any JavaScript for DOM.

Live extensions make the ready method less useful

The main goal for live extensions is to fill gap of the ready method that doesn't work for future HTML content inserted via JavaScript. Additionally to that there are several issues that I think makes sense to cover to clarify the decision of deprecating the DOM.ready in favor of DOM.extend.

Issue 1: code separation

If you use JavaScript a lot on client side, you often need to initialize bunch of plugins. After some time your code looks like below:

DOM.ready(function() {
    // init plugin 1
    // init plugin 2
    ...
    // init plugin n
});

The ready method callback quickly becomes to be a huge. It's possible to split it into several calls and move into several separate files but it's a rare practice.

The main reason of such situation is that every ready call is very similar. There is no distinction and separation between them by sense.

This is what makes live extensions to be different. The first argument of the DOM.extend call is a CSS selector that usually describes the sense of the extension (based on class or tag name). So it means you are forced to separate your code by sense that in general is a good practice to follow.

To summarize: using live extensions makes your code to be more maintainable.

Issue 2: testability

In JavaScript functions produce a new context, and all local variables are not accessable outside. Because of that ready methods are usually hard to test or they require additional refactoring.

DOM.ready(function() {
    // how to test logic here?
});

During development of the live extensions concept I kept in mind testability. And existing API allows to test constructors very easily.

This is how a live extension looks like:

DOM.extend("selector", {
    constructor: function() {
        // extension initialization
        this.doPartOneOfInit();
        ...
        this.doPartTwoOfInit();
    },
    doPartOneOfInit: function(a, b) {
        // do part one
    },
    doPartTwoOfInit: function(c) {
        // do part two
    }
})

Then you can make a mock object that have private methods on it:

var mockObject = DOM.mock("selector");

mockObject.doPartOneOfInit(1, 2);
// assert state
mockObject.doPartTwoOfInit(3);
// assert state

Issue 3: doesn't work for future content

This why live extensions were created. I want to get rid of the ready calls because they have this limitation that doesn't make sense to do in most of use cases. The better-dom library APIs should ecourage developers to use a good practices, like making plugins that work initially for future content even in case they are not aware of such feature.

Conclusion

I hope the article helps to understand why DOM.ready is useless. In case you still need it you can just use the construction below:

DOM.extend("body", {
    constructor: function() {
        // this is equal to DOM.ready callback 
    }
});

Starting from 1.7.6 the method DOM.ready is deprecated. It is scheduled for removal in version 1.8.