Comme dans le titre, y a-t-il un moyen de forcer select2 à toujours créer un menu déroulant au lieu d'un menu déroulant?
Il semble également que du javascript soit à l'origine du retournement lorsque vous faites défiler la liste déroulante au-dessus, en ajoutant une nouvelle classe CSS "select2-drop-above", ou les deux.
EDIT: J'aurais dû préciser que je récupère la bibliothèque via select2-Rails. J'espère qu'il existe un moyen de contourner ce problème en n'emportant pas toute la select2 lib en moi-même et en modifiant directement le fichier select2.js.
La modification du plug-in n'est pas préférable, comme vous l'avez mentionné. J'ai eu un problème similaire et je ne pouvais pas trouver un moyen d'utiliser les options select2
pour forcer la liste déroulante à rester en dessous. La solution que j'ai finalement trouvée est la suivante:
$("#mySelect2").select2({ ...options... })
.on('select2-open', function() {
// however much room you determine you need to prevent jumping
var requireHeight = 600;
var viewportBottom = $(window).scrollTop() + $(window).height();
// figure out if we need to make changes
if (viewportBottom < requireHeight)
{
// determine how much padding we should add (via marginBottom)
var marginBottom = requireHeight - viewportBottom;
// adding padding so we can scroll down
$(".aLwrElmntOrCntntWrppr").css("marginBottom", marginBottom + "px");
// animate to just above the select2, now with plenty of room below
$('html, body').animate({
scrollTop: $("#mySelect2").offset().top - 10
}, 1000);
}
});
Ce code détermine s'il y a suffisamment de place pour placer la liste déroulante au bas de la liste et, dans le cas contraire, la crée en ajoutant une marge inférieure à un élément de la page. Il se place ensuite juste au-dessus du select2 afin que le menu déroulant ne bascule pas.
Vous pouvez simplement éditer select2.js
Où il est dit
enoughRoomBelow = dropTop + dropHeight <= viewportBottom,
enoughRoomAbove = (offset.top - dropHeight) >= this.body().scrollTop(),
il suffit de changer pour
enoughRoomBelow = true,
enoughRoomAbove = false,
Comme la modification du code source n’est pas une option et que l’ajout d’un hook à l’événement select2:open
n’est pas très élégant, en particulier lorsque vous avez plusieurs instances de select2 dans la même page, j’ai écrit une petite extension pour le plugin Select2
.
Mon implémentation est inspirée par un RP du référentiel du plugin ( https://github.com/select2/select2/pull/4618 ) qui n'a pas encore été fusionné.
Fondamentalement, le code suivant remplace la fonction de plug-in d'origine qui gère le positionnement de la liste déroulante et ajoute une nouvelle option (dropdownPosition
) pour forcer le positionnement de la liste déroulante au-dessus/en dessous.
La nouvelle option dropdownPosition
peut prendre les valeurs suivantes: - below
- le menu déroulant est toujours affiché en bas de l'entrée; - above
- le menu déroulant est toujours affiché en haut de l'entrée; - auto
(par défaut) - il utilise l'ancien comportement.
Insérez simplement le code suivant après le fichier select2.js
:
(function($) {
var Defaults = $.fn.select2.AMD.require('select2/defaults');
$.extend(Defaults.defaults, {
dropdownPosition: 'auto'
});
var AttachBody = $.fn.select2.AMD.require('select2/dropdown/attachBody');
var _positionDropdown = AttachBody.prototype._positionDropdown;
AttachBody.prototype._positionDropdown = function() {
var $window = $(window);
var isCurrentlyAbove = this.$dropdown.hasClass('select2-dropdown--above');
var isCurrentlyBelow = this.$dropdown.hasClass('select2-dropdown--below');
var newDirection = null;
var offset = this.$container.offset();
offset.bottom = offset.top + this.$container.outerHeight(false);
var container = {
height: this.$container.outerHeight(false)
};
container.top = offset.top;
container.bottom = offset.top + container.height;
var dropdown = {
height: this.$dropdown.outerHeight(false)
};
var viewport = {
top: $window.scrollTop(),
bottom: $window.scrollTop() + $window.height()
};
var enoughRoomAbove = viewport.top < (offset.top - dropdown.height);
var enoughRoomBelow = viewport.bottom > (offset.bottom + dropdown.height);
var css = {
left: offset.left,
top: container.bottom
};
// Determine what the parent element is to use for calciulating the offset
var $offsetParent = this.$dropdownParent;
// For statically positoned elements, we need to get the element
// that is determining the offset
if ($offsetParent.css('position') === 'static') {
$offsetParent = $offsetParent.offsetParent();
}
var parentOffset = $offsetParent.offset();
css.top -= parentOffset.top
css.left -= parentOffset.left;
var dropdownPositionOption = this.options.get('dropdownPosition');
if (dropdownPositionOption === 'above' || dropdownPositionOption === 'below') {
newDirection = dropdownPositionOption;
} else {
if (!isCurrentlyAbove && !isCurrentlyBelow) {
newDirection = 'below';
}
if (!enoughRoomBelow && enoughRoomAbove && !isCurrentlyAbove) {
newDirection = 'above';
} else if (!enoughRoomAbove && enoughRoomBelow && isCurrentlyAbove) {
newDirection = 'below';
}
}
if (newDirection == 'above' ||
(isCurrentlyAbove && newDirection !== 'below')) {
css.top = container.top - parentOffset.top - dropdown.height;
}
if (newDirection != null) {
this.$dropdown
.removeClass('select2-dropdown--below select2-dropdown--above')
.addClass('select2-dropdown--' + newDirection);
this.$container
.removeClass('select2-container--below select2-container--above')
.addClass('select2-container--' + newDirection);
}
this.$dropdownContainer.css(css);
};
})(window.jQuery);
L'initialisation du plugin avec comme suit:
$(document).ready(function() {
$(".select-el").select2({
dropdownPosition: 'below'
});
});
Violon ici: https://jsfiddle.net/byxj73ov/
Dépôt Github: https://github.com/andreivictor/select2-dropdownPosition
Vous pouvez le faire en écrasant CSS comme suit:
.select-dropdown {
position: static;
}
.select-dropdown .select-dropdown--above {
margin-top: 336px;
}
J'avais l'habitude de trouver une solution plus simple et plus rapide pour cela:
$("select").select2({
// Options
}).on('select2:open',function(){
$('.select2-dropdown--above').attr('id','fix');
$('#fix').removeClass('select2-dropdown--above');
$('#fix').addClass('select2-dropdown--below');
});
C'est simple, il suffit de changer le .select2-dropdown - au-dessus de pour .select2-dropdown - au-dessous de dans l'événement opening (select2: open).
Cela ne fonctionnera que dans le cadre de l'événement et il pourrait exister de nombreuses autres manières de l'exécuter en changeant simplement de classe lorsque la sélection est ouverte.
ps. Cela ne fonctionnera pas si vous essayez de renseigner votre sélection en utilisant jquery.
Mise à jour de la réponse de [shanabus]
jQuery("#line_item").select2({
formatResult: Invoice.formatLineItem
})
.on('select2-open', function() {
// however much room you determine you need to prevent jumping
var requireHeight = $("#select2-drop").height()+10;
var viewportBottom = $(window).height() - $("#line_item").offset().top;
// figure out if we need to make changes
if (viewportBottom < requireHeight)
{
// determine how much padding we should add (via marginBottom)
var marginBottom = requireHeight - viewportBottom;
// adding padding so we can scroll down
$(".aLwrElmntOrCntntWrppr").css("marginBottom", marginBottom + "px");
// animate to just above the select2, now with plenty of room below
$('html, body').animate({
scrollTop: $("#select2-drop").offset().top - 10
}, 1000);
}
});
.select2-dropdown--above { bottom: auto !important; }