Home > Uncategorized > Knockout observables where there was no observables before!

Knockout observables where there was no observables before!

A truly great example of a jQuery plugin is Ben Alman’s resize, which allows you to bind to a resize event on any element, not just the window. It does this by checking for changes to the size of the element in a timer loop – a hack, to be sure, but very nicely hidden behind the same programming interface used in the more normal cases.

Inspired by this, we can take the same approach for knockout, and turn any property of the DOM into an observable.

var simulatedObservable = (function() {

    var timer = null;
    var items = [];

    var check = function() {
        items = items.filter(function(item) {
            return !!item.elem.parents('html').length;
        });
        if (items.length === 0) {
            clearInterval(timer);
            timer = null;
            return;
        }
        items.forEach(function(item) {
            item.obs(item.getter());
        });
    };

    return function(elem, getter) {
        var obs = ko.observable(getter());
        items.push({ obs: obs, getter: getter, elem: $(elem) });
        if (timer === null) {
            timer = setInterval(check, 100);
        }
        return obs;
    };
})();

What’s going on? The whole thing is in the module pattern, so the actual function we’re defining is the one we return at the end. It accepts an element and another function that should get the current value of the property we want to observe. Why do we need the element? It’s just so we can detect when we should stop observing the property.

As long as one of these observables is active, we keep running the check function at 100 ms intervals, which means we share one timer across all instances, automatically stopping it when there are no observables still active. And the check function is pretty self-explanatory. It doesn’t even really need to check if the value has changed, because ko.observable does that for us when we give it a new value. The only bit of real work it has to do is check whether an element is still in the DOM (which is what that parents('html').length part is about).

So how can we use this? Let me count the ways… Suppose you want an observable that holds the vertical offset of a div?

var offset = simulatedObservable(element, function() {
    return $(element).offset().top;
});

And you’re done!

Advertisements
Categories: Uncategorized Tags: ,
  1. No comments yet.
  1. December 26, 2012 at 3:46 pm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: