Opened 15 months ago

Closed 15 months ago

Last modified 15 months ago

#15043 closed bug (fixed)

Widget: Internal list of elements with classes can't be cleaned up

Reported by: WickedDevils Owned by: Alexander Schmitz
Priority: minor Milestone: 1.12.1
Component: ui.widget Version: 1.12.0
Keywords: Cc:
Blocked by: Blocking:

Description (last modified by Ryan J Ollos)

I used the code from site http://jqueryui.com/autocomplete/#combobox and noticed a significant performance issue on my page. My list has ~3000 options in the combobox. I noticed that most of the time it was stuck in the in the jquery js file within this set of code:

        indexOf = function( list, elem ) {
                var i = 0,
                        len = list.length;
                for ( ; i < len; i++ ) {
                        if ( list[i] === elem ) {
                                return i;
                        }
                }
                return -1;
        },`

Even though my list object was only expected to be ~3000 rows, it was actually significantly higher. Here is my stacktrace:

indexOf (jquery-1.12.4.js:644)
hasDuplicate (jquery-1.12.4.js:1375)
Sizzle.uniqueSort (jquery-1.12.4.js:1518)
processClassString (jquery-ui.js:510)
_classes (jquery-ui.js:523)
_toggleClass (jquery-ui.js:549)
_addClass (jquery-ui.js:537)
refresh (jquery-ui.js:5249)
(anonymous function) (jquery-ui.js:144)
_suggest (jquery-ui.js:6100)
(anonymous function) (jquery-ui.js:144)
__response (jquery-ui.js:6043)
(anonymous function) (jquery-ui.js:144)
_superApply (jquery-ui.js:133)
__response (jquery-ui.js:6229)
(anonymous function) (jquery-ui.js:144)
(anonymous function) (jquery-ui.js:6027)
proxy (jquery-1.12.4.js:529)
_source (combobox.html:108)
(anonymous function) (jquery-ui.js:144)
proxy (jquery-1.12.4.js:529)
_search (jquery-ui.js:6019)
(anonymous function) (jquery-ui.js:144)
search (jquery-ui.js:6011)
(anonymous function) (jquery-ui.js:144)
(anonymous function) (jquery-ui.js:5992)
handlerProxy (jquery-ui.js:621)

In processClassString it does line:

current = $( $.unique( current.get().concat( options.element.get() ) ) );

The current list is my 3000 or so rows initially, and then it adds the elements back into that list making it roughly 6000 rows. Depending on how the user is interacting with the page (clicking the dropdown icon multiple times, typing into the textbox for the filter and then deleting the text, etc), it will keep adding to itself. At one point I had almost 200,000 rows in my 'list' variable.

I was able to reproduce this by stepping through the code using Chrome developer tools on http://jqueryui.com/autocomplete/#combobox .

Change History (11)

comment:1 Changed 15 months ago by Scott González

Resolution: patcheswelcome
Status: newclosed

From the demo page: "This is not a supported or even complete widget."

comment:2 Changed 15 months ago by WickedDevils

It's not the widget that is the problem. It is the code behind it that duplicates the data. Did you read what I said at all?

comment:3 Changed 15 months ago by Scott González

Yes, I read what you wrote. I don't see how the code could get up to 200,000 elements from 3,000. It does one concatenation, and then removes the duplicates, so the max count should never be more than 6,000. But even that number should never be reached. Because you've said that there are many interactions involved, and each interaction is actually its own completely separate operation with a new list each time, I don't see the correlation. Your stack trace shows processClassString() being called once and that doesn't provide any information about the number of elements.

Unless you can provide a concrete way for us to see that there is a problem, there's not much we can do.

comment:4 Changed 15 months ago by WickedDevils

I created a video on how to reproduce the issue with the jquery autocomplete example. I have significantly more import options than what the example has.

https://www.youtube.com/watch?v=9zNP5DsyKqA

comment:5 Changed 15 months ago by WickedDevils

This is the chunk of code I have found so far where the ui-menu-items are added

		// Don't refresh list items that are already adapted
		newItems = items.not( ".ui-menu-item, .ui-menu-divider" );
		newWrappers = newItems.children()
			.not( ".ui-menu" )
				.uniqueId()
				.attr( {
					tabIndex: -1,
					role: this._itemRole()
				} );
		this._addClass( newItems, "ui-menu-item" )
			._addClass( newWrappers, "ui-menu-item-wrapper" );

I can see them adding to the list in newItems

comment:6 Changed 15 months ago by Scott González

Component: ui.autocompleteui.widget
Resolution: patcheswelcome
Status: closedreopened
Summary: JQuery autocomplete performance issueWidget: Internal list of elements with classes can't be cleaned up
Version: 1.11.31.12.0

Ok, now I see what's happening. The widget keeps track of the element that have classes applied, but in the case of autocomplete, the menu is constantly rebuilt, so the list grows with every render. Widgets need a way to signify that elements have been removed and should be taken out of the list.

comment:7 Changed 15 months ago by WickedDevils

I reverted jquery UI JS to 1.9 and this problem does not exist. Somewhere between 1.9 and 1.12 it is not working correctly.

comment:8 Changed 15 months ago by Scott González

That somewhere is 1.12.0, which is why I corrected the version field.

comment:9 Changed 15 months ago by Scott González

Milestone: none1.12.1
Resolution: fixed
Status: reopenedclosed
Last edited 15 months ago by Ryan J Ollos (previous) (diff)

comment:10 Changed 15 months ago by Alexander Schmitz

Owner: set to Alexander Schmitz

In 89af4c2:

Widget: Untrack classes elements when they are removed from the DOM

Fixes #15043
Closes gh-1744

comment:11 Changed 15 months ago by Ryan J Ollos

Description: modified (diff)
Note: See TracTickets for help on using tickets.