-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Remove 100ms lag on blur by using custom blur and focus events. #2990
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,6 +34,7 @@ class Chosen extends AbstractChosen | |
container_props = | ||
'class': container_classes.join ' ' | ||
'title': @form_field.title | ||
'tabIndex': '-1' | ||
|
||
container_props.id = @form_field.id.replace(/[^\w]/g, '_') + "_chosen" if @form_field.id.length | ||
|
||
|
@@ -76,8 +77,10 @@ class Chosen extends AbstractChosen | |
|
||
@container.on 'mousedown.chosen', (evt) => this.container_mousedown(evt); return | ||
@container.on 'mouseup.chosen', (evt) => this.container_mouseup(evt); return | ||
@container.on 'mouseenter.chosen', (evt) => this.mouse_enter(evt); return | ||
@container.on 'mouseleave.chosen', (evt) => this.mouse_leave(evt); return | ||
@container.on 'focusin.chosen', (evt) => this.container_focusin(evt); return | ||
@container.on 'focusout.chosen', (evt) => this.container_focusout(evt); return | ||
@container.on 'chosen:blur.chosen', (evt) => this.close_field(evt); return | ||
@container.on 'chosen:focus.chosen', (evt) => this.input_focus(evt); return | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think relying on custom events here ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This suggests that those events can/should be invoked from a client implementation, which I think is not the case, right? So I think inlining them below would be better. |
||
|
||
@search_results.on 'mouseup.chosen', (evt) => this.search_results_mouseup(evt); return | ||
@search_results.on 'mouseover.chosen', (evt) => this.search_results_mouseover(evt); return | ||
|
@@ -93,10 +96,8 @@ class Chosen extends AbstractChosen | |
@form_field_jq.on "chosen:open.chosen", (evt) => this.container_mousedown(evt); return | ||
@form_field_jq.on "chosen:close.chosen", (evt) => this.close_field(evt); return | ||
|
||
@search_field.on 'blur.chosen', (evt) => this.input_blur(evt); return | ||
@search_field.on 'keyup.chosen', (evt) => this.keyup_checker(evt); return | ||
@search_field.on 'keydown.chosen', (evt) => this.keydown_checker(evt); return | ||
@search_field.on 'focus.chosen', (evt) => this.input_focus(evt); return | ||
@search_field.on 'cut.chosen', (evt) => this.clipboard_event_checker(evt); return | ||
@search_field.on 'paste.chosen', (evt) => this.clipboard_event_checker(evt); return | ||
|
||
|
@@ -150,16 +151,22 @@ class Chosen extends AbstractChosen | |
container_mouseup: (evt) -> | ||
this.results_reset(evt) if evt.target.nodeName is "ABBR" and not @is_disabled | ||
|
||
container_focusin: (evt) -> | ||
return if @active_field | ||
@container.trigger("chosen:focus") | ||
|
||
container_focusout: (evt) -> | ||
setTimeout () => | ||
unless @container[0].contains(document.activeElement) | ||
@container.trigger("chosen:blur") if @active_field | ||
|
||
search_results_mousewheel: (evt) -> | ||
delta = evt.originalEvent.deltaY or -evt.originalEvent.wheelDelta or evt.originalEvent.detail if evt.originalEvent | ||
if delta? | ||
evt.preventDefault() | ||
delta = delta * 40 if evt.type is 'DOMMouseScroll' | ||
@search_results.scrollTop(delta + @search_results.scrollTop()) | ||
|
||
blur_test: (evt) -> | ||
this.close_field() if not @active_field and @container.hasClass "chosen-container-active" | ||
|
||
close_field: -> | ||
$(@container[0].ownerDocument).off "click.chosen", @click_test_action | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,6 @@ class AbstractChosen | |
@click_test_action = (evt) => this.test_active_click(evt) | ||
@activate_action = (evt) => this.activate_field(evt) | ||
@active_field = false | ||
@mouse_on_container = false | ||
@results_showing = false | ||
@result_highlighted = null | ||
@is_rtl = @options.rtl || /\bchosen-rtl\b/.test(@form_field.className) | ||
|
@@ -55,20 +54,12 @@ class AbstractChosen | |
else | ||
item.html | ||
|
||
mouse_enter: -> @mouse_on_container = true | ||
mouse_leave: -> @mouse_on_container = false | ||
|
||
input_focus: (evt) -> | ||
if @is_multiple | ||
setTimeout (=> this.container_mousedown()), 50 unless @active_field | ||
else | ||
@activate_field() unless @active_field | ||
|
||
input_blur: (evt) -> | ||
if not @mouse_on_container | ||
@active_field = false | ||
setTimeout (=> this.blur_test()), 100 | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we now only trigger our |
||
label_click_handler: (evt) => | ||
if @is_multiple | ||
this.container_mousedown(evt) | ||
|
@@ -253,7 +244,6 @@ class AbstractChosen | |
break | ||
when 9 # tab | ||
this.result_select(evt) if @results_showing and not @is_multiple | ||
@mouse_on_container = false | ||
break | ||
when 13 # enter | ||
evt.preventDefault() if @results_showing | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,9 @@ $chosen-sprite-retina: url('[email protected]') !default; | |
vertical-align: middle; | ||
font-size: 13px; | ||
user-select: none; | ||
&:focus { | ||
outline: none; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @adunkman Just to double check: this just removes some extra extraneous focus on some unexpected element, right? Chosen currently styles properly on focus like you'd expect (good for accessibility!), so I'm hoping this is just that! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct! This removes a focus rectangle around the Chosen DIV itself, which only occurs when clicking on search results (after the search input has blurred but before the search result is selected). It’s a side effect of adding |
||
} | ||
* { | ||
box-sizing: border-box; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When attempting to click on an item in the Chosen drop but missing by a pixel or two, we don’t want the Chosen drop to close. We can add a tab index, which allows it to receive focus — resulting in the following:
focusout
event, which is caught on the container and asetTimeout
is scheduled.focusin
event, which is caught on the container and nothing happens (@active_field
is true).setTimeout
expires, checking to see if the container containsdocument.activeElement
, which is in this case, itself. As defined in the spec, a Node.contains()
itself, and therefore the drop is not closed.