Today we build a Gmail style interaction for holding shift while you select checkboxes. Grab all the exercises and starter files over at JavaScript30.com
Simply genius, Wes. Thanks! These flags can do some real magic with little code, and then you try to do it without the flag - it takes too much time and effort.
Really nice solution! There is a small bug where if you check a box, then uncheck it, then hold shift and click it again for a third time, all the boxes below it get checked as well. The way I fixed this was to simply add a check to confirm that the lastChecked is not the currently checked box. Before: if (e.shiftKey && this.checked) After : if (e.shiftKey && this.checked && lastChecked != this)
There is also another bug, if you click on checkbox, then uncheck it, it will still remain as lastChecked and when you will click on another checkbox holding shift all checkboxes between them will be checked. The sollutions is quite simple: if (e.shiftKey && lastChecked.checked && this.checked && lastChecked != this) Here is a working example of my solution: codepen.io/vitfl/pen/aPMwGm
@@vitfl2580 Hey, bro. There is an error message in the console when you hold shift and click on the checkbox for the first click checkbox. The solution : let lastChecked = {checked: false};
I love how simple your code is! I tried to first generate a list, where each task had a data property (with its index: 0,1,2...). Get the indexes of previously clicked and current clicked checkboxes. Generate indexes between these checkboxes. And only then (having indexes of the checkboxes between) iterate through the array and change the state of a checkbox . Your tutorials are very good.
I retrieved first clicked element and the second, then just filtered the checkboxes array and set filtered items to the state of the first clicked element. So there's no trouble with lastChecked stored as let.
A couple bugs in addition to the one Nilson listed: If you check a box, uncheck it, then hold shift and click somewhere else it will still check all in between. I fixed this by adding to the if statement: && lastChecked.checked). If you hold shift for the first box you check it will check all boxes below. This is fixed with my above solution but will throw an error because lastChecked is undefined on the first check. So I just assigned lastChecked to the first checkbox: let lastChecked = checkboxes[0];. You could also add a typeof if statement to check if lastChecked is undefined before the main if statement.
So much nicer than the way I figured out. I used one flag (counter = false) and looped over the checkboxes, but to do that I had to force the nodelist into an array: Array.from(checks). While there, I just set the counter to true the first checked box I found, checked the rest, until I came to one that wasn't checked, and then set the counter back to false and breaked. Pretty hacky, but it worked!
Not sure if this is a good solution but it works haha. 1. I kept track of shift-clicked item and last non-shift clicked item. 2. Used indOf on the input array to get their positions then used slice to return array of all items in between. 3. I then looped through the sliced array and just marked all items to "checked = true"
Cool script. I find it nice that if I click a checkbox while holding SHIFT it will check all boxes below that. that gives a quick check all from a a given starting point. Now I need to mod the script to perform an uncheck range
Did you manage to solve the uncheck problem? I tried checkbox.checked = !checkbox.checked but it unchecks the first box because its reversing its original state - Haha if that makes sense
Excellent Tutorial as always. Wish I could say I got it first time, but I was no where near. Please could you explain the lastChecked and this values on the checkboxes? I am not sure what set them apart.
Yep sure, this = context of the function and relates to the first clicked checkbox in this scenario. lastChecked = The last checked checkbox by the users mouse click. The rest of the tutorial then goes onto explain how you tell the computer to only target and change the value of checkboxes inside of those two values. Let me know if you have any further questions on this. Also would recommend watching that part again.
Okay thank you I think I get it (but I think It's the other way around 'this' refers to the one we are currently checking and 'lastChecked' refers to the first one we checked) but either way, thank you!
Aha, you are a star! Yes indeed. I completely missed that. That way you can select an item at the bottom of the list, and still have the same effect if you select an item further up the list on the second click. The method he provides allows the Shift + Select feature to work both ways!
Coz in short forEach always returns undefined but map returns the new array as the length of the array it is called on for example if you call map method on array which has length of 4 it returns new array with the length of. so here he is not returning anything he just looping through to get his result. so
when you check one, and then another, and then uncheck one of them, hold shift and check one unchecked box. the last box that was unchecked gets checked again. i wanted to look for a solution but my head hurts i don't wanna play anymore.
just wanna start by saying I'm a huge fan of yours. I took the challange before watching the solution part and turned out I found couple Bug/limitations in your finished code mentioned at bottom here. My solution didn't have those Bugs/limitations. here's what I did : hz-tyfoon.github.io/vanillaJsCheckMultipleCheckboxsByHoldingShiftKey/ here's the couple bug found in your solution: bug-1: if at the very first time a checkbox is checked while holding the shift button, then all the checkboxs from the bottom to that clicked item gets checked. bug-2: In gmail U can both check and also "uncheck" multiple checkbox by holding shift key but, in this code unchecking multiple "checked" item don't work. Again, Thanks a tonn for these videos. You're one of the best frontEnd teachers for me. Felt really excited after I finished this challenge on my own, that's why I shared it by commenting. Thanks again. Love from Bangladesh.
Nice. First of as you suggested I've implemented on my own. And then watched yours soltion. And I found a bug in this app. For example if you checked first one and after that check input below first one (a few times) (without Shift) - and after you checked AGAIN THIS CHECKBOX but with Shift - it will work out in some strange way. (Hope my poem is understandable).
Mine solution of course is not so elegant, but it works without bugs (I guess) )) const $itemCheckboxes = document.querySelectorAll('.item input') let prevCheckedIndexBuffer = null let prevCheckedIndex = null document.querySelector('.inbox').addEventListener('click', e => { if(e.target.tagName !== 'INPUT') return if(!e.shiftKey) { if(e.target.checked) { prevCheckedIndexBuffer = prevCheckedIndex prevCheckedIndex = Array.from($itemCheckboxes).findIndex(item => item == e.target) } else { prevCheckedIndex = prevCheckedIndexBuffer } } else { const index = Array.from($itemCheckboxes).findIndex(item => item == e.target) const [minId, maxId] = [prevCheckedIndex, index].sort((a, b) => a - b) for(let i = minId; i
Why not loop from the first checked index plus one to the second checked index minus one? It would be faster but more importantly better code imo. I try to avoid flags when possible.
I did that!! But thought it wasn't as fast... As it is checking a box one at a time. I thought Wes' solution would be a better way in terms of performance. Hmmm...Here's my solution: codepen.io/Tubbie/live/rrYjZB Can you share yours too?
Uncaught TypeError: checkboxes.forEach is not a function Here's my code const checkboxes = $('.inbox input[type="checkbox"]'); function handleCheck(e) { console.log(e); } checkboxes.forEach(checkbox => { checkbox.on('click', handleCheck); }); Please explain me the bugg
you good. too bad, im not decent enough to be able to do this alone. For some reason javascript is so challenging for me.. it's less "mathematical" than java.
@@Ludo045 well, im gonna be honest, i already know javascript beforehand lol, i also already learn react (web framework). I just watch this series to know how vanilla js work.
@@ccrsxx Still learning and doing these challenge is still something.. I dont think I've been to do a single of these challenge until now.. from 1 to 11
@@Ludo045 Yeah, i think you shouldn't do any of these projects just yet, before knowing a basic understanding in javascript. I also struggle a lot when I did these project two weeks ago. This series is definitely for folks that are already know a basic of javascript.
@@ccrsxx What are these basic supposed to be ? 😅 I mean I think i did take them and understanding how their work, array, conditional structure, DOM, css, html etc.. Sure for now i cant do kickass html/css page, but i have no problem reading and understanding a code. 😅
const checkboxes = document.querySelectorAll('.inbox input[type="checkbox"]') checkboxes.forEach(checkBox => { checkBox.addEventListener('click', handleClick) }) let lastChecked function handleClick(e) { let inBetween = false if (e.shiftKey && this.checked && lastChecked) { checkboxes.forEach(checkbox => { if (this === checkbox || lastChecked === checkbox) { inBetween = !inBetween } if (inBetween) { checkbox.checked = true } }) } if (this.checked) { lastChecked = this } else { lastChecked = null } } some bug fixes to your code, It handle case when someone click on a checkbox with shift key and there no previous checked box