JavaScript Date Sorter

The JavaScript Date Sorter is a vanilla JavaScript Project that uses an HTML form to grab a name and date input and adds it to a table. There is a third column that takes the date, calculates the age then adds it to the table. This table can be sorted and items can be deleted. The data uses the browsers LocalStorage to store the items. There are buttons to recalculate the age and clear the table.

Background

Over the last 2 years, I have been learning more about JavaScript. I see lots of different projects in the web sphere and wanted to create something that is different to the norm. My inspiration for this project came from a colleague who was trying to calculate the ages of people from their date of birth. If given a long list of people this could be tedious. Hence why I created this little project. Enjoy!

Prerequisites

A modern browser and a text editor.  I recommend VS Code. Some knowledge of HTML and JavaScript would be beneficial but not required. You can use the VS Code Live Server plugin to execute the code in your browser. 

Download

The latest JavaScript Guessing Game update has been released on 23rd March 2022.

Also available on GitHub

The Concept

The basic concept is to get the name and date of birth of a person and add the information to a table. On adding the data to the table a third column calculates the age. There are buttons to regenerate the table if, for example, the date of birth stored has passed. You can also delete all the data if required. The data is stored in the browser’s local storage and none is sent to any server. If you do add server functionality please remember to sanitize the input particularly if the data is being sent to a database.

Lets get Coding

HTML Source

The HTML uses Google Fonts. The table will be added to the tableContainer div.

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript Date Sorter</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Oswald:[email protected]&family=Poppins&display=swap" rel="stylesheet">
    <link href="css/global.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <div class="item">
            <div class="innerContainer">
                <h1>JavaScript Date Sorter</h1>
                <p>A handy little date sorter which calculates the age of the person upon entering their date of birth.
                    All data is saved in the browsers local storage.
                    The calculate age button refreshes the age stored in the local storage.
                </p>
            </div>
            <div class="innerContainer">
                <form id="userData" method="POST">
                    <label for="name">Name</label>
                    <input type="text" name="name" id="name">
                    <label for="birth_date">DOB</label>
                    <input type="date" name="birth_date" id="birth_date">
                    <button id="submit" type="submit">Add</button>
                </form>
            </div>
            <div class="innerContainer">
                <div id="info"></div>
                <button id="checkAge">Recalculate Age</button>
                <button id="clearTable">Clear Table</button>
            </div>
        </div>
        <div id="tableContainer" class="item">
        </div>
    </div>

    <script src="js/global.js"></script>
</body>
</html>

Loading the Table

The main functionality of the JavaScript Date Sorter. It uses the JavaScript window.onload function to create the table. Adds the dropdown icons and sorter function to the table.

/**
 * Loads the table on load
 */
