Table in HTML & JavaScript

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

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.

Git Repo

Bare Bones Markup

  • 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 first 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.
  • 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 how it’s filtered.
  • 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.
  • The first 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.
  • The comparison fn is a fancy way of comparing the key values with one another. Then reordering them.
  • The sortColumn fn does a number of things. One such item is it destroys and recreates the caret. We run the populateTable fn again.
  • The last block imports the JSON.
var headers = Array.from(document.querySelectorAll('.headers > div')),
  search = document.getElementById('search-field'),
  clear = document.querySelector('.clear'),
  caret = document.querySelector('.caret'),
  body = document.querySelector('.body'),
  filterType = null,
  filtered = null,
  data = null,
  markup = '',
  str = '';

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;
}

/* 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;
  };
}

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);
  }
})();

Style the Table

Styling is fairly straightforward. Certainly nothing overly complex. I will say however, if you think it’s easy, try and coding it yourself. 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.

html,
  body {
    width: 100%;
    padding: 0;
    margin: 0;
  }
  #html-table {
    min-width: 650px;
  }
  .masthead {
    display: grid;
    padding: 0 10px;
    margin: 10px 0;
    grid-template-columns: calc(100% - 200px) 200px;
    height: 25px;
  }
  .masthead h4 {
    margin: 0;
    line-height: 25px;
  }
  .masthead input {
    height: 100%;
    width: 100%;
    font: normal 14px sans-serif;
    display: block;
    box-sizing: border-box;
    border-radius: 4px;
    border: 1px solid #313b3f;
    outline: none;
  }
  
  .masthead .search {
    width: 200px;
    position: relative;
  }
  
  .masthead .clear {
    border: 1px solid black;
    display: none;
    border-radius: 3px;
    position: absolute;
    top: 50%;
    right: 4px;
    transform: translateY(-50%);
    padding: 1px 3px;
    font-size: 10px;
    cursor: pointer;
  }
  
  .enable-clear {
    display: block !important;
  }
  
  .masthead input::placeholder {
    font: normal 13px sans-serif;
  }
  
  main {
    overflow-x: auto;
  }
  .headers,
  .tble-rows {
    display: grid;
    grid-template-columns: repeat(5, 20%);
  }
  .tble-rows:nth-child(odd) {
    background-color: lightgrey;
  }
  .tble-cells {
    padding: 5px 10px;
  }
  .headers {
    font-weight: 600;
    cursor: pointer;
  }
  .headers > div {
    padding: 5px 10px;
  }
  .tble-cells {
    white-space: nowrap;
  }
  
  #html-table {
    font-family: sans-serif;
    font-size: 13px;
    display: table;
    width: 100%;
    max-width: 768px;
    margin: 50px auto 0 auto;
  }
  .caret {
    transform: rotate(180deg);
    display: inline-block;
    font-size: 10px;
    margin-left: 5px;
  }
  footer {
    position: absolute;
    background-color: #313b3f;
    color: #d9a74a;
    width: 100%;
    padding: 10px;
    text-align: center;
    box-sizing: border-box;
    font: normal 14px sans-serif;
    bottom: 0;
  }
  .website {
    font: normal 14px sans-serif;
    position: fixed;
    width: 100%;
    box-sizing: border-box;
    top: 0;
    letter-spacing: 0.5px;
    display: block;
    cursor: pointer;
    background-color: #313b3f;
    padding: 10px;
    overflow: auto;
  }
  .website a {
    text-decoration: none;
    color: #d9a74a;
    float: right;
  }
  .website span {
    color: #d9a74a;
  }

Concluding Table in HTML…

You may be thinking, why would I build this in 2023? 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.


*