Add sorting allows to header like a table sorter

I want to add arrow bottons/icons to my header on an HTML table. Right now, once you click anywhere on the header it sorts my data on the table without displaying any arrows (see screenshots below)

Ascending sort

Descending sort

I googled and tested like 5 hours already, but I could not find what I was looking for. I want the header to display a drop-up button/icon when the table is sorted in descending order and be a drop-down when in ascending order. I hope this makes sense.

Here is my CSS/JS/HTML for your reference. I don’t look for the complete solution code, but am just looking for hints or any directions that I can try on my own. Thank you for the help in advance.


avascript: (function () {
    const isEmptyOrNaN = (obj) => obj === "" || isNaN(obj);
    const getCellValueInColumn = (tr, columnIdx) =>
      tr.children[columnIdx].innerText || tr.children[columnIdx].textContent;
    const compareCellValues = (cellValue1, cellValue2) => {
      return isEmptyOrNaN(cellValue1) || isEmptyOrNaN(cellValue2)
        ? cellValue1.toString().localeCompare(cellValue2)
        : cellValue1 - cellValue2;
    const compareFnFactory = (columnIdx, ascending) => (firstEl, secondEl) => {
      const cellValue1 = getCellValueInColumn(firstEl, columnIdx);
      const cellValue2 = getCellValueInColumn(secondEl, columnIdx);
      return ascending
        ? compareCellValues(cellValue1, cellValue2)
        : compareCellValues(cellValue2, cellValue1);
    document.querySelectorAll("th").forEach((th) =>
      th.addEventListener("click", () => {
        const table = th.closest("table");
        const tbody = table.querySelector("tbody");
        const columnIdx = Array.from(th.parentNode.children).indexOf(th);
        const compareFn = compareFnFactory(columnIdx, (this.ascending = !this.ascending));
          .forEach((tr) => tbody.appendChild(tr));


    background-color: var(--bs-table-bg);

    background-color: var(--bs-table-active-bg);


<!DOCTYPE html>
<html lang="en">
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src=""></script>
    <!--bootstrap v5.1-->
    <link href="" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <script src=""></script>
    <script src=""></script>
    <!--Custom CSS-->
    <link rel="stylesheet" href="style.css"> 
    <title>CONFIDENCIAL - users</title>
    <div class="mt-4 container" id="app">
        <table class="table table-success table-striped">
            <tr v-for="post, index in posts" :key = "post.userId">
              <td scope="row">{{ index + 1 }}</td> 
              <td>{{ }}</td>
              <td>{{ post.title }}</td>
              <td>{{ post.body }}</td>
        new Vue({
            data: {
                  posts: []
                .then(response => 
                    this.posts =
                .catch(error => 
    <!--custom js-->
    <script src="main.js"></script>


To do this you have to provide your own icons for example with <img> tag and change them on user clicks from JS code.

But, in fact, your code is not the Vue way of working with user input. See for example v-on:click directive: Introduction — Vue.js. With Vue you can sort by click more easily then with pure JavaScript.

Alright, thank you so much for the tips again @9509706156 ! I ended up finding a fix that applies that v-on:click directive to my code.
I’m new to vue.js so I’m looking forward to tapping on its potential.