window.onload = () => {
	if (check !== null) {
		let list = getStorage(),
			tableContainer = document.getElementById("tableContainer"),
			table = document.createElement("table"),
			tblBody = document.createElement("tbody"),
			tblHeader = document.createElement("thead");
		tblHeader.innerHTML = `<tr><th><input id="cb" name="cb" type="checkbox"/></th><th>Name <span>˅</span><span class="hidden">˄</span></th><th>Birth Date <span>˅</span><span class="hidden">˄</span></th><th>Age <span>˅</span><span class="hidden">˄</span></th></tr>`;
		table.appendChild(tblHeader);
		list.forEach((element, index) => {
			let row = document.createElement("tr"),
				cellBx = document.createElement("td"),
				x = document.createElement("input");
			x.setAttribute("type", "checkbox");
			x.setAttribute("name", "select");
			x.setAttribute("id", index);
			cellBx.appendChild(x);
			row.append(cellBx);
			row.id = `id_${index}`;
			for (let i = 0; i < element.length; i++) {
				let cell = document.createElement("td"),
					cellText = document.createTextNode(element[i]);
				cell.appendChild(cellText);
				row.appendChild(cell);
			}
			tblBody.appendChild(row);
		});
		let div = document.createElement("div"),
			btn = document.createElement("button");
		btn.setAttribute("id", "delBtn");
		btn.innerText = "Delete";
		btn.addEventListener("click", removeData);
		div.appendChild(btn);
		table.appendChild(tblBody);
		tableContainer.appendChild(table);
		tableContainer.appendChild(div);
        
        //Sorting data
		const getCellValue = (tr, idx) =>
			tr.children[idx].innerText || tr.children[idx].textContent;
		const comparer = (idx, asc) => (a, b) =>
			((v1, v2) =>
				v1 !== "" && v2 !== "" && !isNaN(v1) && !isNaN(v2)
					? v1 - v2
					: v1.toString().localeCompare(v2))(
				getCellValue(asc ? a : b, idx),
				getCellValue(asc ? b : a, idx)
			);
        //creates the table head with the dropdowns
		let thead = document.querySelectorAll("th");
		thead.forEach((th) =>
			th.addEventListener("click", () => {
				const table = th.closest("table"),
					tbody = table.querySelector("tbody"),
					span = th.querySelectorAll("span");
				span.forEach((ele) => {
					ele.classList.contains("hidden")
						? ele.classList.remove("hidden")
						: ele.classList.add("hidden");
				});
				Array.from(tbody.querySelectorAll("tr"))
					.sort(
						comparer(
							Array.from(th.parentNode.children).indexOf(th),
							(this.asc = !this.asc)
						)
					)
					.forEach((tr) => tbody.appendChild(tr));
			})
		);
		//Select checkboxes
		document.getElementById("cb").addEventListener("click", function () {
			let checkboxes = document.querySelectorAll('input[name="select"]');
			checkboxes.forEach((ele) => {
				this.checked ? (ele.checked = true) : (ele.checked = false);
			});
		});
	}
};

Delete the Items

The removeData function will delete the items that you have selected in the table. If there are no items left after you delete them it will clear the JSListDate Local Storage.

/**
 * Deletes the items selected in the table
 */
let removeData = () => {
	let checked = document.querySelectorAll("input:checked"),
		store = getStorage(),
		len = checked.length;
	for (let i = 0; i < checked.length; i++) {
		store.splice(checked[i].id, len);
	}
	//check the store length - if it has more than 0
	if (store.length !== 0) {
		//add to the database
		localStorage.setItem("JSListDate", JSON.stringify(store));
	} else {
		//no entries left remove everything
		localStorage.removeItem("JSListDate");
	}
	location.reload();
};

Handling the Submit

Once the user presses the add button the data is passed to the CalculateAge function and adds the data to Local Storage.

/**
 * Handles the submit button, saves to local storage
 */
const handleSubmit = (e) => {
	e.preventDefault();
	let arr = [],
		data = [...e.currentTarget.elements].filter((ele) => ele.type !== "submit");
	const dob = new Date(data[1].value);
	let age = CalculateAge(dob);
	data.forEach((element) => {
		arr.push(element.value);
	});
	arr.push(age);
	setStorage(arr);
	location.reload();
};
document.getElementById("userData").addEventListener("submit", handleSubmit);

Calculate Age Function

The CalculateAge function takes the date parameter submitted in the handle event above and returns the age of the user.

/**
 * Calculates the age of the person
 * @param {Date} dob
 * @returns age
 */
let CalculateAge = (dob) => {
	const currentDate = new Date(),
		// To calculate the time difference of two dates
		Difference_In_Time = currentDate.getTime() - dob.getTime();
	// To calculate the no. of days between two dates
	return Math.floor(Difference_In_Time / (1000 * 3600 * 24) / 365.25);
};

Recalculate Age Button

The age data in the table is doesn’t change its state automatically. If you leave the table for a period of time the age may be incorrect. This button will recalculate the age of each person in the table.

/**
 * Check age button will recalculate the age in the database
 */
