How to Build a Table in HTML & JavaScript while Learning Valuable Dev Skills

Table in HTML showing different features of search, filter, and sort.

Last Updated on August 9, 2024 by E. Scott

This JSON powered table in HTML is search friendly and sortable. Nimble enough to plug right into an array of projects. Subsequently, there’s an onslaught of additional features that can be added here. But I think this’s a good foundation. With the current setup we could add pagination and/ or expandable rows. Just to name a few.

I often encounter instances where I need components such as a table in html. In my experience, it’s common for devs to use Bootstrap or Material for components. Even if said libraries are not being used elsewhere in the app, I’ve seen resources like this to be the go to vs building it from scratch.

I prefer to build components from scratch because I often need full control. Full control either to match a mock or add/ remove features. Using components from libraries and modifying them to suit our needs is often tedious and unsustainable.

Table in html with page header and footer and zebra stripped rows. Looking professional.

Git Repo

Table in HTML Markup

When building components, we always need a game plan. My game plan was to render the table body while everything else was static. So, everything in the body will be rendered according to what’s needed. Below is the skeleton. It’s a bare bones markup minus the body section which is rendered via JavaScript. Let’s quickly run through it.

  • Add the title (h4) and input field inside of a parent wrapper.
  • Create the headers nested inside of parent tags as well.
  • Most importantly, add an empty section for the body content with class ‘body’. This is where the table content will be injected.
<main>
  <div id="html-table">
    <div class="masthead">
      <h4>Smart Phones List</h4>
      <div class="search">
        <input id="search-field" type="text" placeholder="Search ..." />
        <span class="clear">&#x2715</span>
      </div>
    </div>
    <div class="headers">
      <div>ID</div>
      <div>Brand</div>
      <div>Category</div>
      <div>Title</div>
      <div>Price</div>
    </div>
    <div class="body"></div>
  </div>
  </main>

Table in HTML Script

  • The first variable gets all the child divs of parent elements with header class.
  • Get the search field followed by the caret—which is currently non existent. We’re gonna add it dynamically. That’s the purpose of caret != undefined ? caret.remove() : ”; in the sortColumn fn. sortColumn says, “If the caret is not undefined, remove it. Otherwise, do nothing.
var headers = Array.from(document.querySelectorAll('.headers > div')),
  search = document.getElementById('search-field'),
  clear = document.querySelector('.clear'),
  caret = document.querySelector('.caret'),

function sortColumn(e) {
  let index = null;
  filterType = e.target.innerHTML.toLowerCase();
  data.sort(comparison(filterType));
  populateTable(data);

  caret != undefined ? caret.remove() : '';
  caret = document.createElement('span');
  caret.classList.add('caret');
  caret.innerHTML = '&#x25B2';
  index = e.target.getAttribute('data-id');
  e.target.appendChild(caret);
}
  • The body variable represents the body class (table body). This is where we inject the HTML.
  • filterType retrieves the name of the clicked column to use it for filtering.
  • filtered is an array used to reorganize table rows, when filtered.
  • data is the incoming JSON, and markup is the actual HTML.
  • str is the input field value.
var body = document.querySelector('.body'),
  filterType = null,
  filtered = null,
  data = null,
  markup = '',
  str = '';
  • This first block clears the table via clicking on the X inside the search field.
  • The second block is a key-up listener on the search field. It rearranges the data by looping over an array of objects, then repopulates the table.
  • To repopulate the data, markup and the body innerHTML are set to empty. We then populate the table, and set the innerHTML to the new order.
clear.addEventListener('click', () => {
  search.value = '';
  populateTable(data);
  clear.classList.remove('enable-clear');
});

/* Search & Filter */
search.addEventListener('keyup', () => {
  str = search.value;
  if (str != '') {
    filtered = [];
    for (let x of data) {
      Object.values(x).filter((val) => {
        if (typeof val === 'string' && val.includes(str)) {
          filtered.push(x);
          filtered = filtered.filter((item, i) => filtered.indexOf(item) === i);
          populateTable(filtered);
        }
        if (typeof val === 'number' && val.toString().indexOf(str) > -1) {
          filtered.push(x);
          populateTable(filtered);
        }
        clear.classList.add('enable-clear');
      });
    }
  } else populateTable(data);
});

