-
Notifications
You must be signed in to change notification settings - Fork 20
/
maximize-select2-height.js
154 lines (136 loc) · 6.69 KB
/
maximize-select2-height.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// maximize-select2-height v1.0.4
// (c) Panorama Education 2020
// MIT License
// This jQuery/Select2 plugin expands a Select2 dropdown to take up as much
// height as possible given its position on the page and the current viewport
// size. The plugin correctly handles:
// - Dynamic window resizing.
// - The effects of scroll bars on the viewport.
// - Select2 rendering dropdowns both upwards and downwards.
// NOTE: The original <select> element that is $().select2()'d *must* have a
// unique ID for this code to work. (Ex: <select id="my-unique-id"></select>)
(function ($) {
"use strict";
// We can find these elements now, since the properties we check on them are
// all via methods that are recalculated each time.
var $window = $(window);
var $document = $(document);
// @param {Object} options The options object passed in when this plugin is
// initialized
// @param {Boolean} dropdownDownwards True iff the dropdown is rendered
// downwards (Select2 sometimes renders the options upwards to better fit on
// a page)
// @return {Object} The options passed in, combined with defaults. Keys are:
// - cushion: The number of pixels between the edge of the dropdown and the
// edge of the viewable window. [Default: 10, except when a
// horizontal scroll bar would interfere, in which case it's 30.]
// NOTE: If a value is passed in, no adjustments for possible
// scroll bars are made.
var settings = function (options, dropdownDownwards) {
return $.extend({
cushion: (
dropdownDownwards && $document.width() > $window.width()
) ? 30 : 10
}, options);
};
// @param {String} id The DOM element ID for the original <select> node
// @param {jQuery object} $select2Results The DOM element with class
// "select2-results"
// @param {jQuery object} $grandparent The grandparent object of the
// $select2Results object
// @param {Object} options The options object passed in when this plugin is
// initialized
// @param {Boolean} dropdownDownwards True iff the dropdown is rendered
// downwards (Select2 sometimes renders the options upwards to better fit on
// a page)
// @return {Number} the maximum height of the Select2 results box to display
var computeMaxHeight = function (
$select, $select2Results, $grandparent, options, dropdownDownwards
) {
var height;
var resultsBoxMiscellaniaHeight;
var widgetBoxOffset;
if (dropdownDownwards) {
// When the dropdown appears downwards, the formula is:
// visible window size
// + out-of-window pixels we've scrolled past
// - size of content (including offscreen content) above results box
// ------------------------------------------
// total height available to us
// innerHeight is more accurate across browsers than $(window).height(),
// but clientHeight works better than innerHeight on mobile browsers.
height = window.document.documentElement.clientHeight +
$window.scrollTop() -
$select2Results.offset().top;
} else {
// When the dropdown appears upwards, the formula is:
// vertical position of the widget (clickable) dropdown box
// - out-of-window pixels we've scrolled past
// - height of the search box and other content above the actual results
// but in the results box
// ------------------------------------------
// total height available to us
// Compute the global vertical offset of the widget box (the one with the
// downward arrow that the user clicks on to expose options).
widgetBoxOffset = $select.offset().top;
// Compute the height, if any, of search box and other content in the
// results box but not part of the results.
resultsBoxMiscellaniaHeight = $grandparent.height() -
$select2Results.height();
height = widgetBoxOffset -
$window.scrollTop() -
resultsBoxMiscellaniaHeight;
}
// Leave a little cushion to prevent the dropdown from
// rendering off the edge of the viewport.
return height - settings(options, dropdownDownwards).cushion;
};
// Call on a jQuery Select2 element to maximize the height of the dropdown
// every time it is opened.
// @param {Object} options The options object passed in when this plugin is
// initialized
$.fn.maximizeSelect2Height = function (options) {
return this.each(function (_, el) {
var $el = $(el);
// Each time the Select2 is opened, resize it to take up as much vertical
// space as possible given its position and the current viewport size.
$el.on("select2:open", function () {
// We have to put this code block inside a timeout because we determine
// whether the dropdown is rendered upwards or downwards via a hack that
// looks at the CSS classes, and these aren't set until Select2 has a
// chance to render the box, which occurs after this event fires.
// The alternative solution that avoids using a timeout would be to
// directly modify the document's stylesheets (instead of the styles for
// individual elements), but that is both ugly/dangerous and actually
// impossible for us because we need to modify the styles of a parent
// node of a given DOM node when the parent has no unique ID, which CSS
// doesn't have the ability to do.
setTimeout(function () {
var $select2Results = $("#select2-" + el.id + "-results");
var $parent = $select2Results.parent();
var $grandparent = $parent.parent();
var dropdownDownwards = $grandparent
.hasClass("select2-dropdown--below");
var maxHeight = computeMaxHeight(
$el,
$select2Results,
$grandparent,
options,
dropdownDownwards
);
// Set the max height of the relevant DOM elements. We use max-height
// instead of height directly to correctly handle cases in which there
// are only a few elements (we don't want a giant empty dropdown box).
$parent.css("max-height", maxHeight);
$select2Results.css("max-height", maxHeight);
// Select2 corrects the positioning of the results box on scroll, so
// we trigger that event here to let it auto-correct. This is done for
// the case where the dropdown appears upwards; we adjust its max
// height but we also want to move it up further, lest it cover up the
// initial dropdown box.
$(document).trigger("scroll");
});
});
});
};
})(jQuery);