document.getElementById("checkAge").addEventListener("click", () => {
	if (check !== null) {
		let list = getStorage();
		list.forEach((element) => {
			const dob = new Date(element[1]);
			element[2] = CalculateAge(dob);
		});
		setStorage(list, true);
		location.reload();
	} else {
		displayMessage("No data to check");
	}
});

Clear the Table Button

Removes the table from Local Storage. It uses the built-in browser prompt to confirm the action. If there is no data to clear it will output a message.

/**
 * Clears the database
 */
document.getElementById("clearTable").addEventListener("click", () => {
	if (check !== null) {
		if (window.confirm("Are you sure?")) {
			localStorage.removeItem("JSListDate");
			location.reload();
		}
	} else {
		displayMessage("No table to clear");
	}
});

Display Message

The displayMessage function will output a message if there is no data present when pressing the clear table or recalculate the age button.

/**
 *
 * Displays a message if a buttton is pressed and there is no data to show
 * @param {string} msg
 */
let displayMessage = (msg) => {
	let info = document.getElementById("info");
	info.innerText = msg;
	setTimeout(function () {
		info.innerText = "";
	}, 5000);
};

CSS

/*
  Josh's Custom CSS Reset
  https://www.joshwcomeau.com/css/custom-css-reset/
*/
*,
*::before,
*::after {
	box-sizing: border-box;
}
* {
	margin: 0;
}
html,
body {
	height: 100%;
	font-size: 16px;
}
body {
	line-height: 1.5;
	-webkit-font-smoothing: antialiased;
}
img,
picture,
video,
canvas,
svg {
	display: block;
	max-width: 100%;
}
input,
button,
textarea,
select {
	font: inherit;
}
p,
h1 {
	overflow-wrap: break-word;
}
#root,
#__next {
	isolation: isolate;
}
body {
	font-family: "Poppins", sans-serif;
	background: url("../img/cool-background.png") repeat;
	color: rgb(255, 255, 255);
}
h1 {
	font-family: "Oswald", sans-serif;
	font-size: 2.5rem;
	color: #2c3373;
}
p {
	font-size: 1.5rem;
}
.container {
	display: grid;
	grid-template-columns: 1fr 1fr;
	column-gap: 1rem;
	padding: 0 1rem;
	width: 100%;
}
.item {
	place-self: center;
}
.innerContainer {
	margin: 5rem 0;
}
table {
	color: black;
}
thead,
tfoot {
	background-color: rgba(41, 42, 46, 0.795);
	font-weight: bold;
	color: #e4f0f5;
}
tbody {
	background-color: #e4f0f5;
}
caption {
	padding: 10px;
	caption-side: bottom;
}
table {
	border-collapse: collapse;
	border: 2px solid rgba(41, 42, 46, 0.795);
	letter-spacing: 1px;
}
td,
th {
	border: 1px solid rgb(190, 190, 190);
	padding: 5px 10px;
}
th {
	cursor: pointer;
}
td {
	text-align: center;
}
label {
	font-weight: bold;
	color: #2c3373;
	font-size: larger;
}
#info {
	padding: 2rem 0;
}
input[type="text"],
input[type="date"] {
	padding: 1rem;
}
button {
	background-color: rgb(41 42 46);
	color: rgb(248, 248, 248);
	padding: 0.5rem;
	border-radius: 0.5rem;
	border: solid 2px #252626;
	font-weight: bold;
}
.hidden {
	display: none;
}
@media screen and (max-width: 900px) {
	.container {
		grid-template-columns: 100%;
	}
}

Demo & Screenshots

JavaScript Date Sorter
Screenshot

https://js-name-datesorter.glitch.me/

Conclusion

Please feel free to fork the code and tweak it further. If you use any of the code for your own projects please link back to my site by token of appreciation. Please feel free to request features or request other tutorials.

Sources

Stack Overflow – Sorting HTML Table with JavaScript

Codegrepper – Get all form values in JavaScript

Topics:

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.