August 26, 2019
Let's Build: With JavaScript - Dynamic Checkboxes
Welcome to another installment of my Let's Build: With JavaScript series. This video tutorial teaches you how to make a collection of dynamic HTML checkboxes using vanilla JavaScript.
The ongoing Let's Build: With JavaScript series is a collection of tutorials that I have compiled to give those new to the language or those looking to finally learn the core components, a fresh take on solving common problems. At my day job, I'm faced with all types of complex problems and scenarios that require JavaScript to solve. Those problems ultimately inspire these tutorials and the future ones to come.
Getting Started
In applications like Dropbox, Asana, Google Drive, and more there is commonly a list of files/tasks/etc. Each list item often allows you to perform certain actions either independently or in bulk. Within those patterns, I often see checkboxes that allow you to narrow in actions on a particular list item. Sometimes you may want to perform bulk actions on several actions. Doing this gets more complex but is very possible.
I'll be solving a few of those patterns in this tutorial including:
- Selecting all checkboxes at once
- Shift + clicking to select multiple checkboxes
- Clearing all checkbox selections at once
- Adding an alternate state to those items that are checked
This all happens in less than 90 lines of JavaScript.
HTML
The HTML is basic markup with a few classes used to target the elements via JavaScript and CSS.
<div class="container">
<h1 class="h1">Dynamic Checkboxes with JavaScript</h1>
<button class="js-clear">Clear <span></span> checked</button>
<table class="table">
<thead>
<tr>
<th align="left"><input type="checkbox" class="js-select-all" />
</th>
<th align="left">Publish?</th>
<th align="left">Author</th>
<th align="left">Created at</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>
<div class="published">
<input type="checkbox" id="1"/>
<label class="title" for="1">My first blog post</label>
</div>
</td>
<td>Andy</td>
<td>August 23, 2019</td>
</tr>
<tr>
<td>2</td>
<td>
<div class="published">
<input type="checkbox" id="2"/>
<label class="title" for="2">My second blog post</label>
</div>
</td>
<td>Randy</td>
<td>August 22, 2019</td>
</tr>
<tr>
<td>3</td>
<td>
<div class="published">
<input type="checkbox" id="3" />
<label class="title" for="3"> My third blog post</label>
</div>
</td>
<td>John</td>
<td>August 21, 2019</td>
</tr>
<tr>
<td>4</td>
<td>
<div class="published">
<input type="checkbox" id="4"/>
<label class="title" for="4">My fourth blog post</label>
</div>
</td>
<td>Jane</td>
<td>August 20, 2019</td>
</tr>
<tr>
<td>5</td>
<td>
<div class="published">
<input type="checkbox" id="5"/>
<label class="title" for="5">My fifth blog post</label>
</div>
</td>
<td>Ryan</td>
<td>August 19, 2019</td>
</tr>
<tr>
<td>6</td>
<td>
<div class="published">
<input type="checkbox" id="6"/>
<label class="title" for="6">My sixth blog post</label>
</div>
</td>
<td>Nicole</td>
<td>August 18, 2019</td>
</tr>
</tbody>
</table>
</div><div class="container">
<h1 class="h1">Dynamic Checkboxes with JavaScript</h1>
<button class="js-clear">Clear <span></span> checked</button>
<table class="table">
<thead>
<tr>
<th align="left"><input type="checkbox" class="js-select-all" />
</th>
<th align="left">Publish?</th>
<th align="left">Author</th>
<th align="left">Created at</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>
<div class="published">
<input type="checkbox" id="1"/>
<label class="title" for="1">My first blog post</label>
</div>
</td>
<td>Andy</td>
<td>August 23, 2019</td>
</tr>
<tr>
<td>2</td>
<td>
<div class="published">
<input type="checkbox" id="2"/>
<label class="title" for="2">My second blog post</label>
</div>
</td>
<td>Randy</td>
<td>August 22, 2019</td>
</tr>
<tr>
<td>3</td>
<td>
<div class="published">
<input type="checkbox" id="3" />
<label class="title" for="3"> My third blog post</label>
</div>
</td>
<td>John</td>
<td>August 21, 2019</td>
</tr>
<tr>
<td>4</td>
<td>
<div class="published">
<input type="checkbox" id="4"/>
<label class="title" for="4">My fourth blog post</label>
</div>
</td>
<td>Jane</td>
<td>August 20, 2019</td>
</tr>
<tr>
<td>5</td>
<td>
<div class="published">
<input type="checkbox" id="5"/>
<label class="title" for="5">My fifth blog post</label>
</div>
</td>
<td>Ryan</td>
<td>August 19, 2019</td>
</tr>
<tr>
<td>6</td>
<td>
<div class="published">
<input type="checkbox" id="6"/>
<label class="title" for="6">My sixth blog post</label>
</div>
</td>
<td>Nicole</td>
<td>August 18, 2019</td>
</tr>
</tbody>
</table>
</div>
CSS
The CSS is basic markup to make our tables look more presentable. I also add some custom styling for those items in a checked state.
@import url("https://fonts.googleapis.com/css?family=Montserrat:400,400i,700");
body {
background: peachpuff;
font-family: 'Montserrat';
}
.container {
max-width: 700px;
margin: 2rem auto;
background: #fff;
padding: 40px;
border-radius: 10px;
}
.table {
width: 100%;
margin-bottom: 1rem;
color: #212529;
border-collapse: collapse;
}
.table thead th {
border-bottom: 2px solid #dee2e6;
border-top: 0;
vertical-align: bottom;
user-select: none;
}
.table td,
.table th {
padding: 0.75rem;
border-top: 1px solid #dee2e6;
user-select: none;
}
td.title {
font-family: 'Georgia', serif;
font-style: italic;
}
button {
background: #f4f4f4;
padding: 10px 16px;
margin-bottom: 10px;
border-radius: 3px;
appearance: none;
border: 0;
border-radius: 8px;
line-height: normal;
&:hover {
background: #f0f0f0;
cursor: pointer;
}
}
.published {
display: flex;
align-items: center;
label {
margin-left: 16px;
font-family: "Georgia", serif;
font-size: 16px;
font-style: italic;
}
}
input[type=checkbox]:checked + label {
text-decoration: line-through;
}
Finally, the JavaScript
I use an object-oriented approach towards JavaScript. Everything lives within a global object I created that describes the feature. There are many other ways to write your JavaScript code so I don't want you to think this is the only way. I found this way quite helpful for me in terms of reusability. I can share logic quite easily between functions and methods within the global object. This also is scoped to the global object which means fewer conflicts with JavaScript written elsewhere.
const DynamicCheckboxes = {
checkboxes: document.querySelectorAll('.table td input[type="checkbox"]'),
selectAllTarget: document.querySelector('.js-select-all'),
clearBtn: document.querySelector('.js-clear'),
initialize() {
this.shiftToSelect();
this.selectAll();
this.clearChecked();
this.showRemoveCheckedButton();
},
shiftToSelect() {
const checkboxes = this.checkboxes;
let lastChecked;
function handleCheck(event) {
// Check if shift key is down and check if checkbox is checked
let inBetween = false;
if (event.shiftKey && this.checked) {
checkboxes.forEach(checkbox => {
if (checkbox === this || checkbox === lastChecked) {
inBetween = !inBetween;
}
if (inBetween) {
checkbox.checked = true;
}
});
}
lastChecked = this;
}
checkboxes.forEach(checkbox => checkbox.addEventListener('click', handleCheck, false));
},
selectAll() {
function handleSelectAll(event) {
this.checkboxes.forEach(checkbox => {
checkbox.checked ? (checkbox.checked = false) : (checkbox.checked = true)
})
}
this.selectAllTarget.addEventListener('click', handleSelectAll.bind(this), false)
},
showRemoveCheckedButton() {
this.clearBtnDisplay('none')
document.addEventListener('change', this.showBtn.bind(this))
},
showBtn(event) {
const checkboxesChecked = document.querySelectorAll('.table td input[type=checkbox]:checked').length
if (checkboxesChecked > 0) {
this.clearBtn.querySelector('span').textContent = checkboxesChecked;
this.clearBtnDisplay('block');
} else {
this.clearBtn.querySelector('span').textContent = '';
this.clearBtnDisplay('none');
}
},
clearBtnDisplay(state) {
this.clearBtn.style.display = state;
},
clearChecked() {
this.clearBtn.addEventListener('click', removeChecked.bind(this), false);
function removeChecked() {
this.checkboxes.forEach(checkbox => (checkbox.checked = false));
this.selectAllTarget.checked = false;
this.clearBtnDisplay('none');
}
}
};
DynamicCheckboxes.initialize();
I recommend watching the video to see this come to life and hear my thinking as I'm putting it all together. At first glance, the code looks a bit complicated but in theory, each function within the DynamicCheckboxes
object is a building block for the features we wanted to add in the first place. Some functions share logic while others act independently. You can probably guess, scaling something like this is quite hard. That's likely why the rise of frameworks is all the buzz at the moment.
Closing things out
Hopefully, you've learned a bit here! I approached JavaScript in an ignorant manner. I wanted to build everything with it but then realized I needed to take a step back and focus on understanding smaller aspects of problems we encounter day-to-day as developers. There is always going to be better ways to write code but I found that first solving the problem allows you to take a new look at what you've accomplished and then later refactor into something more legible and performant. Sometimes writing less code actually can make things harder to read so doing beat yourself up if you look at code that's been refactored to the most extreme amount.
The Series So Far
- Letβs Build: With JavaScript β DIY Dropdowns and Responsive Menus
- Let's Build: With JavaScript - Broadcast Bar with Cookies
- Let's Build: With JavaScript - Sticky Nav
- Let's Build: With JavaScript - Dynamic Tabs
- Let's Build: With JavaScript - Modals
- Let's Build: With JavaScript - HTML5 Video Player
- Let's Build: With JavaScript - Accordions
- Let's Build: With JavaScript - Skeleton Screen Effect
- How to Code an Off-Canvas Menu - Let's Build: With JavaScript
- Show More β Show Less Toggle with JavaScript
- How to use Local Storage with JavaScript
Leave a reply
Categories
Collection
Part of the Let's Build: With JavaScript collection
Sign in or Sign up to leave a response.
1 response
Attention:
Are you looking for a REAL FINANCIAL LOAN COMPANY to give you loan between 50,000 Euro and 50,000,000 Euro (for a business or company loan, personal loan, home loans, car loan, debt consolidation loans, venture capital, healthcare loan, etc.)
Or have you been denied a loan from a bank or financial institution because of one reason or the other?
Apply now and get real financial loan processed and approved within 3 days.
MARK DAVSON FINANCIAL LOAN FIRM, we are an "Internationally Accredited Loan Lender" that give REAL FINANCIAL LOANS to individuals and companies in a low interest rate of 2% with your valid identity card or your country international passport for verification.
Our loan Repayment starts 1 (One) year after you have received your loan, and repayment duration ranges from between 3 to 35 years duration.
Our company also need a person that can be our company representative in your country.
FOR IMMEDIATE RESPONSE AND PROCESSING OF YOUR LOAN REQUEST WITHIN 2 WORKING DAYS,
Contact us directly through this email: [email protected]
Contact us with the following information:
Full Name:____________________________
Amount Needed as Loan:________________
Loan Duration:_________________________
Purpose for Loan:______________________
Date of Birth:___________________________
Gender:_______________________________
Marital status:__________________________
Contact Address:_______________________
City/Zip code:__________________________
Country:_______________________________
Occupation:____________________________
Mobile Phone:__________________________
Send your request for immediate response to: [email protected]
Thank you
C.E.O. Rev. Mark Davson
MARK DAVSON FINANCIAL LOAN FIRM
United Kingdom, England (UK)
Email:[email protected]
Email: [email protected]
Email: [email protected]
WhatsApp:+447404320361