web-dev-qa-db-fra.com

Comment puis-je sélectionner plusieurs cases à cocher comme GMail?

Dans GMail, l'utilisateur peut cliquer sur une case à cocher dans la liste des e-mails, maintenir la touche Maj enfoncée et sélectionner une deuxième case à cocher. Le JavaScript sélectionnera/désélectionnera ensuite les cases à cocher situées entre les deux checboxes.

Je suis curieux de savoir comment cela se fait? S'agit-il de JQuery ou d'un JavaScript basique (ou complexe)?

81
Ascalonian

J'ai écrit une démo autonome qui utilise jquery:

$(document).ready(function() {
    var $chkboxes = $('.chkbox');
    var lastChecked = null;

    $chkboxes.click(function(e) {
        if (!lastChecked) {
            lastChecked = this;
            return;
        }

        if (e.shiftKey) {
            var start = $chkboxes.index(this);
            var end = $chkboxes.index(lastChecked);

            $chkboxes.slice(Math.min(start,end), Math.max(start,end)+ 1).prop('checked', lastChecked.checked);
        }

        lastChecked = this;
    });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<head>
</head>
<body>
    <input type="checkbox" id="id_chk1" class="chkbox" value="1" />Check 1<br/>
    <input type="checkbox" id="id_chk2" class="chkbox" value="2" />Check 2<br/>
    <input type="checkbox" id="id_chk3" class="chkbox" value="3" />Check 3<br/>
    <input type="checkbox" id="id_chk4" class="chkbox" value="4" />Check 4<br/>
    <input type="checkbox" id="id_chk5" class="chkbox" value="5" />Check 5<br/>
    <input type="checkbox" id="id_chk6" class="chkbox" value="6" />Check 6<br/>
    <input type="checkbox" id="id_chk7" class="chkbox" value="7" />Check 7<br/>
</body>
</html>
163
BC.

Cela se fait via un javascript assez simple.

Ils gardent la trace de l'id de la dernière case cochée et quand lorsqu'une autre case est cochée, ils utilisent attribut d'événement shiftKey pour voir si le décalage a été maintenu en cliquant sur la case. Si c'est le cas, ils définissent propriété vérifiée de chaque case à cocher entre les deux sur true.

Pour déterminer quand une case est cochée, ils utilisent probablement un événement onclick dans les cases à cocher

36
Ben S

Récemment, j'ai écrit un plugin jQuery qui fournit cette fonctionnalité et plus encore.

Après avoir inclus le plugin, il vous suffit d'initialiser le contexte des cases à cocher avec l'extrait de code suivant:

$('#table4').checkboxes({ range: true });

Voici le lien vers la documentation, la démo et le téléchargement: http://rmariuzzo.github.io/checkboxes.js/

14
Rubens Mariuzzo

Il semble que chaque réponse que je peux trouver en ligne dépend entièrement de jQuery pour cela. JQuery ajoute très peu de fonctionnalités. Voici une version rapide qui ne nécessite aucun framework:

function allow_group_select_checkboxes(checkbox_wrapper_id){
    var lastChecked = null;
    var checkboxes = document.querySelectorAll('#'+checkbox_wrapper_id+' input[type="checkbox"]');

    //I'm attaching an index attribute because it's easy, but you could do this other ways...
    for (var i=0;i<checkboxes.length;i++){
        checkboxes[i].setAttribute('data-index',i);
    }

    for (var i=0;i<checkboxes.length;i++){
        checkboxes[i].addEventListener("click",function(e){

            if(lastChecked && e.shiftKey) {
                var i = parseInt(lastChecked.getAttribute('data-index'));
                var j = parseInt(this.getAttribute('data-index'));
                var check_or_uncheck = this.checked;

                var low = i; var high=j;
                if (i>j){
                    var low = j; var high=i; 
                }

                for(var c=0;c<checkboxes.length;c++){
                    if (low <= c && c <=high){
                        checkboxes[c].checked = check_or_uncheck;
                    }   
                }
            } 
            lastChecked = this;
        });
    }
}

Et puis initialisez-le chaque fois que vous en avez besoin:

allow_group_select_checkboxes('[id of a wrapper that contains the checkboxes]')
6
Ben D

Vous avez cette solution de http://abcoder.com/javascript/jquery/simple-check-uncheck-all-jquery-function/ (maintenant mort) :

Code JavaScript et HTML

var NUM_BOXES = 10;

// last checkbox the user clicked
var last = -1;

function check(event) {
  // in IE, the event object is a property of the window object
  // in Mozilla, event object is passed to event handlers as a parameter
  if (!event) { event = window.event }
  var num = parseInt(/box\[(\d+)\]/.exec(this.name)[1]);
  if (event.shiftKey && last != -1) {
     var di = num > last ? 1 : -1;
     for (var i = last; i != num; i += di) {
        document.forms.boxes['box[' + i + ']'].checked = true;
     }
  }
  last = num;
}

function init() {
  for (var i = 0; i < NUM_BOXES; i++) {
    document.forms.boxes['box[' + i + ']'].onclick = check;
  }
}
<body onload="init()">
    <form name="boxes">
    <input name="box[0]" type="checkbox">
    <input name="box[1]" type="checkbox">
    <input name="box[2]" type="checkbox">
    <input name="box[3]" type="checkbox">
    <input name="box[4]" type="checkbox">
    <input name="box[5]" type="checkbox">
    <input name="box[6]" type="checkbox">
    <input name="box[7]" type="checkbox">
    <input name="box[8]" type="checkbox">
    <input name="box[9]" type="checkbox">
    </form>
</body>
3
Adnan

Eh bien, le message est assez ancien, mais voici une solution que je viens de rencontrer: plug-in de champ jQuery

3
Mike

Inspiré par les bonnes réponses fournies, voici une version JavaScript simple utilisant Array.prototype pour forcer les nodelists à utiliser les fonctions de tableau, plutôt que les boucles for.

(function () { // encapsulating variables with IIFE
  var lastcheck = null // no checkboxes clicked yet

  // get desired checkboxes
  var checkboxes = document.querySelectorAll('div.itemslist input[type=checkbox]')

  // loop over checkboxes to add event listener
  Array.prototype.forEach.call(checkboxes, function (cbx, idx) {
    cbx.addEventListener('click', function (evt) {

      // test for shift key, not first checkbox, and not same checkbox
      if ( evt.shiftKey && null !== lastcheck && idx !== lastcheck ) {

        // get range of checks between last-checkbox and shift-checkbox
        // Math.min/max does our sorting for us
        Array.prototype.slice.call(checkboxes, Math.min(lastcheck, idx), Math.max(lastcheck, idx))
          // and loop over each
          .forEach(function (ccbx) {
            ccbx.checked = true
        })
      }
      lastcheck = idx // set this checkbox as last-checked for later
    })
  })
}())
<div class="itemslist">
  <input type="checkbox" name="one"   value="1">
  <input type="checkbox" name="two"   value="2">
  <input type="checkbox" name="three" value="3">
  <input type="checkbox" name="four"  value="4">
  <input type="checkbox" name="five"  value="5">
</div>
1
bloodyKnuckles

J'ai vraiment aimé l'exemple de gyo et ajouté du code pour qu'il fonctionne sur toutes les cases à cocher portant le même nom.

J'ai également ajouté un MutationObserver pour que les événements soient également gérés sur les cases à cocher nouvellement ajoutées.

$(document).ready(function() {
    var previouslyClicked = {};

    var rangeEventHandler = function(event) {
        if (event.shiftKey && previouslyClicked[this.name] && this != previouslyClicked[this.name]) {
            var $checkboxes = $('input[type=checkbox][name='+this.name+']').filter(':visible');
            var start = $checkboxes.index( this );
            var end = $checkboxes.index( previouslyClicked[this.name] );
//              console.log('range', start, end, this, previouslyClicked[this.name]);
            $checkboxes.slice(Math.min(start,end), Math.max(start,end)+ 1).prop('checked', previouslyClicked[this.name].checked);
        } else {
            previouslyClicked[this.name] = this;
        }
    };

    if ("MutationObserver" in window) { // https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver/MutationObserver to refresh on new checkboxes
        var mutationCallback = function(mutationList, observer) {
            mutationList.forEach((mutation) => {
                mutation.addedNodes.forEach((node) => {
                    if (node.nodeName == 'INPUT' && node.type == 'checkbox') {
                        $(node).on('click.selectRange', rangeEventHandler);
                    }
                });
            });
        };

        var observer = new MutationObserver(mutationCallback);
        observer.observe(document, {
            childList: true,
            attributes: false,  // since name is dynamically read
            subtree: true
        });
    }

    $('input[type=checkbox][name]').on('click.selectRange', rangeEventHandler);
});
<html>
<head>
</head>
<body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <div>
    First:
    <input type="checkbox" name="first">
    <input type="checkbox" name="first">
    <input type="checkbox" name="first">
    <input type="checkbox" name="first">
    <input type="checkbox" name="first">
  </div>
  <div>
    Second:
    <input type="checkbox" name="second">
    <input type="checkbox" name="second">
    <input type="checkbox" name="second">
    <input type="checkbox" name="second">
    <input type="checkbox" name="second">
  </div>
</body>
</html>
1
Gerben Versluis
  • A trouvé la meilleure solution qui fonctionne pour les cases à cocher sélectionnées et désélectionnées.

  • Utilise un noyau javascript et Jquery.

$(document).ready(function() {
    var $chkboxes = $('.chkbox');
    var lastChecked = null;

    $chkboxes.click(function(e) {
        if(!lastChecked) {
            lastChecked = this;
            return;
        }

        if(e.shiftKey) {
            var start = $chkboxes.index(this);
            var end = $chkboxes.index(lastChecked);

            $chkboxes.slice(Math.min(start,end), Math.max(start,end)+ 1).prop('checked', e.target.checked);

        }

        lastChecked = this;
    });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
    <head>
    </head>
    <body>
        <input type="checkbox" id="id_chk1" class="chkbox" value="1" />Check 1<br/>
        <input type="checkbox" id="id_chk2" class="chkbox" value="2" />Check 2<br/>
        <input type="checkbox" id="id_chk3" class="chkbox" value="3" />Check 3<br/>
        <input type="checkbox" id="id_chk4" class="chkbox" value="4" />Check 4<br/>
        <input type="checkbox" id="id_chk5" class="chkbox" value="5" />Check 5<br/>
        <input type="checkbox" id="id_chk6" class="chkbox" value="6" />Check 6<br/>
        <input type="checkbox" id="id_chk7" class="chkbox" value="7" />Check 7<br/>
    </body>
</html>
1
Yogesh Kapadne

J'ai pris la version jQuery de @BC. et l'a transformé en une version ES6, car le code résout en fait assez élégamment le problème, au cas où quelqu'un tomberait encore dessus ...

function enableGroupSelection( selector ) {
  let lastChecked = null;
  const checkboxes = Array.from( document.querySelectorAll( selector ) );

  checkboxes.forEach( checkbox => checkbox.addEventListener( 'click', event => {
    if ( !lastChecked ) {
      lastChecked = checkbox;

      return;
    }

    if ( event.shiftKey ) {
      const start = checkboxes.indexOf( checkbox );
      const end   = checkboxes.indexOf( lastChecked );

      checkboxes
        .slice( Math.min( start, end ), Math.max( start, end ) + 1 )
        .forEach( checkbox => checkbox.checked = lastChecked.checked );
    }

    lastChecked = checkbox;
  } ) );
}
0
Moritz Friedrich

C'est une solution jquery que j'ai écrite et utilisée:

  • Toutes les cases à cocher ont la même classe nommée chksel
  • Pour une sélection individuelle plus rapide, une classe portera l'ordre nommé chksel_index
  • De plus, chaque checkbox possède un attribut nommé rg qui contient le même index

    var chksel_last=-1;
    $('.chksel').click(function(ev){
       if(ev.shiftKey){var i=0;
          if(chksel_last >=0){
            if($(this).attr('rg') >= chksel_last){
             for(i=chksel_last;i<=$(this).attr('rg');i++){$('.chksel_'+i).attr('checked','true')}}
            if($(this).attr('rg') <= chksel_last){for(i=$(this).attr('rg');i<=chksel_last;i++){$('.chksel_'+i).attr('checked','true')}}
          }  
          chksel_last=$(this).attr('rg');
       }else{chksel_last=$(this).attr('rg');}
    

    })

0
Dan

Voici également une autre implémentation similaire à la sélection multiple d'Outlooks.

    <script type="text/javascript">

function inRange(x, range)
{
    return (x >= range[0] && x <= range[1]);
}

$(document).ready(function() {
    var $chkboxes = $('.chkbox');
    var firstClick = 1;
    var lastClick = null;
    var range = [];

    $chkboxes.click(function(e) {
        if(!e.shiftKey && !e.ctrlKey) {

            $('#index-' + firstClick).prop('checked', false);

            firstClick = $chkboxes.index(this) + 1;

            if (firstClick !== null && firstClick !== ($chkboxes.index(this)+1)) {
                $('#index-' + firstClick).prop('checked', true);
            }
        } else if (e.shiftKey) {
            lastClick = $chkboxes.index(this) + 1;
            if ((firstClick < lastClick) && !inRange(lastClick, range)) {
                for (i = firstClick; i < lastClick; i++) {
                    $('#index-' + i).prop('checked', true);
                }
                range = [firstClick, lastClick];
            } else if ((firstClick > lastClick) && !inRange(lastClick, range)) {
                for (i = lastClick; i < firstClick; i++) {
                    $('#index-' + i).prop('checked', true);
                }
                range = [lastClick, firstClick];
            } else if ((firstClick < lastClick) && inRange(lastClick, range)) {
                for (i = 1; i < 100; i++) {
                    $('#index-' + i).prop('checked', false);
                }

                for (i = firstClick; i < lastClick; i++) {
                    $('#index-' + i).prop('checked', true);
                }
                range = [firstClick, lastClick];
            }else if ((firstClick > lastClick) && inRange(lastClick, range)) {
                for (i = 1; i < 100; i++) {
                    $('#index-' + i).prop('checked', false);
                }

                for (i = lastClick; i < firstClick; i++) {
                    $('#index-' + i).prop('checked', true);
                }
                range = [lastClick, firstClick];
            }
        }
    });
});
0
vasgen