/* Populate HTML */
function populateTable(arr) {
  markup = '';
  body.innerHTML = '';
  renderData(arr);
  body.innerHTML = markup;
}
  • Notice the render function below and how it’s called in populateTable (shown above).
  • The comparison function is a fancy way of comparing the key values with one another. Then reordering them.
/* Render Data */
function renderData(arr) {
  for (var i = 0; i < arr.length; i++) {
    markup +=
      '<div class="tble-rows">' +
      '<div class="tble-cells">' + arr[i].id + '</div>' +
      '<div class="tble-cells">' + arr[i].brand + '</div>' +
      '<div class="tble-cells">' + arr[i].category + '</div>' +
      '<div class="tble-cells">' + arr[i].title + '</div>' +
      '<div class="tble-cells">' + arr[i].price + '</div>' +
      '</div>';
  }
}

/* Sort */
function comparison(key, order = 'ascending') {
  return (a, b) => {
    const varA = typeof a[key] === 'string' ? a[key].toUpperCase() : a[key];
    const varB = typeof b[key] === 'string' ? b[key].toUpperCase() : b[key];

    let comparison = 0;
    if (varA > varB) {
      comparison = 1;
    } else if (varA < varB) {
      comparison = -1;
    }
    return order === 'descending' ? comparison * -1 : comparison;
  };
}
  • The sortColumn function does two things
    • destroys and recreates the caret
    • runs the populateTable function again
  • The last block imports the JSON
function sortColumn(e) {
  let index = null;
  filterType = e.target.innerHTML.toLowerCase();
  data.sort(comparison(filterType));
  populateTable(data);

  caret != undefined ? caret.remove() : '';
  caret = document.createElement('span');
  caret.classList.add('caret');
  caret.innerHTML = '&#x25B2';
  index = e.target.getAttribute('data-id');
  e.target.appendChild(caret);
}

(async () => {
  const { default: json } = await import('path-to-json', {
    assert: { type: 'json' },
  });
  data = json.products;
  populateTable(data);
  for (var i = 0; i < headers.length; i++) {
    headers[i].addEventListener('click', sortColumn);
  }
})();

Again, I chose to build this table in html component from scratch because it’s very common for designs to change. Clients want new features or change existing ones. All of which would be very difficult if all possible in the event we used a library based component. Table in html is probably among the most common components. We always need features to be modularized, contain clean code, and they must be sustainable.

Sustainable because we have no idea who will be making changes if we’re working in a team environment. Or when. Similarly, we may be making said changes 10 months down the line. And we of course never want to give ourselves more work. So make the functions do one thing if possible. Simple functions also makes them easier to be tested.

If you’re however looking for an Angular data table, I got you covered. Here it is.

Need a feature rich React version? Here’s a React table with expandable rows.

Aside from building custom components, I’ve done a lot of data visualizations. Such as this custom line chart soltuion.

Style the Table

Styling is fairly straightforward. Certainly nothing overly complex. I will say however, if you think it’s easy, give it a go! A lot of devs say CSS is a breeze or literally despise writing it. My experience is many devs really don’t know how to write CSS—even at a basic level.

Lots of people rely on libraries like Bootstrap for columns, but don’t know how to work with absolutes, flex, and grid. I’ve seen senior devs who can’t build a three column layout. So, regardless of how easy it may look, put the same effort into learning CSS and Sass as you would with TypeScript, Spring, or any other technology. Regardless, here’s the CSS.

Concluding Table in HTML…

You may be thinking, why would I build this in 2024? Beginner devs often know frameworks like React, but don’t know HTML, CSS, or the basics or JavaScript. Some may say JavaScript is dead, but it’s undoubtedly the backbone of TypeScript. JSX is akin to TypeScript in that it’s written with JavaScript support and both compile to thereof. So, if you know JSX or TypeScript but not JavaScript, you’re limited.

JSX (JavaScript Syntax Extension and occasionally referred as JavaScript XML) is an extension to the JavaScript language syntax which provides a way to structure component rendering using syntax familiar to many developers commonly used in React. It is similar in appearance to HTML.

Wikipedia

In conclusion, this is one method of developing something natively. Something that can otherwise be written faster and easier with a framework. Anywho, check out the code. Try and make it your own. Hope you enjoyed this brief explanation of my table in HTML.

Be the first to comment

Leave a Reply

Your email address will not be published.


*