Browse Source

reworked how dropdown is managed in singleselect so the search field has focus even when the dropdown is not opened and can capture te first keypress. fixes #196

Igor Vaynberg 13 years ago
parent
commit
1605a631e5
1 changed files with 76 additions and 63 deletions
  1. 76 63
      select2.js

+ 76 - 63
select2.js

@@ -806,26 +806,34 @@
 
 
             if (!this.shouldOpen()) return false;
             if (!this.shouldOpen()) return false;
 
 
+            window.setTimeout(this.bind(this.opening), 1);
+
+            return true;
+        },
+
+        /**
+         * Performs the opening of the dropdown
+         */
+        // abstract
+        opening: function() {
             this.clearDropdownAlignmentPreference();
             this.clearDropdownAlignmentPreference();
 
 
             if (this.search.val() === " ") { this.search.val(""); }
             if (this.search.val() === " ") { this.search.val(""); }
 
 
-            this.container.addClass("select2-dropdown-open").addClass("select2-container-active");
-            if(this.dropdown[0] !== this.body.children().last()[0]) {
-				// ensure the dropdown is the last child of body, so the z-index is always respected correctly
-                this.dropdown.detach().appendTo(this.body);
-            }
-
             this.dropdown.addClass("select2-drop-active");
             this.dropdown.addClass("select2-drop-active");
 
 
             this.positionDropdown();
             this.positionDropdown();
 
 
             this.updateResults(true);
             this.updateResults(true);
+
+            if(this.dropdown[0] !== this.body.children().last()[0]) {
+                this.dropdown.detach().appendTo(this.body);
+            }
+
             this.dropdown.show();
             this.dropdown.show();
             this.ensureHighlightVisible();
             this.ensureHighlightVisible();
             this.focusSearch();
             this.focusSearch();
-
-            return true;
+            this.container.addClass("select2-dropdown-open").addClass("select2-container-active");
         },
         },
 
 
         // abstract
         // abstract
@@ -1132,11 +1140,11 @@
                 "class": "select2-container",
                 "class": "select2-container",
                 "style": "width: " + this.getContainerWidth()
                 "style": "width: " + this.getContainerWidth()
             }).html([
             }).html([
-                "    <a href='javascript:void(0)' class='select2-choice'><input type='text' class='select2-offscreen select2-focusser'/>",
+                "    <a href='javascript:void(0)' class='select2-choice'>",
                 "   <span></span><abbr class='select2-search-choice-close' style='display:none;'></abbr>",
                 "   <span></span><abbr class='select2-search-choice-close' style='display:none;'></abbr>",
                 "   <div><b></b></div>" ,
                 "   <div><b></b></div>" ,
                 "</a>",
                 "</a>",
-                "    <div class='select2-drop' style='display:none;'>" ,
+                "    <div class='select2-drop select2-offscreen'>" ,
                 "   <div class='select2-search'>" ,
                 "   <div class='select2-search'>" ,
                 "       <input type='text' autocomplete='off' class='select2-input'/>" ,
                 "       <input type='text' autocomplete='off' class='select2-input'/>" ,
                 "   </div>" ,
                 "   </div>" ,
@@ -1145,16 +1153,23 @@
                 "</div>"].join(""));
                 "</div>"].join(""));
         },
         },
 
 
+        // single
+        opening: function () {
+            this.parent.opening.apply(this, arguments);
+            this.dropdown.removeClass("select2-offscreen");
+        },
+
         // single
         // single
         close: function () {
         close: function () {
             if (!this.opened()) return;
             if (!this.opened()) return;
             this.parent.close.apply(this, arguments);
             this.parent.close.apply(this, arguments);
+            this.dropdown.removeAttr("style").addClass("select2-offscreen").insertAfter(this.selection).show();
         },
         },
 
 
         // single
         // single
         focus: function () {
         focus: function () {
             this.close();
             this.close();
-            this.selection.focus();
+            this.search.focus();
         },
         },
 
 
         // single
         // single
@@ -1165,7 +1180,7 @@
         // single
         // single
         cancel: function () {
         cancel: function () {
             this.parent.cancel.apply(this, arguments);
             this.parent.cancel.apply(this, arguments);
-            this.selection.focus();
+            this.search.focus();
         },
         },
 
 
         // single
         // single
@@ -1174,65 +1189,66 @@
             var selection,
             var selection,
                 container = this.container,
                 container = this.container,
                 dropdown = this.dropdown,
                 dropdown = this.dropdown,
