jQuery UI Sortable with multiple elements throws "TypeError: Cannot read property 'insertBefore' of null"

jQuery UI Sortable with multiple elements throws "TypeError: Cannot read property 'insertBefore' of null"

我正在尝试按照 this answer: http://jsfiddle.net/hQnWG/614/

中的示例使用多个元素实现拖放

该示例在 jQuery 1.9.1 和 jQuery UI 1.9.2(最高 1.10.1)上运行良好,但一旦我使用更新版本,像 jQuery 1.11.3 和 jQuery UI 1.10.2 或更高版本我遇到以下问题。

选择多个元素并开始缓慢地将最底层拖动到顶部,然后才能捕捉到下一个位置,以下JS错误是抛出的:

Uncaught TypeError: Cannot read property 'insertBefore' of null
   at t.(/hQnWG/614/anonymous function).(anonymous function)._rearrange (https://code.jquery.com/ui/1.10.2/jquery-ui.min.js:7:15171)
   at t.(/hQnWG/614/anonymous function).(anonymous function)._rearrange (https://code.jquery.com/ui/1.10.2/jquery-ui.min.js:5:5028)
   at t.(/hQnWG/614/anonymous function).(anonymous function)._mouseDrag (https://code.jquery.com/ui/1.10.2/jquery-ui.min.js:7:6)
   at t.(/hQnWG/614/anonymous function).(anonymous function)._mouseDrag (https://code.jquery.com/ui/1.10.2/jquery-ui.min.js:5:5028)
   at t.(/hQnWG/614/anonymous function).(anonymous function)._mouseMove (https://code.jquery.com/ui/1.10.2/jquery-ui.min.js:5:12328)
   at t.(/hQnWG/614/anonymous function).(anonymous function)._mouseMove (https://code.jquery.com/ui/1.10.2/jquery-ui.min.js:5:5028)
   at HTMLDocument.o._mouseCapture._mouseDistanceMet._mouseDelayMet._mouseMoveDelegate (VM687 jquery-ui.min.js:5)
   at HTMLDocument.dispatch (VM251 jquery-1.11.3.min.js:4)
   at HTMLDocument.r.handle (VM251 jquery-1.11.3.min.js:4)

这是带有升级 jQuery 版本的示例 jsfiddle 的分支: http://jsfiddle.net/v3p6wsk2/6/

我试过调试它,但找不到任何解释拖动项的parentNode 可以为null 的原因。

我什至在 jQuery UI 1.10.2. change log 中找不到任何关于可能破坏该脚本的更改的提示。

很遗憾,我无法更改我正在处理的项目中的版本。有没有可能让它与提到的版本一起工作?

那么问题的根源就在这里:

_rearrange: function(event, i, a, hardRefresh) {
  a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction === "down" ? i.item[0] : i.item[0].nextSibling));

  //Various things done here to improve the performance:
  // 1. we create a setTimeout, that calls refreshPositions
  // 2. on the instance, we have a counter variable, that get's higher after every append
  // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
  // 4. this lets only the last addition to the timeout stack through
  this.counter = this.counter ? ++this.counter : 1;
  var counter = this.counter;

  this._delay(function() {
    if(counter === this.counter) {
      this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
    }
  });
}

当我使用未压缩版本生成错误时,我看到:

Uncaught TypeError: Cannot read property 'insertBefore' of null
    at $.(anonymous function).(anonymous function)._rearrange (https://code.jquery.com/ui/1.10.3/jquery-ui.js:4628:68)
    at $.(anonymous function).(anonymous function)._rearrange (https://code.jquery.com/ui/1.10.3/jquery-ui.js:401:25)
    at $.(anonymous function).(anonymous function)._mouseDrag (https://code.jquery.com/ui/1.10.3/jquery-ui.js:3879:11)
    at $.(anonymous function).(anonymous function)._mouseDrag (https://code.jquery.com/ui/1.10.3/jquery-ui.js:401:25)
    at $.(anonymous function).(anonymous function)._mouseMove (https://code.jquery.com/ui/1.10.3/jquery-ui.js:933:9)
    at $.(anonymous function).(anonymous function)._mouseMove (https://code.jquery.com/ui/1.10.3/jquery-ui.js:401:25)
    at HTMLDocument._mouseMoveDelegate (https://code.jquery.com/ui/1.10.3/jquery-ui.js:911:16)
    at HTMLDocument.dispatch (https://code.jquery.com/jquery-1.11.3.min.js:4:8549)
    at HTMLDocument.r.handle (https://code.jquery.com/jquery-1.11.3.min.js:4:5252)
Location: jquery-ui.js:4628

_rearrange 函数以条件操作开始。如果 a 存在,则 append 占位符 Else insertBefore() 数组中特定元素的 parentNode

所以,我的猜测是 .insertBefore() 是 DOM 方法而不是 jQuery 方法。根据结构,parentNode 返回一个 null 元素。

HTML DOM parentNode Property

Return Value: A Node object, representing the parent node of a node, or null if the node has no parent

好的,所以我们确定 i.item[0].parentNode 正在重新调整 null 值并导致问题。

我怀疑问题的症结在于 helper 没有包装器,因此 stop 中的每个项目都没有 parent;因此,没有 parentNode。我认为解决方法是删除 1 个项目,然后在其后追加其他元素,或者在另一个回调事件中追加元素。

一旦我有更多信息,我会更新这个。

同样,这并不总是一个问题,到目前为止,这只是基于 Chrome 的浏览器中的一个问题。

更新 1

看起来将此从 stop 切换到 update 回调有所帮助。两者细微差别:

stop( event, ui )

This event is triggered when sorting has stopped.

VS

update( event, ui )

This event is triggered when the user stopped sorting and the DOM position has changed.

我曾希望这就是诀窍。更新后我最初没有得到它,然后我确实得到了它。将不得不继续工作。

更新 2

这个 fiddle 在 FF 和 Chromium 中没有错误:https://jsfiddle.net/Twisty/wzuak8as/99/

我能够找出 helper 创建过程中的错误。我认为问题与这行代码有关:

return helper.append(elements);

所以这段代码表明我们正在返回一个 jQuery 对象,helper,这是一个 <li> 元素作为匿名函数的结果。同时,我们试图将额外的 jQuery 对象 elements 附加到 <li> 元素中。

当我将代码调整为:

helper.append(elements);
return helper;

错误不再出现。

我回去查看了 jQuery UI 1.9.2:

中的相同功能
_rearrange: function(event, i, a, hardRefresh) {
  a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction == 'down' ? i.item[0] : i.item[0].nextSibling));
  //Various things done here to improve the performance:
  // 1. we create a setTimeout, that calls refreshPositions
  // 2. on the instance, we have a counter variable, that get's higher after every append
  // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
  // 4. this lets only the last addition to the timeout stack through
  this.counter = this.counter ? ++this.counter : 1;
  var counter = this.counter;
  this._delay(function() {
    if(counter == this.counter) this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
  });
}

我找不到这两个库的区别。我也无法找出为什么一个操作没有错误而另一个没有。他们应该是一样的。

在我的测试中,我已经能够运行代码没有错误。希望对您有所帮助,您会将其标记为答案。

开发人员工具正在缓存控制台结果,但我没有看到错误出现。我返回到您的原始代码,应用了更改,但得到了同样的错误。

据我所知,问题是调用 parentNode.insertBefore 的元素是被拖动的元素之一(已从原始 table 中删除),因此确实不再有 parent。

真正让我明白的是,我认为问题不直接出在 helperstop 函数中,而是介于两者之间,因为错误在 sort 之后立即被触发-事件。

我不知道这个问题是从哪里产生的,因为它在旧版本上运行,但通过反复试验,我可能找到了解决您问题的方法。

您可以隐藏它们并在将删除的元素插入列表后删除隐藏的元素,而不是删除您的助手中的元素。

参见 twistys fiddle I just updated

这可能会解决您的问题,尽管这并不能解决(甚至找不到)导致此问题的原因。