【问题标题】:Wrapping a DOM element inside a JavaScript object在 JavaScript 对象中包装 DOM 元素
【发布时间】:2011-06-12 05:38:24
【问题描述】:

我注意到我一直在编写的 JavaScript 中有一个常见的模式,并且想知道是否已经有一个模式可以将类似的东西定义为最佳实践?从本质上讲,它是如何获取 DOM 元素并将其包装在内部/将其与 JavaScript 对象相关联。以这个示例为例,您需要在 Web 应用程序中使用过滤器。您的页面如下所示:

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

然后你可以像这样包装元素:

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

还有 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
};

这种方法叫什么,最佳实践和注意事项是什么?

【问题讨论】:

  • 我不想听起来像个没有头脑的 jQuery 粉丝,但 确实 看起来你最好还是使用 jQuery 或类似的框架。虽然它的主要目的是帮助您进行 DOM 选择和操作,但整个库都是基于您正在谈论的这种 DOM 包装模式构建的,因此它的插件功能和其他辅助功能自然使您可以轻松地做到这一点:好吧。
  • 我仍然需要使用构造函数,因为一个文档中会有多个过滤器。你能告诉我一个使用 jQuery 和 Filter 对象的例子吗?
  • 我会尽快发布答案。另外,下次记得在您的评论中包含@Box9,以便我收到通知。我只是碰巧偶然发现了这个问题。

标签: javascript dom constructor javascript-objects


【解决方案1】:

继续我对这个问题的评论,jQuery 是一个潜在的工具,因为它已经为你所追求的东西提供了一些基础。然而,话虽如此,它确实引入了自己的复杂性,而且,并非所有的“jQuery 方式”都是平等的。我会建议一种使用 jQuery 作为“对象模型”的方法,但它可能适合也可能不适合您的需求。


首先要做的事情。 jQuery 的理念是您首先选择元素,使用$() 或等效的jQuery() 来开始一切。所有操作在概念上都以此开始。与创建包装元素的对象并保留对该包装器的引用相比,这是一种稍微不同的思维方式,但本质上这是 jQuery 为您所做的。对 $('#some-id') 的调用会抓取 id 为“some-id”的元素并将其包装在一个 jQuery 对象中。


一种方法:编写“过滤器”插件。

initFilter() jQuery 方法替换您的构造函数。您可以通过修改 jQuery 原型并使用 jQuery 对象作为包装器来做到这一点。 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();
    });
};

然后为updateFeed()做类似的事情:

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

并像这样使用它:

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

请注意,updateFeed 可以简单地内联到单击处理程序中,以防止对 jQuery 命名空间造成不必要的污染。但是,像这样使用 jQuery 的一个优点是,如果您以后需要在对象上调用某个函数,则不需要保留对该对象的引用,因为 jQuery 将所有引用与实际元素联系在一起。如果您想以编程方式调用updateFeed,那么:

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

然后将在正确的对象上调用。


需要考虑的一些事项

这种方法肯定有缺点。一个是我们使用.data() 保存在元素上的所有属性在作用于该元素的所有jQuery 函数之间共享。我试图通过在属性名称前加上“filter-”来缓解这种情况,但根据对象的复杂性,这可能不合适。

此外,这种精确的方法可能不太适合需要大量操作的对象(即具有许多函数的对象),因为所有这些函数对所有 jQuery 对象都是通用的。有一些方法可以封装所有这些,我不会在这里介绍,但是 jQuery-ui 用他们的小部件来做到这一点,我正在我正在创建的库中尝试另一种替代方法。

但是,稍微退后一点,我建议首先使用 jQuery 的唯一原因是您的 Filter 对象似乎与 DOM 紧密相关。它将事件绑定到 DOM,它根据用户交互修改 DOM,基本上它似乎存在于 DOM 中,所以使用基于 DOM 的东西,即 jQuery。

【讨论】:

  • 我认为这里最大的问题是,正如您所说,所有数据属性的单一范围,更糟糕的是,所有方法的单一范围。它使用 jQuery.fn 范围来存储您可能放入页面的所有对象使用的每个函数,从而降低了模块的可移植性。如果您尝试使用多个库构建页面,则必须确保所有库都没有同名的方法。使用 JS 对象模式,您可以拥有 myapp.module.Filter.updateFilter 和 myapp.module.SpecialFilter.updateFilter 而不必担心两者会在 jQuery.fn.updateFilter 发生冲突
【解决方案2】:

您可能对 Dojo 的小部件库 Dijit 感兴趣 - 如果我正确理解您的问题,它基本上可以满足您的要求,而且还有很多其他功能。

在 Dijit 中,小部件本质上封装了 DOM 节点、其内容、定义其行为的任何 JavaScript 以及(单独导入的)CSS 以设置其外观样式。

小部件有自己的生命周期、注册表和事件(包括许多简单地映射到小部件内节点上的 DOM 事件,例如 myWidget.onClick 可以有效地调用 myWidget.domNode.onclick)。

小部件可以(但不是必须)将它们的初始内容定义在单独的 HTML 模板文件中,通过它还可以将模板内节点上的事件绑定到小部件方法,以及在小部件上设置属性引用模板中的特定节点。

我在这里几乎没有触及表面。如果您想了解更多相关信息,可以从以下参考页面开始:

总而言之,我不知道你的最终目标是什么,也许这对你的目的来说有点多(考虑到我正在向你扔一个完整的其他库),但我认为它可能会激怒你至少有兴趣。

【讨论】:

  • +1,不可否认,jQuery 在这个领域并不像其他库那样先进。
  • 不错的图书馆,它绝对引起了我的兴趣。但是我试图找出这种模式的低级实现细节。如果我保留对 DOM 元素(可能是小部件容器元素)的引用作为构造函数对象的属性,那么有哪些挑战和要记住的事情?我在周末读到的一件事是在 click 函数上使用绑定方法来减少这些事件的作用域链占用空间。本质上,我希望找到这种模式的名称,以便找到更多要阅读的材料。
猜你喜欢
  • 1970-01-01
  • 2011-04-01
  • 2012-12-12
  • 2019-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-06
相关资源
最近更新 更多