From cfbec6875b04bda4c3284e84545027f844e4223d Mon Sep 17 00:00:00 2001 From: Andrew Dunkman Date: Tue, 29 May 2018 15:54:08 -0400 Subject: [PATCH 1/2] Rely on focusin, focusout on container. This allows us to accurately catch mouse events on elements within the container, removing the need to do manual focus tracking in our code. --- coffee/chosen.jquery.coffee | 16 ++++++++++++++-- coffee/chosen.proto.coffee | 16 ++++++++++++++-- sass/chosen.scss | 3 +++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/coffee/chosen.jquery.coffee b/coffee/chosen.jquery.coffee index 0054f0e89f0..c90843b2af9 100644 --- a/coffee/chosen.jquery.coffee +++ b/coffee/chosen.jquery.coffee @@ -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 @@ -78,6 +79,10 @@ class Chosen extends AbstractChosen @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.input_blur(evt); return + @container.on 'chosen:focus.chosen', (evt) => this.input_focus(evt); return @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 +98,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,6 +153,15 @@ 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? diff --git a/coffee/chosen.proto.coffee b/coffee/chosen.proto.coffee index 25bd08c9630..0b5734ff391 100644 --- a/coffee/chosen.proto.coffee +++ b/coffee/chosen.proto.coffee @@ -12,6 +12,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 @@ -56,6 +57,10 @@ class @Chosen extends AbstractChosen @container.observe "mouseup", (evt) => this.container_mouseup(evt) @container.observe "mouseenter", (evt) => this.mouse_enter(evt) @container.observe "mouseleave", (evt) => this.mouse_leave(evt) + @container.observe "focusin", (evt) => this.container_focusin(evt) + @container.observe "focusout", (evt) => this.container_focusout(evt) + @container.observe "chosen:blur", (evt) => this.input_blur(evt) + @container.observe "chosen:focus", (evt) => this.input_focus(evt) @search_results.observe "mouseup", (evt) => this.search_results_mouseup(evt) @search_results.observe "mouseover", (evt) => this.search_results_mouseover(evt) @@ -72,10 +77,8 @@ class @Chosen extends AbstractChosen @form_field.observe "chosen:open", (evt) => this.container_mousedown(evt) @form_field.observe "chosen:close", (evt) => this.close_field(evt) - @search_field.observe "blur", (evt) => this.input_blur(evt) @search_field.observe "keyup", (evt) => this.keyup_checker(evt) @search_field.observe "keydown", (evt) => this.keydown_checker(evt) - @search_field.observe "focus", (evt) => this.input_focus(evt) @search_field.observe "cut", (evt) => this.clipboard_event_checker(evt) @search_field.observe "paste", (evt) => this.clipboard_event_checker(evt) @@ -145,6 +148,15 @@ 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.fire('chosen:focus') + + container_focusout: (evt) -> + setTimeout () => + unless @container.contains(document.activeElement) + @container.fire('chosen:blur') if @active_field + search_results_mousewheel: (evt) -> delta = evt.deltaY or -evt.wheelDelta or evt.detail if delta? diff --git a/sass/chosen.scss b/sass/chosen.scss index f2555a675f2..473065157ed 100644 --- a/sass/chosen.scss +++ b/sass/chosen.scss @@ -8,6 +8,9 @@ $chosen-sprite-retina: url('chosen-sprite@2x.png') !default; vertical-align: middle; font-size: 13px; user-select: none; + &:focus { + outline: none; + } * { box-sizing: border-box; } From 78556a7e10feeea84945cbeba40cfd345374f0ac Mon Sep 17 00:00:00 2001 From: Andrew Dunkman Date: Tue, 29 May 2018 16:02:26 -0400 Subject: [PATCH 2/2] Remove tracking of mouse_on_container. We can instead rely on our custom focus and blur events now. --- coffee/chosen.jquery.coffee | 7 +------ coffee/chosen.proto.coffee | 7 +------ coffee/lib/abstract-chosen.coffee | 10 ---------- 3 files changed, 2 insertions(+), 22 deletions(-) diff --git a/coffee/chosen.jquery.coffee b/coffee/chosen.jquery.coffee index c90843b2af9..e76d4787b0f 100644 --- a/coffee/chosen.jquery.coffee +++ b/coffee/chosen.jquery.coffee @@ -77,11 +77,9 @@ 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.input_blur(evt); return + @container.on 'chosen:blur.chosen', (evt) => this.close_field(evt); return @container.on 'chosen:focus.chosen', (evt) => this.input_focus(evt); return @search_results.on 'mouseup.chosen', (evt) => this.search_results_mouseup(evt); return @@ -169,9 +167,6 @@ class Chosen extends AbstractChosen 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 diff --git a/coffee/chosen.proto.coffee b/coffee/chosen.proto.coffee index 0b5734ff391..fc6de48a9f0 100644 --- a/coffee/chosen.proto.coffee +++ b/coffee/chosen.proto.coffee @@ -55,11 +55,9 @@ class @Chosen extends AbstractChosen @container.observe "mousedown", (evt) => this.container_mousedown(evt) @container.observe "mouseup", (evt) => this.container_mouseup(evt) - @container.observe "mouseenter", (evt) => this.mouse_enter(evt) - @container.observe "mouseleave", (evt) => this.mouse_leave(evt) @container.observe "focusin", (evt) => this.container_focusin(evt) @container.observe "focusout", (evt) => this.container_focusout(evt) - @container.observe "chosen:blur", (evt) => this.input_blur(evt) + @container.observe "chosen:blur", (evt) => this.close_field(evt) @container.observe "chosen:focus", (evt) => this.input_focus(evt) @search_results.observe "mouseup", (evt) => this.search_results_mouseup(evt) @@ -164,9 +162,6 @@ class @Chosen extends AbstractChosen 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.hasClassName("chosen-container-active") - close_field: -> @container.ownerDocument.stopObserving "click", @click_test_action diff --git a/coffee/lib/abstract-chosen.coffee b/coffee/lib/abstract-chosen.coffee index d53c968fb81..bc946230b3e 100644 --- a/coffee/lib/abstract-chosen.coffee +++ b/coffee/lib/abstract-chosen.coffee @@ -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 - 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