JQuery Faster UI drag and drop initialization when a container has hidden elements

I have an unordered list of "source" that can contain up to 1000 list items. I want to drag items from the source list to the "destination" list. Everything works fine for me until my source list is filtered. I am using the jsquery quicksearch plugin to filter (search) my source list. The filter is performed by setting "display: none"; on items that do not match the search.

When 1..n objects in my source list are hidden, the drag and drop operation is not triggered at startup. Meaning, I click on the element that I want to drag, move the mouse around the screen, but the element that I drag does not appear under my cursor until a full second appears after I started the drag.

For diagnostics, I reduced my use case to one list that I want to sort. I completely ruled out using quicksearch by simply copying half of my list items as hidden. I am still able to reproduce "non-fluid" behavior. My example:

http://pastebin.com/g0mVE6sc

If I remove the overflow style from the list in my example, the performance will be a little better, but still slower than I hope to see.

Does anyone have any suggestions for me before I start looking at other options?

Thanks in advance.

+6
source share
5 answers

As you can see in this example, jsferf calculating the outer width () / outerHeight () (this is what the plugin is - see below) for hidden elements (with no display) is horribly slower than for visible elements , since this is achieved by the attribute style or class.

The only way I found a workaround for this and still achieve the same result is to set the height for the elements to hide to zero , instead of working with the display property using the atttibute style or class:

<li style="height: 0;">b</li> <li class="hidden">b</li> .hidden { height: 0 } 

DEMO (with class) - DEMO (with attr style)


What happens when sorting while dragging an item?

When you start a drag and drop, the plugin updates the list of all elements and recounts the positions of all elements. The plugin actually gets outerWidth and outerHeight:

 _mouseStart: function(event, overrideHandle, noActivation) { ... //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture this.refreshPositions(); ... } refreshPositions: function(fast) { ... for (var i = this.items.length - 1; i >= 0; i--) { var item = this.items[i]; ... if (!fast) { item.width = t.outerWidth(); item.height = t.outerHeight(); } var p = t.offset(); item.left = p.left; item.top = p.top; }; ... return this; },​ 
+11
source

If you still want to use display: none, this is a simple fix for the jQuery user interface source specified in Didier's answer:

 if (!fast) { if(item.item.css('display') === 'none') { item.width = 0; item.height = 0; } else { item.width = t.outerWidth(); item.height = t.outerHeight(); } } 

This is my first stackoverflow post, so let me know if I messed up something.

+6
source

I also had a similar problem, but with hidden containers to delete instead of sortable items. Here is my solution using Jordan's answer to both sortable items and their containers and simply replacing the delete method.

 $.ui.sortable.prototype.refreshPositions = function(fast) { //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent position will change if(this.offsetParent && this.helper) { this.offset.parent = this._getParentOffset(); } for (var i = this.items.length - 1; i >= 0; i--){ var item = this.items[i]; //We ignore calculating positions of all connected containers when we're not over them if(item.instance != this.currentContainer && this.currentContainer && item.item[0] != this.currentItem[0]) continue; var t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item; if (!fast) { /********** MODIFICATION ***********/ if(item.item.css('display') === 'none') { item.width = 0; item.height = 0; } else { item.width = t.outerWidth(); item.height = t.outerHeight(); } /********** END MODIFICATION ***********/ } var p = t.offset(); item.left = p.left; item.top = p.top; }; if(this.options.custom && this.options.custom.refreshContainers) { this.options.custom.refreshContainers.call(this); } else { for (var i = this.containers.length - 1; i >= 0; i--){ /********** MODIFICATION ***********/ if (this.containers[i].element.css('display') == 'none') { this.containers[i].containerCache.left = 0; this.containers[i].containerCache.top = 0; this.containers[i].containerCache.width = 0; this.containers[i].containerCache.height = 0; } else { var p = this.containers[i].element.offset(); this.containers[i].containerCache.left = p.left; this.containers[i].containerCache.top = p.top; this.containers[i].containerCache.width = this.containers[i].element.outerWidth(); this.containers[i].containerCache.height = this.containers[i].element.outerHeight(); } /********** END MODIFICATION ***********/ }; } return this; }; 
+5
source

I ran into the same problem ... I was looking for a solution, but there seems to be no solution to the jquery problem, only some workarounds ... I did not find a single solution, just another workaround.

In my case, I just created a general method for searching in a sortable list, where when entering the code, the code goes and searches for each element in the list and hides it by fadeout if the cost does not match. This worked very well, but when you have hundreds of items in the list, the hidden one gets large enough to cause a slow drag & drop effect. My solution was to reorder the list, bringing matching elements to the top. Just delete and add again ...

This way I have no problem with hidden elements :)

It is a pity that this was not a solution, but simply a workaround.

Hi

+1
source

More recently, I ran into this problem again ... and found that my workaround was not the best solution. Since the problem is the height ... I just created a CSS class with

.hidden {display: block; line-height:0; height: 0; overflow: hidden; padding: 0; margin: 0; }

and instead of setting the hidden element, just add this class and remove it to show / hide the element.

Regards, AP

+1
source

Source: https://habr.com/ru/post/909324/


All Articles