【问题标题】:How to track lists of nodes in a Jquery plugin (if clients may remove those DOM nodes)如何跟踪 Jquery 插件中的节点列表(如果客户端可以删除这些 DOM 节点)
【发布时间】:2011-11-22 00:16:15
【问题描述】:

我正在编写一个 jQuery 插件,它“指向”DOM 中的一定数量的节点。然而,如果我尝试让我的插件保存对一组节点的引用,我担心它们会“过时”。

(虽然我意识到 JavaScript 是垃圾收集并且不会崩溃,但我希望能够使我的列表保持最新,而不是保留应该被 GC 处理的东西。)

我想到的第一件事是可能有某种钩子。但似乎没有一个看起来值得信赖的标准:

With JQuery, is it possible to have a function run when a DOM element calls .remove()?

Callback on the removal of an element from the DOM tree?

jQuery remove() callback?

这让我想知道通过在节点列表上放置类属性来维护节点列表。这样,他们在列表中的成员将与他们一起旅行。但正如人们可能担心的那样,枚举这可能会非常缓慢,这就是为什么您应该将查询表述为tags before classes

除了潜在的性能问题外,我想知道是否认为插件出于这种与样式无关的目的将类插入 DOM 节点是一种糟糕的形式。 (.data() 的优点之一是它相对带外,除了这个列表问题之外,我正在使用它。)

这似乎是一个很常见的问题,其他插件已经解决了。我很想将类解决方案用于它的“正确性”属性,即使它速度较慢。但是,有没有一种更快、更规范的方法可以两全其美?

【问题讨论】:

  • 我认为这很大程度上取决于您对节点所做的工作。
  • @Andrew 我的插件需求其实比较简单——我想要一种快速的方法来枚举它们。客户在运行过程中对一些元素调用$(element).myPlugIn(),以赋予这些元素一些“魔力”......作为该过程的一部分,能够枚举所有节点这一“魔力”非常重要应用于。但是我觉得把它们放在一个全局列表中很不舒服,以防插件的客户端正在修改文档,并且可能在没有明确调用插件来告诉它的情况下删除一些节点......

标签: jquery jquery-plugins jquery-callback


【解决方案1】:

我相信 jQuery UI 是如何做到的。特别是Droppables,他们在内部维护一个 jQuery 对象列表,当有东西悬停在它上面时,它们会迭代这些对象。他们以两种方式管理列表,

  1. 添加在运行 jQuery.remove() 时触发的销毁处理程序。 (他们挂钩到 jQuerys 删除函数来执行此操作)尽管如此,这不会处理普通的 javascript 删除,
  2. 在做任何事情之前检查它是否存在。

我认为他们不会删除它,因为它可能会从 DOM 中删除然后放回原处。

【讨论】:

  • 查看 jQuery UI 是个好主意。所以你指的是$.ui.ddmanager.droppables的处理?如果是这样,remove() 的挂钩是在哪里制作的? github.com/jquery/jquery-ui/blob/master/ui/…
  • 我还注意到在 droppables 源中,each() 后面有一个选择器,上面写着find(":data(droppable)").not(".ui-draggable-dragging")。这是否建议您可以根据具有给定 $.data() 字段的节点进行选择?这确实是我想要的,但我想这和按班级看一样慢,如果不是更慢的话:github.com/jquery/jquery-ui/blob/master/ui/…
  • @HostileFork 查找并没有那么慢,因为范围仅限于放置目标和子对象。使用类会快得多,该数据选择器实际上是 UI 自定义的,它在核心中定义。像这样的自定义选择器会慢得多。
  • 自定义选择器啊。好吧,在接近赏金截止日期之前,没有人有更多话要说。将您关于 jQuery UI 的观点视为 jQuery 用户的规范(什么更高的权限?)。但我很好奇他们技术的好/坏/丑……这会破坏“走树”还是只去除特定的根?他们的方法得到了什么样的保证?
【解决方案2】:

很抱歉打扰您,但“可信赖的基于 jQuery 的代码”在术语上是自相矛盾的。 FWIW:

