From d8d2aa5b03d1e7e9bbea2062288d751101bd701a Mon Sep 17 00:00:00 2001 From: Kevin Brown Date: Tue, 27 Aug 2019 22:13:41 -0400 Subject: [PATCH] Switch back to late binding the positioning handlers This is how it used to work pre-4.0.9, where the bound handlers for resizing and positioning the dropdown were bound at the first time that the dropdown was opened. This was changed for the 4.0.9 release as a part of 3f75227a693 where it was not clear why this late binding was happening, so it was removed to simplify the code. The late binding is necessary because the handlers within results for generating the results, and thus the content within the dropdown, are bound after the handlers for the dropdown are bound. This results in situations where the handlers for positioning the dropdown are bound before the dropdown is updated with content, resulting in the positioning being calculated for the old content and thus being incorrectly placed. By binding the positioning handlers after the dropdow is opened for the first time, we can ensure that the positioning handlers are bound after the result handlers, so we won't have to worry about this issue. The handlers are only bound once because they only need to perform all the calculations a single time. There is no need to keep checking all of the calculated styles more than we need to, so this is guarded by a flag which ensures it is only bound a single time per dropdown. Fixes #5619 Fixes #5620 --- src/js/select2/dropdown/attachBody.js | 66 +++++++++++++++++---------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/src/js/select2/dropdown/attachBody.js b/src/js/select2/dropdown/attachBody.js index 6cc279b7..acd8c1c8 100644 --- a/src/js/select2/dropdown/attachBody.js +++ b/src/js/select2/dropdown/attachBody.js @@ -16,6 +16,9 @@ define([ container.on('open', function () { self._showDropdown(); self._attachPositioningHandler(container); + + // Must bind after the results handlers to ensure correct sizing + self._bindContainerResultHandlers(container); }); container.on('close', function () { @@ -23,31 +26,6 @@ define([ self._detachPositioningHandler(container); }); - container.on('results:all', function () { - self._positionDropdown(); - self._resizeDropdown(); - }); - - container.on('results:append', function () { - self._positionDropdown(); - self._resizeDropdown(); - }); - - container.on('results:message', function () { - self._positionDropdown(); - self._resizeDropdown(); - }); - - container.on('select', function () { - self._positionDropdown(); - self._resizeDropdown(); - }); - - container.on('unselect', function () { - self._positionDropdown(); - self._resizeDropdown(); - }); - this.$dropdownContainer.on('mousedown', function (evt) { evt.stopPropagation(); }); @@ -89,6 +67,44 @@ define([ this.$dropdownContainer.detach(); }; + AttachBody.prototype._bindContainerResultHandlers = + function (decorated, container) { + + // These should only be bound once + if (this._containerResultsHandlersBound) { + return; + } + + var self = this; + + container.on('results:all', function () { + self._positionDropdown(); + self._resizeDropdown(); + }); + + container.on('results:append', function () { + self._positionDropdown(); + self._resizeDropdown(); + }); + + container.on('results:message', function () { + self._positionDropdown(); + self._resizeDropdown(); + }); + + container.on('select', function () { + self._positionDropdown(); + self._resizeDropdown(); + }); + + container.on('unselect', function () { + self._positionDropdown(); + self._resizeDropdown(); + }); + + this._containerResultsHandlersBound = true; + }; + AttachBody.prototype._attachPositioningHandler = function (decorated, container) { var self = this;