Skip to content

Commit 6a0c002

Browse files
committed
Add a module for attaching the dropdown to the body
In Select2 3.x, the dropdown is attached to the body element and it is floated above all other elements. In Select2 4.x, the dropdown is attached directly to the Select2 container, which allows us to skip any special placing logic. This happens to be how Chosen currently does it, and it prevents us from having the dropdown display up, as well as a few other strange issues that can't be prevented. This new module will most likely become the default, as it matches the functionality of Select2 3.x and has quite a few advantages. The other positioning code will need to be broken out into a separate module in the future.
1 parent 31c0931 commit 6a0c002

10 files changed

Lines changed: 416 additions & 21 deletions

File tree

dist/js/select2.amd.full.js

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,8 @@ define('select2/results',[
230230
this.$results.append($options);
231231
};
232232

233-
Results.prototype.position = function ($results, $container) {
234-
var $resultsContainer = $container.find('.select2-results');
233+
Results.prototype.position = function ($results, $dropdown) {
234+
var $resultsContainer = $dropdown.find('.select2-results');
235235
$resultsContainer.append($results);
236236
};
237237

@@ -2812,6 +2812,79 @@ define('select2/dropdown/infiniteScroll',[
28122812
return InfiniteScroll;
28132813
});
28142814

2815+
define('select2/dropdown/attachBody',[
2816+
2817+
], function () {
2818+
function AttachBody (decorated, $element, options) {
2819+
decorated.call(this, $element, options);
2820+
}
2821+
2822+
AttachBody.prototype.bind = function (decorated, container, $container) {
2823+
var self = this;
2824+
2825+
decorated.call(this, container, $container);
2826+
2827+
container.on('open', function () {
2828+
self._showDropdown();
2829+
});
2830+
2831+
container.on('close', function () {
2832+
self._hideDropdown();
2833+
});
2834+
2835+
this.$dropdownContainer.on('mousedown', function (evt) {
2836+
evt.stopPropagation();
2837+
});
2838+
};
2839+
2840+
AttachBody.prototype.position = function (decorated, $dropdown, $container) {
2841+
// Clone all of the container classes
2842+
$dropdown.attr('class', $container.attr('class'));
2843+
2844+
$dropdown.removeClass('select2');
2845+
$dropdown.addClass('select2-container--open');
2846+
2847+
$dropdown.css({
2848+
position: 'absolute'
2849+
});
2850+
2851+
$dropdown.width($container.outerWidth(false));
2852+
2853+
this.$container = $container;
2854+
};
2855+
2856+
AttachBody.prototype.render = function (decorated) {
2857+
var $container = $('<span></span>');
2858+
2859+
var $dropdown = decorated.call(this);
2860+
$container.append($dropdown);
2861+
2862+
this.$dropdownContainer = $container;
2863+
2864+
return $container;
2865+
};
2866+
2867+
AttachBody.prototype._hideDropdown = function (decorated) {
2868+
this.$dropdownContainer.detach();
2869+
};
2870+
2871+
AttachBody.prototype._positionDropdown = function () {
2872+
var css = this.$container.offset();
2873+
2874+
css.top += this.$container.outerHeight(true);
2875+
2876+
this.$dropdownContainer.css(css);
2877+
};
2878+
2879+
AttachBody.prototype._showDropdown = function (decorated) {
2880+
this.$dropdownContainer.appendTo(document.body);
2881+
2882+
this._positionDropdown();
2883+
};
2884+
2885+
return AttachBody;
2886+
});
2887+
28152888
define('select2/i18n/en',[],function () {
28162889
return {
28172890
errorLoading: function () {
@@ -2884,6 +2957,7 @@ define('select2/defaults',[
28842957
'./dropdown/search',
28852958
'./dropdown/hidePlaceholder',
28862959
'./dropdown/infiniteScroll',
2960+
'./dropdown/attachBody',
28872961

28882962
'./i18n/en'
28892963
], function ($, ResultsList,
@@ -2892,6 +2966,7 @@ define('select2/defaults',[
28922966
Utils, Translation, DIACRITICS,
28932967
SelectData, ArrayData, AjaxData, Tags, MinimumInputLength,
28942968
Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
2969+
AttachBody,
28952970
EnglishTranslation) {
28962971
function Defaults () {
28972972
this.reset();
@@ -2948,6 +3023,11 @@ define('select2/defaults',[
29483023

29493024
options.dropdownAdapter = SearchableDropdown;
29503025
}
3026+
3027+
options.dropdownAdapter = Utils.Decorate(
3028+
options.dropdownAdapter,
3029+
AttachBody
3030+
);
29513031
}
29523032

29533033
if (options.selectionAdapter == null) {
@@ -3162,10 +3242,9 @@ define('select2/core',[
31623242

31633243
var ResultsAdapter = this.options.get('resultsAdapter');
31643244
this.results = new ResultsAdapter($element, this.options, this.data);
3165-
31663245
this.$results = this.results.render();
31673246

3168-
this.results.position(this.$results, $container);
3247+
this.results.position(this.$results, this.$dropdown);
31693248

31703249
// Bind events
31713250

dist/js/select2.amd.js

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,8 @@ define('select2/results',[
230230
this.$results.append($options);
231231
};
232232

233-
Results.prototype.position = function ($results, $container) {
234-
var $resultsContainer = $container.find('.select2-results');
233+
Results.prototype.position = function ($results, $dropdown) {
234+
var $resultsContainer = $dropdown.find('.select2-results');
235235
$resultsContainer.append($results);
236236
};
237237

@@ -2812,6 +2812,79 @@ define('select2/dropdown/infiniteScroll',[
28122812
return InfiniteScroll;
28132813
});
28142814

2815+
define('select2/dropdown/attachBody',[
2816+
2817+
], function () {
2818+
function AttachBody (decorated, $element, options) {
2819+
decorated.call(this, $element, options);
2820+
}
2821+
2822+
AttachBody.prototype.bind = function (decorated, container, $container) {
2823+
var self = this;
2824+
2825+
decorated.call(this, container, $container);
2826+
2827+
container.on('open', function () {
2828+
self._showDropdown();
2829+
});
2830+
2831+
container.on('close', function () {
2832+
self._hideDropdown();
2833+
});
2834+
2835+
this.$dropdownContainer.on('mousedown', function (evt) {
2836+
evt.stopPropagation();
2837+
});
2838+
};
2839+
2840+
AttachBody.prototype.position = function (decorated, $dropdown, $container) {
2841+
// Clone all of the container classes
2842+
$dropdown.attr('class', $container.attr('class'));
2843+
2844+
$dropdown.removeClass('select2');
2845+
$dropdown.addClass('select2-container--open');
2846+
2847+
$dropdown.css({
2848+
position: 'absolute'
2849+
});
2850+
2851+
$dropdown.width($container.outerWidth(false));
2852+
2853+
this.$container = $container;
2854+
};
2855+
2856+
AttachBody.prototype.render = function (decorated) {
2857+
var $container = $('<span></span>');
2858+
2859+
var $dropdown = decorated.call(this);
2860+
$container.append($dropdown);
2861+
2862+
this.$dropdownContainer = $container;
2863+
2864+
return $container;
2865+
};
2866+
2867+
AttachBody.prototype._hideDropdown = function (decorated) {
2868+
this.$dropdownContainer.detach();
2869+
};
2870+
2871+
AttachBody.prototype._positionDropdown = function () {
2872+
var css = this.$container.offset();
2873+
2874+
css.top += this.$container.outerHeight(true);
2875+
2876+
this.$dropdownContainer.css(css);
2877+
};
2878+
2879+
AttachBody.prototype._showDropdown = function (decorated) {
2880+
this.$dropdownContainer.appendTo(document.body);
2881+
2882+
this._positionDropdown();
2883+
};
2884+
2885+
return AttachBody;
2886+
});
2887+
28152888
define('select2/i18n/en',[],function () {
28162889
return {
28172890
errorLoading: function () {
@@ -2884,6 +2957,7 @@ define('select2/defaults',[
28842957
'./dropdown/search',
28852958
'./dropdown/hidePlaceholder',
28862959
'./dropdown/infiniteScroll',
2960+
'./dropdown/attachBody',
28872961

28882962
'./i18n/en'
28892963
], function ($, ResultsList,
@@ -2892,6 +2966,7 @@ define('select2/defaults',[
28922966
Utils, Translation, DIACRITICS,
28932967
SelectData, ArrayData, AjaxData, Tags, MinimumInputLength,
28942968
Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
2969+
AttachBody,
28952970
EnglishTranslation) {
28962971
function Defaults () {
28972972
this.reset();
@@ -2948,6 +3023,11 @@ define('select2/defaults',[
29483023

29493024
options.dropdownAdapter = SearchableDropdown;
29503025
}
3026+
3027+
options.dropdownAdapter = Utils.Decorate(
3028+
options.dropdownAdapter,
3029+
AttachBody
3030+
);
29513031
}
29523032

29533033
if (options.selectionAdapter == null) {
@@ -3162,10 +3242,9 @@ define('select2/core',[
31623242

31633243
var ResultsAdapter = this.options.get('resultsAdapter');
31643244
this.results = new ResultsAdapter($element, this.options, this.data);
3165-
31663245
this.$results = this.results.render();
31673246

3168-
this.results.position(this.$results, $container);
3247+
this.results.position(this.$results, this.$dropdown);
31693248

31703249
// Bind events
31713250

dist/js/select2.full.js

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9765,8 +9765,8 @@ define('select2/results',[
97659765
this.$results.append($options);
97669766
};
97679767

9768-
Results.prototype.position = function ($results, $container) {
9769-
var $resultsContainer = $container.find('.select2-results');
9768+
Results.prototype.position = function ($results, $dropdown) {
9769+
var $resultsContainer = $dropdown.find('.select2-results');
97709770
$resultsContainer.append($results);
97719771
};
97729772

@@ -12347,6 +12347,79 @@ define('select2/dropdown/infiniteScroll',[
1234712347
return InfiniteScroll;
1234812348
});
1234912349

12350+
define('select2/dropdown/attachBody',[
12351+
12352+
], function () {
12353+
function AttachBody (decorated, $element, options) {
12354+
decorated.call(this, $element, options);
12355+
}
12356+
12357+
AttachBody.prototype.bind = function (decorated, container, $container) {
12358+
var self = this;
12359+
12360+
decorated.call(this, container, $container);
12361+
12362+
container.on('open', function () {
12363+
self._showDropdown();
12364+
});
12365+
12366+
container.on('close', function () {
12367+
self._hideDropdown();
12368+
});
12369+
12370+
this.$dropdownContainer.on('mousedown', function (evt) {
12371+
evt.stopPropagation();
12372+
});
12373+
};
12374+
12375+
AttachBody.prototype.position = function (decorated, $dropdown, $container) {
12376+
// Clone all of the container classes
12377+
$dropdown.attr('class', $container.attr('class'));
12378+
12379+
$dropdown.removeClass('select2');
12380+
$dropdown.addClass('select2-container--open');
12381+
12382+
$dropdown.css({
12383+
position: 'absolute'
12384+
});
12385+
12386+
$dropdown.width($container.outerWidth(false));
12387+
12388+
this.$container = $container;
12389+
};
12390+
12391+
AttachBody.prototype.render = function (decorated) {
12392+
var $container = $('<span></span>');
12393+
12394+
var $dropdown = decorated.call(this);
12395+
$container.append($dropdown);
12396+
12397+
this.$dropdownContainer = $container;
12398+
12399+
return $container;
12400+
};
12401+
12402+
AttachBody.prototype._hideDropdown = function (decorated) {
12403+
this.$dropdownContainer.detach();
12404+
};
12405+
12406+
AttachBody.prototype._positionDropdown = function () {
12407+
var css = this.$container.offset();
12408+
12409+
css.top += this.$container.outerHeight(true);
12410+
12411+
this.$dropdownContainer.css(css);
12412+
};
12413+
12414+
AttachBody.prototype._showDropdown = function (decorated) {
12415+
this.$dropdownContainer.appendTo(document.body);
12416+
12417+
this._positionDropdown();
12418+
};
12419+
12420+
return AttachBody;
12421+
});
12422+
1235012423
define('select2/i18n/en',[],function () {
1235112424
return {
1235212425
errorLoading: function () {
@@ -12419,6 +12492,7 @@ define('select2/defaults',[
1241912492
'./dropdown/search',
1242012493
'./dropdown/hidePlaceholder',
1242112494
'./dropdown/infiniteScroll',
12495+
'./dropdown/attachBody',
1242212496

1242312497
'./i18n/en'
1242412498
], function ($, ResultsList,
@@ -12427,6 +12501,7 @@ define('select2/defaults',[
1242712501
Utils, Translation, DIACRITICS,
1242812502
SelectData, ArrayData, AjaxData, Tags, MinimumInputLength,
1242912503
Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
12504+
AttachBody,
1243012505
EnglishTranslation) {
1243112506
function Defaults () {
1243212507
this.reset();
@@ -12483,6 +12558,11 @@ define('select2/defaults',[
1248312558

1248412559
options.dropdownAdapter = SearchableDropdown;
1248512560
}
12561+
12562+
options.dropdownAdapter = Utils.Decorate(
12563+
options.dropdownAdapter,
12564+
AttachBody
12565+
);
1248612566
}
1248712567

1248812568
if (options.selectionAdapter == null) {
@@ -12697,10 +12777,9 @@ define('select2/core',[
1269712777

1269812778
var ResultsAdapter = this.options.get('resultsAdapter');
1269912779
this.results = new ResultsAdapter($element, this.options, this.data);
12700-
1270112780
this.$results = this.results.render();
1270212781

12703-
this.results.position(this.$results, $container);
12782+
this.results.position(this.$results, this.$dropdown);
1270412783

1270512784
// Bind events
1270612785

dist/js/select2.full.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)