JavaScript/Multi-selection

Multiple check boxes in a form can be checked with the help of JavaScript. The interface of the demonstration has buttons for each selection method: "Select all", "Select none", and "Invert selection", which are self-explanatory. In addition, there is "Select range", which selects the range between the first and last checked box, and "Zebra select", which alternately selects ranges of check boxes. This means that if the boxes 2, 6, 10, and 14 are checked, the boxes 2 to 6 and 10 to 14 will be selected, and 7, 8, and 9 are not selected.

A range of boxes can also be selected by holding ↑ Shift and clicking on a box.

The code below contains the selection algorithms, referred to by their function names.

<!DOCTYPE html>
<html lang="en">
<head>
	<title>Multi-selection in JavaScript</title>
	<meta name="author" content="Elominius from Wikiversity">

	<!-- the viewport meta tag magnifies the page on mobile browsers to make it useable there -->
	<meta name="viewport" content="initial-scale=1.0, user-scalable=yes">

<style type="text/css">
body {
/* dark theme */ background-color:#343; color:#ddd;
/* modern font pack */ font-family: ubuntu,'noto sans','open sans', calibri, 'segoe ui', 'trebuchet ms', arial, helvetica, verdana, tahoma, 'Bitstream Vera Sans', 'sans-serif';
}
a { color: lightblue; }

/* prevent buttons from sticking to each other */
button { margin-top: 1em; }

/* Initially grey out containers that are only meant to be shown with JavaScript activated, in order to provide a preview. */
JS_show { opacity:0.2; pointer-events:none; } 
	/* for completely hiding it if preferred, use: JS_show { display: none; } */

</style>

</head>

<body>
<JS_show>
<button class="range_selector" onclick="select_all();">Select all</button>
<button class="range_selector" onclick="select_none();">Select none</button>
<button class="range_selector" onclick="invert_selection();">Invert selection</button>
	<!-- This checks all unchecked boxes and unchecks all checked boxes.  -->
&nbsp;&nbsp;
<button class="range_selector" onclick="select_range();">Select range</button>
<button class="range_selector" onclick="zebra_select();">Zebra select</button>
<!--
"Select range" does select all items between the first and last selected one. First known to be used in the early 2010s by ES File Explorer, a file manager for the Android mobile operating system.

"Zebra select" alternately selects ranges of checked boxes. This allows selecting multiple ranges of items.
-->
<br />

<form style="column-width:250px">
  <!-- Note: the "id", "name", and "value" parameters have no effect on the behaviour of the buttons. -->
  <input type="checkbox" id="item1" name="item1" value="1">
  <label for="item1">Item 1</label><br>
  <input type="checkbox" id="item2" name="item2" value="2">
  <label for="item2">Item 2</label><br>
  <input type="checkbox" id="item3" name="item3" value="3">
  <label for="item3">Item 3</label><br>
  <input type="checkbox" id="item4" name="item4" value="4">
  <label for="item4">Item 4</label><br>
  <input type="checkbox" id="item5" name="item5" value="5">
  <label for="item5">Item 5</label><br>
  <input type="checkbox" id="item6" name="item6" value="6">
  <label for="item6">Item 6</label><br>
  <input type="checkbox" id="item7" name="item7" value="7">
  <label for="item7">Item 7</label><br>
  <input type="checkbox" id="item8" name="item8" value="8">
  <label for="item8">Item 8</label><br>
  <input type="checkbox" id="item9" name="item9" value="9">
  <label for="item9">Item 9</label><br>
  <input type="checkbox" id="item10" name="item10" value="10">
  <label for="item10">Item 10</label><br>
  <input type="checkbox" id="item11" name="item11" value="11">
  <label for="item11">Item 11</label><br>
  <input type="checkbox" id="item12" name="item12" value="12">
  <label for="item12">Item 12</label><br>
</form>
</JS_show>

