Nitish Dayal, Software & Applications Developer - Contact
Last Commit Date: May 12, 2017
We're given an HTML page that displays a collection of input
HTML elements
of type checkbox
, each followed by a p
HTML element that will have it's
text contents striked through when an input is checked. Write the necessary
JavaScript code to allow a user to select multiple items while holding down
the shift key.
The answer key provided is flawed; open it up, hold down shift, and select the first item. All of the inputs become checked off! That's not good. So, once again, my answer & this guide will solve this challenge in a way that differs from the provided solution. Following this guide will allow the user to select OR unselect multiple items from the list while holding the shift key.
Number each of the input
elements by giving them an id
property that starts
at 0 and increments by 1. Declare a const
and define it as a reference to all
input elements that are children of the .inbox
class. Declare a let
variable
that will reference the last input item that was selected (for now, we will just
declare the variable, and we'll define it at a later point). Iterate through the
NodeList (current value of the const
we just declared) and attach an
event listener to each checkbox that will call upon a yet-to-be-defined event handler
function on a 'click' event.
The event handler function will check to see if the 'shift' key was held down for this
event, and if there is a previously selected checkbox. If both conditions are true,
we'll set the checked
property of the function context (the target, or this
argument, of the function) to reflect the checked
property of the last selected input.
Then we'll declare another const
and define it as an array from derived from the
NodeList item that contains the input
elements BETWEEN the input
that is the
function context and the previously selected input
. Iterate through this array and
set the checked
property of every array element to match the checked
property of
the function context. Finally, we'll define the let
variable responsible for referencing
the last selected input
as the function context (this
).
Steps:
-
HTML
-
Number each
input
element by giving them anid
property, starting at 0.<input type="checkbox" id="0"> ... <input type="checkbox" id="1"> ... <input type="checkbox" id="2">
-
-
JavaScript
-
Declare an immediately invoked anonymous function that will contain the rest of the JavaScript code, so as to avoid polluting the global namespace.
-
Declare & define a
const
variable as a reference to everyinput
element that is a child of theinbox
class.const checkBoxes = document.querySelectorAll('.inbox input[type="checkbox"]');
-
Declare a
let
variable that will be defined later as the lastinput
that was selected.let lastSelected;
-
Iterate through the NodeList and add an event listener to each element that will call upon a yet-to-be-defined event handler function on a click event.
// The name of the function to be used as the event handler is your choice. checkBoxes.forEach( // In this example, the event handler function is called 'multiChecker' checkbox => checkbox.addEventListener('click', multiChecker) );
-
Declare the event handler function and allow it accept one parameter, the event:
-
Check if the
let
variable declared in step 2 has been defined & theshift
key was pressed when this event occured. -
In the body of the conditional statement, set the
checked
property of the function context to be thechecked
property of the lastinput
that was selected.if (lastSelected && e.shiftKey) { this.checked = lastSelected.checked; /* ... */ }
-
Declare two
let
variables that will be used at a later point to slice away all theinput
elements that aren't between the last selectedinput
and theinput
element that is the function context./* Still in the body of the conditional statement */ // We can declare multiple variables in one go by separating them with commas let startIdx, endIdx;
-
If the
ID
property of the function context is greater than theID
property of the last selectedinput
, set the firstlet
variable as theID
property of the last selectedinput
and set the secondlet variable
as theID
property of theinput
that is the function context. Otherwise, flip 'em!/* Still in the body of the conditional statement */ /* There are a few ways to handle this requirement. I've provided examples * that showcase using ternary operators + destructuring assignments, and * using a standard conditional operator + variable assignments. */ // Using a ternary operator & destructuring assignment this.id > lastSelected.id ? [startIdx, endIdx] = [lastSelected.id, this.id] : [startIdx, endIdx] = [this.id, lastSelected.id]; // Using standard conditional operators if (this.id > lastSelected.id) { startIdx = lastSelected.id; endIdx = this.id; } else { startIdx = this.id; endIdx = lastSelected.id; }
-
Declare a
const
variable & define it as an array created from the NodeList that only contains theinput
elements between the function context and the last selectedinput
./* Still in the body of the conditional statement */ /** In order to remove all the 'input' elements aside from the ones in between * the function context and the last selected input, we'll use the `slice` * method. `slice` takes two arguments: the index that we want to slice our * array from, and the index where we want to cut off this new sliced portion. * I incremented the first argument by 1 because the input element associated * with the original value of startIdx has already been accounted for (it's either * 'this` or 'lastSelected', and we only want the inputs between those two). **/ const middleInputs = Array.from(checkBoxes).slice(parseInt(startIdx) + 1, endIdx);
-
Iterate through the newly created array of elements and set the
checked
property of each element to match thechecked
property of the function context./* Still in the body of the conditional statement */ // By setting the 'checked' property to match the function context, we're // able to allow users to check OR uncheck multiple items from the list. WOOOO! middleInputs.forEach(checkbox => checkbox.checked = this.checked);
-
After the conditional statement, define
lastSelected
as the function context so that when this event handler is called again on a NEWinput
element, we can uselastSelected
's ID property to figure out what inputs are between the two that need to be checked/unchecked.if (lastSelected && e.shiftKey){ /* Body of conditional statement */ } // After the conditional statement lastSelected = this;
-
-
DONE. Slightly verbose explanation, but I felt it was necessary given that this approach is quite different from the provided answer. Congratulations on complete 1/3rd of the JavaScript30 challenge! You're a champ. 💪💪