문제

I've noticed a common pattern in the JavaScript I've been writing and was wondering if there is already a pattern out there that defines something similar as best practice? Essentially, it's how to get a DOM element and wrap it inside / associate it with a JavaScript object. Take this example, where you need a filter in your web app. Your page looks like this:

<html>
<head></head>
<body>
    <div id="filter"></div>
</body>
</html>

You'd then wrap the element like so:

var myFilter = new Filter({
    elem: document.getElementById('filter'),
    prop: 'stacks-test',
    someCallback: function() {
        // specify a callback
    }
});

And the JavaScript (where spec is an object passed to the constructor):

var Filter = function(spec) {
    this.elem = spec.elem;
    this.prop = spec.prop;
    this.callback = spec.someCallback;
    this.bindEvents();
};

Filter.prototype.bindEvents = function() {
    var self = this;
    $(this.elem).click(function(e) {
        self.updateFeed();
    };
};

Filter.prototype.updateFeed = function() {
    this.prop; // 'stacks-test'
    this.callback();
    // ...
    // code to interact with other JavaScript objects
    // who in turn, update the document
};

What is this kind of approach called, and what are the best practices and caveats?

도움이 되었습니까?

해결책

You might be interested in Dojo's widget library, Dijit - if I'm understanding your question correctly, it essentially does what you're asking, and a whole lot more.

In Dijit, a widget essentially encapsulates a DOM node, its contents, any JavaScript that defines its behavior, and (imported separately) CSS to style its appearance.

Widgets have their own lifecycle, registry, and events (including many which simply map to DOM events on a node within the widget, e.g. myWidget.onClick could effectively call myWidget.domNode.onclick).

Widgets can (but don't have to) have their initial contents defined in a separate HTML template file, through which it's also possible to bind events on nodes within the template to widget methods, as well as set properties on the widget that reference particular nodes in the template.

I'm barely scratching the surface here. If you want to read more on this, you can start with these reference pages:

All said, I don't know what you're ultimately aiming for, and maybe this is all a bit much for your purposes (considering I'm throwing an entire other library at you), but figured it might pique your interest at least.

다른 팁

Continuing from my comment on the question, jQuery is a potential tool for the job, as it already provides some of the foundations for what you're after. However, having said that, it does introduce complexities of its own, and further, not all "jQuery ways" are equal. I'll suggest one way of using jQuery as your "object model", but it may or may not suit your needs.


First things first. The philosophy of jQuery is that you start everything by selecting the element first, using $(), or equivalently jQuery(). All operations conceptually begin with this. This is a slightly different way of thinking compared to creating an object that wraps an element and keeping a reference to that wrapper, but essentially this is what jQuery does for you. A call to $('#some-id') grabs the element with id of "some-id" and wraps it in a jQuery object.


One way: Write "Filter" plugins.

Replace your constructor with a initFilter() jQuery method. You can do this by modifying the jQuery prototype and using the jQuery object as your wrapper. jQuery's prototype is referenced by jQuery.fn, so:

jQuery.fn.initFilter = function (prop, callback) {
    // Save prop and callback
    this.data('filter-prop', prop);
    this.data('filter-callback', callback);

    // Bind events (makes sense to do this during init)
    this.click(function () {
        $(this).updateFeed();
    });
};

Then do a similar thing for updateFeed():

jQuery.fn.updateFeed = function () {
    this.data('filter-prop');
    this.data('filter-callback')();
});

And use it like this:

$('#filter').initFilter(prop, callback);

Note that updateFeed can simply be in-lined into the click handler to prevent unnecessary pollution of the jQuery namespace. However, one advantage of using jQuery like this is that you do not need to keep a reference to the object if you need to invoke some function on it later, since jQuery ties all references to actual elements. If you'd like to call updateFeed programmatically, then:

$('#filter').updateFeed();

will then be invoked on the correct object.


Some things to consider

There are certainly downsides to this method. One is that all properties, which we've saved against the element using .data(), are shared between all jQuery functions that act on that element. I've attempted to alleviate this by prefixing the property names with "filter-", but depending on the complexity of your object(s), this may not be suitable.

Further, this exact method may not be so suitable for objects that require a lot of manipulation (i.e. objects with many functions) since all of these functions become common to all jQuery objects. There are ways to encapsulate all this which I won't go into here, but jQuery-ui does this with their widgets, and I'm experimenting with yet another alternative in a library I'm creating.

However, pulling back a bit, the only reason I suggested using jQuery in the first place is that your Filter object appears to be heavily tied to the DOM. It binds events to the DOM, it modifies the DOM based on user interaction, basically it appears to live in the DOM, so use something DOM-based, i.e. jQuery.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top