简体   繁体   English

将DOM元素包装在JavaScript对象中

[英]Wrapping a DOM element inside a JavaScript object

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? 我已经注意到我一直在编写的JavaScript中的一个常见模式,并且想知道是否已经存在一种类似于最佳实践的模式? Essentially, it's how to get a DOM element and wrap it inside / associate it with a JavaScript object. 本质上,它是如何获取DOM元素并将其包装在内部/与JavaScript对象关联。 Take this example, where you need a filter in your web app. 以此示例为例,您需要在Web应用中使用过滤器。 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): 和JavaScript(其中spec是传递给构造函数的对象):

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. 您可能对Dojo的小部件库Dijit感兴趣 - 如果我正确理解您的问题,它基本上就是您所要求的,还有更多。

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. 在Dijit中,一个小部件本质上封装了一个DOM节点,它的内容,定义其行为的任何JavaScript,以及(单独导入)CSS来设置其外观的样式。

Widgets have their own lifecycle, registry, and events (including many which simply map to DOM events on a node within the widget, eg myWidget.onClick could effectively call myWidget.domNode.onclick ). 窗口小部件有自己的生命周期,注册表和事件(包括许多只是映射到窗口小部件节点上的DOM事件,例如myWidget.onClick可以有效地调用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. 窗口小部件可以(但不必)在单独的HTML模板文件中定义其初始内容,通过它们还可以将模板内的节点上的事件绑定到窗口小部件方法,以及在窗口小部件上设置引用特定的属性模板中的节点。

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. 继续我对这个问题的评论,jQuery是一个潜在的工具,因为它已经为你所追求的东西提供了一些基础。 However, having said that, it does introduce complexities of its own, and further, not all "jQuery ways" are equal. 然而,话虽如此,它确实引入了它自己的复杂性,而且,并非所有“jQuery方式”都是平等的。 I'll suggest one way of using jQuery as your "object model", but it may or may not suit your needs. 我建议使用jQuery作为“对象模型”的一种方法,但它可能适合您的需求,也可能不适合您。


First things first. 首先要做的事情。 The philosophy of jQuery is that you start everything by selecting the element first, using $() , or equivalently jQuery() . jQuery的理念是通过首先选择元素,使用$()或等效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. 与创建包装元素并保持对该包装器的引用的对象相比,这是一种略微不同的思维方式,但实质上这就是jQuery为您所做的事情。 A call to $('#some-id') grabs the element with id of "some-id" and wraps it in a jQuery object. $('#some-id')的调用会抓取id为“some-id”的元素并将其包装在jQuery对象中。


One way: Write "Filter" plugins. 一种方法:编写“过滤”插件。

Replace your constructor with a initFilter() jQuery method. initFilter() jQuery方法替换构造函数。 You can do this by modifying the jQuery prototype and using the jQuery object as your wrapper. 您可以通过修改jQuery原型并使用jQuery对象作为包装器来完成此操作。 jQuery's prototype is referenced by jQuery.fn , so: jQuery的原型由jQuery.fn引用,所以:

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() : 然后为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. 请注意, updateFeed可以简单地嵌入到单击处理程序中,以防止对jQuery名称空间造成不必要的污染。 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. 但是,使用这样的jQuery的一个优点是,如果需要稍后调用某个函数,则不需要保留对该对象的引用,因为jQuery将所有引用绑定到实际元素。 If you'd like to call updateFeed programmatically, then: 如果您想以编程方式调用updateFeed ,那么:

$('#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. 一个是我们使用.data()为元素保存的所有属性在作用于该元素的所有jQuery函数之间共享。 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. 我试图通过在属性名称前添加“filter-”来减轻这个问题,但是根据对象的复杂程度,这可能不合适。

Further, this exact method may not be so suitable for objects that require a lot of manipulation (ie objects with many functions) since all of these functions become common to all jQuery objects. 此外,这种精确的方法可能不适合需要大量操作的对象(即具有许多功能的对象),因为所有这些函数对所有jQuery对象都是通用的。 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. 有一些方法可以封装所有这些,我不会在这里进行,但是jQuery-ui用它们的小部件来做这个,而我正在尝试在我正在创建的库中的另一个替代方案。

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. 但是,稍微退一步,我建议首先使用jQuery的唯一原因是你的Filter对象似乎与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, ie jQuery. 它将事件绑定到DOM,它根据用户交互修改DOM,基本上它似乎存在于DOM中,因此使用基于DOM的东西,即jQuery。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM