Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go Go Go Go Chosen (Chosen gets a speed boost) #1339

Merged
merged 31 commits into from
Jul 10, 2013
Merged

Conversation

pfiller
Copy link
Contributor

@pfiller pfiller commented Jul 8, 2013

@kenearley @stof @koenpunt

Better get ready because this is a big one.

Chosen has notable speed problems when running with lots and lots of lots of items. Though Chrome can be quite forgiving, there is still a lag when thousands of items are in a list. Forget about using IE8 with thousands of items.

There exists an obvious performance problem with Chosen's current winnow_results method. For each item in a list, Chosen does a DOM manipulation (to show or hide the element). Each manipulation triggers a reflow which isn't such a big deal for a list of 10 ... but you can see how that would quickly cause Chosen to become sluggish.

In this PR, the winnow_results method just loops through the results_data array and marks each result as a match or not. After the results are properly marked, results_option_build is called and the search results html is re-built. That is, we're replacing n dom manipulations with just 1.

I bet you want stats. For a select with 1,000 options:

  • Applying Chosen is 62% faster across all browsers
  • Showing the search results for the first time is 70% faster across all browsers.
  • Showing the search results for the first time is 92% faster in IE8. 92%!!!!!
  • Searching Chosen is 77% faster.

If we bump the list to 5,000 options:

  • Applying Chosen is only 41% faster.
  • Showing the search results for the first time is 89% faster across all browsers.
  • Search Chosen is 72% faster

To get these numbers, I created an HTML page that created a list and ran Chosen 100 times (50 single and 50 multiple) and then I ran it against the new version and the old version. I've shared the test file and the results for those curious.

I don't think these numbers should be treated as scientific, but I do think they are honest. You only need to open a select with 5000 items once in the two different versions of Chosen before you recognize the difference. Chosen is absolutely faster and it feels it.

Now, this is a big change and I would appreciate your careful review. I've done a lot of click testing, but I would love some more. This is a real nice change for Chosen, though, and I'd love to see it land this week.

Thanks!

pfiller added 29 commits June 3, 2013 17:39
Conflicts:
	chosen/chosen.jquery.min.js
	chosen/chosen.proto.min.js
	coffee/chosen.jquery.coffee
	coffee/lib/abstract-chosen.coffee
	public/chosen.jquery.js
	public/chosen.proto.js
Conflicts:
	public/chosen.jquery.js
	public/chosen.jquery.min.js
	public/chosen.proto.min.js
Conflicts:
	public/chosen.jquery.min.js
	public/chosen.proto.min.js
These are dom-based functions which add and remove classes to show
option availability. Our new options_build method makes them
unecessary.
In this new version, the DOM is recreated every time the serach
filter changes. That means the element that @result_highlight refers
to is gone after a search.
new variable to track match status. Also: change value to true if
a parts match is found.
results are currently showing.

results_show calls winnow so there's no need to do this in advance.
This is the only part of winnow_results that is jQuery dependent and
moving it paves the way to move winnow_results into AbstractChosen.
It should have been this way from the beginning, but it wasn't. Moving
to a method will move results_otion_build to AbstractChosen
single_set_selected_text method.

They are always called together so it makes sense to link them more
tightly.

Note: test for @allow_single_deselct is present in the
single_deselect_control_build method itself - so no need for
redundancy.
Conflicts:
	public/chosen.jquery.min.js
setting the search results text. .html() is jQuery-only and has no
place in AbstractChosen.
winnow_results needs to be able to update content and this will
contribute to moving it to AbstractChosen
@pfiller
Copy link
Contributor Author

pfiller commented Jul 8, 2013

I probably should have mentioned: because we are no longer doing dom manipulation in winnow_results, it's been moved into AbstractChosen. A few helper methods have been added to the proto and jquery methods which handle some smaller dom stuff.

@koenpunt
Copy link
Collaborator

koenpunt commented Jul 8, 2013

Oh wow, this is big one. But great work. 👍

Conflicts:
	public/chosen.jquery.js
	public/chosen.jquery.min.js
	public/chosen.min.css
	public/chosen.proto.js
	public/chosen.proto.min.js
@@ -68,7 +86,7 @@ class AbstractChosen

style = if option.style.cssText != "" then " style=\"#{option.style}\"" else ""

'<li id="' + option.dom_id + '" class="' + classes.join(' ') + '"'+style+'>' + option.html + '</li>'
'<li id="' + option.dom_id + '" class="' + classes.join(' ') + '"'+style+'>' + option.search_text + '</li>'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to get rid of the concatenation and use CoffeeScript's interpolation syntax here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually think the interpolation version is harder to read:

"<li id=\"#{option.dom_id}\" class=\"#{classes.join(' ')}\"#{style}>#{option.search_text}</li>"
'<li id="' + option.dom_id + '" class="' + classes.join(' ') + '"'+style+'>' + option.search_text + '</li>'

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They're both ugly, though.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like less quotation marks.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is possible to use triple quotes like this:

"""<li id="#{option.dom_id}" class="#{classes.join(' ')}"#{style}>#{option.search_text}</li>"""

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, duh!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kenearley
Copy link

This is great. I just have the one comment about string concatenation, which is not a blocker. :shipit:

pfiller added a commit that referenced this pull request Jul 10, 2013
@pfiller pfiller merged commit 992cf28 into master Jul 10, 2013
@pfiller pfiller deleted the the-need-for-speed branch July 10, 2013 18:39
pfiller added a commit that referenced this pull request Jul 10, 2013
- Speed Chosen up significantly (#1339)
- Fix broken RTL example #1331
- Updated release procedure to remove S3 #1323
- Include docsupport contents in zip #1321
- Replace deprecated grunt-css with grunt-contrib-cssmin #1319
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants