¿Cómo puedo seleccionar con mayúsculas varias casillas de verificación como GMail?

86

En Gmail, el usuario puede hacer clic en una casilla de verificación en la lista de correo electrónico, mantener presionada la tecla Shift y seleccionar una segunda casilla de verificación. Luego, JavaScript seleccionará / deseleccionará las casillas de verificación que se encuentran entre las dos casillas de verificación.

Tengo curiosidad por saber cómo se hace esto. ¿Es esto JQuery o algún JavaScript básico (o complejo)?

Ascaloniano
fuente
4
Fácil búsqueda de lo esencial para la respuesta de @ BC. Gist.github.com/3784055
Andy Ray

Respuestas:

184

Escribí una demostración autónoma que usa 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>

ANTES DE CRISTO.
fuente
8
Puede llamar a slice en lugar de usar el bucle for. Se vería así: "$ ('. Chkbox'). Slice (min ..., max ... + 1) .attr ('check', lastChecked.checked)"
Matthew Crumley
10
La respuesta es poco convincente sin el complemento jquery abstraído. Así que aquí tienes gist.github.com/3784055
Andy Ray
3
No parece funcionar para hacer un shift-clic varias veces después de desmarcar algunas casillas sin shift-clic. jsfiddle.net/5fG5b
Greg Pettit
3
Eso es porque debería ser .prop('checked', no .attr('checked'. jsFiddle: jsfiddle.net/dn4jv9a5
Caitlin
3
@schnauss gracias, tienes razón y he actualizado la respuesta. En mi defensa, la respuesta original se escribió antes de que prop () estuviera disponible
BC.
36

Esto se hace a través de un javascript bastante simple.

Realizan un seguimiento de la identificación de la última casilla marcada y cuando se marca otra casilla de verificación, usan el atributo de evento shiftKey para ver si se mantuvo el turno al hacer clic en la casilla de verificación. Si es así, establecen la propiedad marcada de cada casilla de verificación entre las dos en verdadero.

Para determinar cuándo se marca una casilla, probablemente utilicen un evento onclick en las casillas de verificación

Ben S
fuente
2
Si lo desea, puede utilizar estas referencias de Mozilla Developer Network: atributo de evento shiftKey , propiedades del elemento de entrada , onclick .
PhoneixS
14

Recientemente, escribí un complemento de jQuery que proporciona esa característica y más.

Después de incluir el complemento, solo necesita inicializar el contexto de las casillas de verificación con el siguiente fragmento de código:

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

Aquí está el enlace a la documentación, demostración y descarga: http://rmariuzzo.github.io/checkboxes.js/

Rubens Mariuzzo
fuente
10

Parece que todas las respuestas que puedo encontrar en línea dependen completamente de jQuery para esto. JQuery agrega muy poca funcionalidad. Aquí hay una versión rápida que no requiere ningún marco:

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;
        });
    }
}

Y luego inicialízalo siempre que lo necesites:

allow_group_select_checkboxes('[id of a wrapper that contains the checkboxes]')
Curva
fuente
3

Bueno, la publicación es bastante antigua, pero aquí hay una solución que acabo de encontrar: jQuery Field Plug-In

Miguel
fuente
3

Obtuve esta solución de http://abcoder.com/javascript/jquery/simple-check-uncheck-all-jquery-function/ (ahora muerto) :

Código JavaScript y 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>

Adnan
fuente
El dominio de enlaces ya no está en uso.
Zaren Wienclaw
2

Inspirado por las excelentes respuestas proporcionadas, aquí hay una versión simple de JavaScript que se usa Array.prototypepara obligar a los nodelistas a usar funciones de matriz, en lugar de forbucles.

(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>

nudillos sangrientos
fuente
¿Cómo mejorar con la función deseleccionar?
RSH
2

Realmente me gustó el ejemplo de gyo y agregué un código para que funcione en todas las casillas de verificación con el mismo nombre.

También agregué un MutationObserver para que los eventos también se manejen en las casillas de verificación recién agregadas.

$(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>

Gerben Versluis
fuente
1

Tomé la versión jQuery de @BC. y lo transformó en una versión ES6, ya que el código en realidad está resolviendo el problema de manera bastante elegante, en caso de que alguien todavía se encuentre con esto ...

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;
  } ) );
}
Moritz Friedrich
fuente
1
  • Encontré la mejor solución que funciona tanto para seleccionar como para anular la selección de las casillas de verificación.

  • Utiliza un javascript y Jquery centrales.

$(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>

Yogesh Kapadne
fuente
0

Aquí también hay otra implementación similar a la selección múltiple de Outlook.

    <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];
            }
        }
    });
});

vasgen
fuente
0

Esta es la solución jquery que escribí y uso:

  • Todas las casillas de verificación tienen el mismo nombre de clase chksel
  • Para una selección individual más rápida, una clase llevará el orden denominado chksel_index
  • Además, cada uno checkboxtiene un atributo llamado rgque contiene el mismo índice

    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');}
    

    })

Dan
fuente
0

esta solución funciona para mí, también basada en ajax para DataTables https://jsfiddle.net/6ouhv7bw/4/

<table id="dataTable">

<tbody>
<tr>
<td><input type="checkbox"></td>
</tr>

<tr>
<td><input type="checkbox"></td>
</tr>

<tr>
<td><input type="checkbox"></td>
</tr>

<tr>
<td><input type="checkbox"></td>
</tr>
</tbody>
</table>

<script>
$(document).ready(function() {
   var $chkboxes = $('#dataTable');
var $range = '#dataTable tbody';
var $first = false;
var $indexWrapp = 'tr';
var lastChecked = null;
var $checkboxes = 'input[type="checkbox"]';

$chkboxes.on('click',$checkboxes,function(e) {

    if ($first===false) {

        lastChecked = $(this).closest($indexWrapp).index();
        lastCheckedInput = $(this).prop('checked');
        $first=true;
        return;
    }

    if (e.shiftKey) {

        var start = lastChecked;
        var end =  $(this).closest($indexWrapp).index();

       $( $range+' '+$indexWrapp).each(function() {
          $currIndex=$(this).index();
          if( $currIndex>=start && $currIndex<=end ){
              $(this).find($checkboxes).prop('checked', lastCheckedInput);
          }

       })
    }

     lastCheckedInput = $(this).prop('checked');
     lastChecked = $(this).closest($indexWrapp).index();
});
</script>
dazzafact
fuente