Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'm trying to implement drag & drop with multiple elements following the example from this answer: http://jsfiddle.net/hQnWG/614/

The example is working fine with jQuery 1.9.1 and jQuery UI 1.9.2 (up to 1.10.1), but as soon as I use newer versions, like jQuery 1.11.3 and jQuery UI 1.10.2 or newer I get the following issues.

When selecting multiple elements and start dragging the bottom most selected element slowly to the top, right before it would snap to the next position, the following JS error is beeing thrown:

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)

Here is a fork of the example jsfiddle with upgraded jQuery versions: http://jsfiddle.net/v3p6wsk2/6/

I tried to debug it, but can't find any explanation how the parentNode of the dragged item can be null. enter image description here

I can't even find any hint in the jQuery UI 1.10.2. change log about what changed that could break that script.

Unfortunately I am not able to change the version in the project I'm working on. Is there any possibility to get this working with the mentioned versions?

like image 999
buggy1985 Avatar asked Jul 25 '26 08:07

buggy1985


2 Answers

As far as I can see, the problem is that the element on which the parentNode.insertBefore is called is one of the dragged elements (which was removed from the original table) and therefore really doesn't have a parent anymore.

What really get's me is that I think the problem is not directly within the helper or stop function but somewhere in between, as the error gets triggered right after the sort-event.

I have no idea where this problem originates as it's functioning with the older versions, but via trial&error I might have found a workaround for your problem.

Instead of removing the elements in your helper, you could just hide them and remove the hidden elements after inserting the dropped elements to the list.

See twistys fiddle I just updated.

This might solve your problem, although this doesn't fix (or even find) the cause for this problem.

like image 193
KingWarin Avatar answered Jul 27 '26 22:07

KingWarin


So the source of the problem lies here:

_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
    }
  });
}

When I generate the error with the Uncompressed version, I see:

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

The _rearrange function starts with a conditional action. If a exists, then append a placeholder Else insertBefore() the parentNode of a specific Element inside of an array.

So, my guess, is that .insertBefore() is the DOM Method and not the jQuery Method. Based on the structure, parentNode is returning a null element.

HTML DOM parentNode Property

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

Ok, so we identify that i.item[0].parentNode is retuning a null value and causes the issue.

I suspect the crux of this is that the helper has no wrapper, hence each item at stop has no parent; therefore, no parentNode. I think the fix will be to drop 1 item and then append after it the other elements or to append the elements in another callback event.

I will update this once I have more information.

Again, this is not always a show stopper and so far, only an issue in Chrome based browsers.

Update 1

Looks like switching this from stop to update callback has helped. Minor differences in the two:

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.

I had hoped this would be the trick. I was not getting it initially after the update and then I did get it. Will have to keep working.

Update 2

This fiddle works without error in FF and Chromium: https://jsfiddle.net/Twisty/wzuak8as/99/

I was able to isolate the error in the creation of the helper. What I believe the issue was related to was this line of code:

return helper.append(elements);

So this code states that we are returning a jQuery Object, helper, which is a <li> element as a result for the anonymous function. At the same time we're attempting to append additional jQuery Objects, elements into the <li> element.

When I adjust the code to something like:

helper.append(elements);
return helper;

The error no longer presents itself.

I went back and looked at the same function in 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
  });
}

I am unable to find a difference in the two libraries. Nor can I find out why one operates without error and one does not. They should be the same.

In my testing, I have been able to run the code without error. I hope that this helps and you will mark it as the answer.

Developer tools were caching console results and I was not seeing the error appear. I went back to your original code, applied the change, and got the same error.

like image 29
Twisty Avatar answered Jul 27 '26 23:07

Twisty



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!