Opened 4 months ago

Closed 4 months ago

Last modified 4 months ago

#15043 closed bug (fixed)

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

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

Description (last modified by rjollos)

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 4 months ago by scottgonzalez

  • Resolution set to patcheswelcome
  • Status changed from new to closed

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

comment:2 Changed 4 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 4 months ago by scottgonzalez

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 4 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 4 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 4 months ago by scottgonzalez

  • Component changed from ui.autocomplete to ui.widget
  • Resolution patcheswelcome deleted
  • Status changed from closed to reopened
  • Summary changed from JQuery autocomplete performance issue to Widget: Internal list of elements with classes can't be cleaned up
  • Version changed from 1.11.3 to 1.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 4 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 4 months ago by scottgonzalez

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

comment:9 Changed 4 months ago by scottgonzalez

  • Milestone changed from none to 1.12.1
  • Resolution set to fixed
  • Status changed from reopened to closed
Last edited 4 months ago by rjollos (previous) (diff)

comment:10 Changed 4 months ago by arschmitz

  • Owner set to arschmitz

In 89af4c2:

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

Fixes #15043
Closes gh-1744

comment:11 Changed 4 months ago by rjollos

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