Web Development

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 an HTML 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 browser 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 trying to calculate people’s ages 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 Date Sorter was 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.

Let’s 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:wght@500&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 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 function removeData 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 function CalculateAge 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 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 function displayMessage 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

Screenshot

Here you can find the demo: 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 as a 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

Share
Published by
Tracy Ridge

Recent Posts

Hot New Web Dev – October 2024

Welcome to Hot Web Dev October 2024, featuring the latest technology and web development news.… Read More

3 weeks ago

New Svelte 5 Guessing Game 2024

In this tutorial, you’ll build a fun and interactive guessing game using Svelte 5, the… Read More

2 months ago

Hot New Web Dev – September 2024

Welcome to Hot Web Dev September 2024, featuring the latest technology and web development news.… Read More

2 months ago

New JavaScript Guessing Game 2024

The JavaScript guessing game tutorial is a simple beginner's project. It features modern JavaScript syntax… Read More

2 months ago

Hot Web Dev – 12 Useful Top Tools of 2023

If you have been following the monthly Hot Web Dev magazine you will find the… Read More

2 months ago

Hot New Web Dev – August 2024

Welcome to Hot Web Dev August 2024, featuring the latest technology and web development news.… Read More

3 months ago