jQuery 与其他几个当前流行的脚本框架一样,创建一个类似Array 的对象来保存查询结果,而不是实现W3C DOM Level 2+ CoreNodeList interface 的对象(简称NodeList)。一个NodeListliveArray-like 对象,它们是原生 ECMAScript 对象,而不是宿主对象,(通常)不是。

如果您不为此使用此类框架,则不会出现此问题,因为所有(准)标准 DOM 方法(如getElementsByClassName())和属性(如document.forms)都会返回/产生NodeList (有时甚至是HTMLCollection)。

DOM Level 2 Events 指定了mutation event types,但从DOM Level 3 Events工作草案)开始,它们是deprecated,现在和probably not even interoperable 上的任何挂钩都不可靠。

因此,您应该为此使用(准)标准方法,而不是 jQuery 结果和突变事件。

但是,如果您仍然想采用 jQuery(-ish) 方式(以及所有其他不明显的缺点),您应该查看 NodeparentNode 属性。 Nodes 不是 Document 节点但仍位于文档树中,具有非 null parentNode 属性值(而 null 应理解为 W3C DOM Level 2+ 核心规范的语言-independent null,因此您应该在 ECMAScript 实现中查找所有类型转换为 true 的值)。因此,所有其他 Nodes 的 parentNode 属性值为 null,因此在 ECMAScript 实现中类型转换为 false

假设myNodes 指的是一个Array 实例,其中的元素是Nodes,而myNode 指的是一个这样的节点对象,以下应该可以工作:

var i = 0;

/* … */

var myNode = myNodes[i];
if (!myNode.parentNode)
{
  myNodes.splice(i, 1);
}

然后您可以偶尔迭代myNodes,例如在通过window.setInterval() 调用的函数中,并从中删除“陈旧”节点。它当然不如 NodeList 好,但即使在不使用 jQuery 的情况下删除节点,即使运行时环境不支持突变事件类型,它也可以工作。

不幸的是,jQuery() 没有返回对 Array 实例的引用。所以你必须先做一个,像这样:

var jqObj = jQuery("…");

/* or jQuery.makeArray(jqObj) */
var a = Array.prototype.slice.call(jqObj, 0);

(我们在这里使用jqObj 引用的对象确实具有length 属性的事实。)

我不能说什么是 jQuery 插件的好样式,至于现在应该很明显的原因,我一开始就尽量避免使用 jQuery。

您还应该学会区分 the programming language*s*, like JavaScript 和可以与它们一起使用的(通常与语言无关的)API,例如 DOM。

HTH。

【讨论】:

  • 这是有用的东西,谢谢——特别是指出 NodeList 的区别!虽然我当然知道 jQuery 是 JavaScript 之上的一个层,但我通常认为这一层是值得的。一段时间以来,我的目标是保持对尝试以跨浏览器方式进行 GUI 应用程序开发的泥潭一无所知。但我显然在这方面失败了(或改变了目标),所以我会回顾一下并尝试选择一个可接受的解决方案。
  • 奖励你赏金和 +1 的见解,但要安德鲁在 jQuery 宇宙的背景下回答......
  • 我 NodeList 不是这个问题的答案。 OP 需要一个仅由他的插件填充的节点列表。如果将它绑定到返回 NodeList 的类或其他选择器,插件将失去对列表的控制。即,如果用户有一个带有插件类的 div 列表,但只在列表的一半上调用插件。该插件将错误地适用于所有 div。
  • 相反,如果项目仍在他的 dom 中,您也不希望从列表中删除它们。即用户将节点类更改为不同的样式会无意中将其从插件中删除。插件必须保留对列表的控制,这就是必须使用数组的原因。
  • 那个e。 G。一组特定的class 属性值组件定义插件考虑哪些元素正是我的观点。与Array 类似的对象的不同之处仅在于您必须重复确定该组元素,而NodeList 将自动更新。找出哪些元素已从类似Array 的对象中消失并非易事,找出哪些元素已从文档树中消失相当昂贵,而使用NodeList,您不必费心首先签入地方。请考虑最初的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-27
  • 2021-06-02
  • 1970-01-01
  • 2021-05-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多