web-dev-qa-db-fra.com

Empêcher select2 de basculer la liste déroulante vers le haut

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.

18
kjb

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.

3
shanabus

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,
23
kei

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

10
andreivictor

Vous pouvez le faire en écrasant CSS comme suit: 

.select-dropdown {
  position: static;
}
.select-dropdown .select-dropdown--above {
      margin-top: 336px;
}
5
Aivus

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.

3
user4542733

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);
            }
        });
1
aWebDeveloper
.select2-dropdown--above { bottom: auto !important; }
0
Tiberiu Raducea