-                containers = $([this.container.get(0), this.dropdown.get(0)]),
-                clickingInside = false,
-                selector = ".select2-choice",
-                focusser=container.find("input.select2-focusser");
+                clickingInside = false;
 
 
-            this.selection = selection = container.find(selector);
+            this.selection = selection = container.find(".select2-choice");
 
 
             this.search.bind("keydown", this.bind(function (e) {
             this.search.bind("keydown", this.bind(function (e) {
-                switch (e.which) {
-                case KEY.UP:
-                case KEY.DOWN:
-                    this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
-                    killEvent(e);
-                    return;
-                case KEY.TAB:
-                case KEY.ENTER:
-                    this.selectHighlighted();
-                    killEvent(e);
-                    return;
-                case KEY.ESC:
-                    this.cancel(e);
+                if (!this.enabled) return;
+
+                if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN || e.which === KEY.SPACE) {
+                    // prevent the page from scrolling
                     killEvent(e);
                     killEvent(e);
                     return;
                     return;
                 }
                 }
+
+                if (this.opened()) {
+                    switch (e.which) {
+                        case KEY.UP:
+                        case KEY.DOWN:
+                            this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
+                            killEvent(e);
+                            return;
+                        case KEY.TAB:
+                        case KEY.ENTER:
+                            this.selectHighlighted();
+                            killEvent(e);
+                            return;
+                        case KEY.ESC:
+                            this.cancel(e);
+                            killEvent(e);
+                            return;
+                    }
+                } else {
+                    if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) {
+                        return;
+                    }
+
+                    this.open();
+
+                    if (e.which === KEY.ENTER) {
+                        // do not propagate the event otherwise we open, and propagate enter which closes
+                        killEvent(e);
+                        return;
+                    }
+                }
             }));
             }));
 
 
-            containers.delegate(selector, "click", this.bind(function (e) {
+            selection.bind("click", this.bind(function (e) {
                 clickingInside = true;
                 clickingInside = true;
 
 
                 if (this.opened()) {
                 if (this.opened()) {
                     this.close();
                     this.close();
-                    selection.focus();
+                    this.search.focus();
                 } else if (this.enabled) {
                 } else if (this.enabled) {
                     this.open();
                     this.open();
                 }
                 }
-                e.preventDefault();
+                killEvent(e);
 
 
                 clickingInside = false;
                 clickingInside = false;
             }));
             }));
-            containers.delegate(selector, "keydown", this.bind(function (e) {
-                if (!this.enabled || e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) {
-                    return;
-                }
-                this.open();
-                if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN || e.which === KEY.SPACE) {
-                    // prevent the page from scrolling
-                    killEvent(e);
-                }
-                if (e.which === KEY.ENTER) {
-                    // do not propagate the event otherwise we open, and propagate enter which closes
-                    killEvent(e);
-                }
-            }));
-            containers.delegate(selector, "focus", function () { if (this.enabled) { containers.addClass("select2-container-active"); dropdown.addClass("select2-drop-active"); }});
-            containers.delegate(selector, "blur", this.bind(function (e) {
-                if (clickingInside) return;
-                if (e.target===focusser.get(0)) return; // ignore blurs from focusser
-                if (!this.opened()) { this.blur(); }
-            }));
+
+            dropdown.bind("click", this.bind(function() { this.search.focus(); }));
 
 
             selection.delegate("abbr", "click", this.bind(function (e) {
             selection.delegate("abbr", "click", this.bind(function (e) {
                 if (!this.enabled) return;
                 if (!this.enabled) return;
@@ -1240,18 +1256,16 @@
                 killEvent(e);
                 killEvent(e);
                 this.close();
                 this.close();
                 this.triggerChange();
                 this.triggerChange();
-                selection.focus();
+                this.search.focus();
             }));
             }));
 
 
+            selection.bind("focus", this.bind(function() { this.search.focus(); }));
+
             this.setPlaceholder();
             this.setPlaceholder();
 
 
-            focusser.bind("focus", function() { selection.focus(); });
-            selection.bind("focus", this.bind(function() {
-                focusser.hide();
+            this.search.bind("focus", this.bind(function() {
                 this.container.addClass("select2-container-active");
                 this.container.addClass("select2-container-active");
             }));
             }));
-            selection.bind("blur", function() { focusser.show(); });
-            this.opts.element.bind("open", function() { focusser.hide(); });
         },
         },
 
 
         clear: function() {
         clear: function() {
@@ -1358,7 +1372,7 @@
             this.opts.element.val(this.id(data));
             this.opts.element.val(this.id(data));
             this.updateSelection(data);
             this.updateSelection(data);
             this.close();
             this.close();
-            this.selection.focus();
+            this.search.focus();
 
 
             if (!equal(old, this.id(data))) { this.triggerChange(); }
             if (!equal(old, this.id(data))) { this.triggerChange(); }
         },
         },
@@ -1623,13 +1637,12 @@
         },
         },
 
 
         // multi
         // multi
-        open: function () {
-            if (this.parent.open.apply(this, arguments) === false) return false;
+        opening: function () {
+            this.parent.opening.apply(this, arguments);
 
 
             this.clearPlaceholder();
             this.clearPlaceholder();
 			this.resizeSearch();
 			this.resizeSearch();
             this.focusSearch();
             this.focusSearch();
-            return true;
         },
         },
 
 
         // multi
         // multi