<noscript>
	<!-- This guidance is shown if JavaScript is not available in the user's browser. -->
	<h2>JavaScript is unavailable</h2>
	<p>To mass-select check boxes, please activate JavaScript or use a web browser that supports it.</p>
	<p>If you are seeing this error in the Android HTML viewer, enter the path to this HTML file into the address bar of the web browser.</p>
</noscript>

<footer>
<hr />
<div id="credit" style="font-size:small;">Originally created by <a href="https://en.wikiversity.org/wiki/User:Elominius">Elominius from Wikiversity</a>. Licensed under <a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-by-SA 3.0</a>.</div>
</footer>

<script type="text/javascript">
	// unhides "JS_show" containers
if ( document.getElementsByTagName("JS_show")[0] ) /* check if JS_show containers exist */ {
	for (
		var count=0; // initiate counter
		count < document.getElementsByTagName("JS_show").length; // count JS_show containers
		count++ // count up. Same as count+=1 and count=count+1.
	) {
		// restore opacity and/or unhide by overriding CSS at the top using inline CSS:
		document.getElementsByTagName("JS_show")[count].setAttribute("style","display:block; opacity:1; pointer-events:all;");
	}
}

var count=0; // Declaring variable for "for" loops to defeat JS Hint error; no functional difference.

var current_form=document.getElementsByTagName("form")[0];
var current_boxes=current_form.getElementsByTagName("input");
number_of_boxes=document.getElementsByTagName("form")[0].getElementsByTagName("input").length-1; // correcting off-by-one error since length is shifted up by one.

var first_checked_box=0,
	 last_checked_box=0,
	 current_box=0;


// == dependencies ==

function find_first_checked() {
	// find first checked box; prevent overrunning the last box and causing exception if none selected.
	first_checked_box=0;
	while (! current_boxes[first_checked_box].checked && first_checked_box < number_of_boxes) {
		first_checked_box++;
	}
	return first_checked_box;
}


function find_last_checked() {
	// find last checked box by counting down from end
	last_checked_box=number_of_boxes;
	while (! current_boxes[last_checked_box].checked && last_checked_box > 0 ) {
		last_checked_box--;
	}
	return last_checked_box;
}

// == main functions ==

function select_range() {

// find out the first and last checked boxes
first_checked_box=find_first_checked();
last_checked_box=find_last_checked();

current_box=current_boxes[first_checked_box+1];
for ( 
	count=first_checked_box;
	current_box != current_boxes[last_checked_box];
	count++
) {
	current_box = current_boxes[count];
	current_box.checked = true;
}

}


// select every box
function select_all() {
	for (
		count=0;
		count < number_of_boxes+1;
		count++
	) { current_boxes[count].checked=true; }
}

// deselect every box
function select_none() {
	for (
		count=0;
		count < number_of_boxes+1;
		count++
	) { current_boxes[count].checked=false; }
}

// deselect selected boxes, select unselected boxes
function invert_selection() {
	for (
		count=0;
		count < number_of_boxes+1;
		count++
	) {
		if (current_boxes[count].checked) { 
		 current_boxes[count].checked=false;
		} else {
		 current_boxes[count].checked=true;
		}
	}
}

function zebra_select() {
var zebra_select_next=false; // reset to "false"
	for (
		count=0;
		count < number_of_boxes+1;
		count++
	) {
		/* switch selection mode */
		// check if box after current one exists to prevent exception from occuring
		while (current_boxes[count+1] && current_boxes[count].checked && count < number_of_boxes) {
			zebra_select_next ? zebra_select_next=false : zebra_select_next=true;
			count++; // skip over already selected boxes
		}
		current_boxes[count].checked=zebra_select_next;
	}
}

// implementing range selection by holding the "Shift" key
current_form.onclick = function(ev) {
	if(ev.shiftKey) {
		var start = find_first_checked();
		var stop = find_last_checked();
		if(start < stop)
		{
			for(count = start; count < stop; count++)
			{
				current_boxes[count].checked=true;
			}
		}
		else
		{
			for(count = start; count > stop; count--)
			{
				current_boxes[count].checked=true;
			}
		}
	}
};
</script>

</body>

</html>