Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <div class="widget-wrap">
  <div id="sort"></div>
  <h1>JAVASCRIPT SORTABLE TABLE</h1>
  
  <!-- (A) TABLE : NEEDS PROPER THEAD/TBODY -->
  <table id="demoA">
    <thead>
      <tr>
        <td>Fruit</td>
        <td>Color</td>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Durian</td>
        <td>Green</td>
      </tr>
      <tr>
        <td>Orange</td>
        <td>Orange</td>
      </tr>
      <tr>
        <td>Apple</td>
        <td>Red</td>
      </tr>
      <tr>
        <td>Grape</td>
        <td>Red</td>
      </tr>
      <tr>
        <td>Blueberry</td>
        <td>Blue</td>
      </tr>
      <tr>
        <td>Pear</td>
        <td>Green</td>
      </tr>
    </tbody>
  </table>
  
    <!-- (X) VISIT CODE-BOXX -->
  <div id="code-boxx">
    Visit
    <a href="https://code-boxx.com/sortable-table-vanilla-javascript/"
       target="_blank">
      Code Boxx
    </a> for more details.
  </div>
</div>

              
            
!

CSS

              
                /* CSS IS OPTIONAL - FOR COSMETICS ONLY */
/* (A) SORTABLE TABLE */
.sorta {
  border-collapse: collapse;
  width: 100%;
}

/* (B) CELLS */
.sorta td, .sorta th {
  padding: 10px;
  text-align: left;
}

/* (C) HEADER */
.sorta thead {
  font-weight: 700;
  color: #fff;
  background: #000;
}
.sorta thead *::selection { background: none; }

/* (D) BODY */
.sorta tbody tr:nth-child(even) {
  /*background: #f2f2f2;*/
  background: rgba(244, 244, 244, 0.6);
}

/* (E) UP/DOWN COLORS */
.sortup { background: red; }
.sortdown { background: blue; }


/* (X) DOES NOT MATTER */
/* PAGE & BODY */
* {
  font-family: arial, sans-serif;
  box-sizing: border-box;
}
body {
  display: flex;
  align-items: center; justify-content: center;
  min-height: 100vh;
  background-image: url(https://images.unsplash.com/photo-1528459801416-a9e53bbf4e17?crop=entropy&cs=srgb&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTY0MjEzNzY1Mw&ixlib=rb-1.2.1&q=85);
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
}

/* WIDGET */
.widget-wrap {
  min-width: 600px;
  padding: 30px;
  border-radius: 20px;
  background: rgba(255, 255, 255, 0.4);
}

/* SVG */
#sort {
  width: 100%; height:100px;
  background-image: url('data:image/svg+xml;utf8,<svg viewBox="0 0 512 512" width="100" xmlns="http://www.w3.org/2000/svg"><path d="M304 416h-64a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm-128-64h-48V48a16 16 0 0 0-16-16H80a16 16 0 0 0-16 16v304H16c-14.19 0-21.37 17.24-11.29 27.31l80 96a16 16 0 0 0 22.62 0l80-96C197.35 369.26 190.22 352 176 352zm256-192H240a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h192a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm-64 128H240a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM496 32H240a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h256a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16z" /></svg>');
  background-repeat: no-repeat;
  background-position: center;
}

/* FOOTER */
#code-boxx {
  font-weight: 600;
  margin-top: 50px;
}
#code-boxx a {
  display: inline-block;
  padding: 5px;
  text-decoration: none;
  background: #b90a0a;
  color: #fff;
}
#code-boxx , h1 { text-align: center; }
              
            
!

JS

              
                //  instance : target html table
//  data : optional, generate table with this data
function sorta (instance, data) {
  // (A) ATTACH FLAGS TO TABLE
  instance.sBy = null; // sort by this field
  instance.sDirection = true; // ascending/descending
  instance.sOrder = []; // calculated sort order

  // (B) ATTACH SORT FUNCTION TO TABLE
  instance.sort = (selected) => {
    // (B1) UPDATE SORT FLAGS
    if (instance.sBy == selected.innerHTML) {
      instance.sDirection = !instance.sDirection;
    } else {
      instance.sBy = selected.innerHTML;
      instance.sDirection = true;
    }

    // (B2) UPDATE CSS OF HEADER CELLS
    for (let c of instance.head.rows[0].cells) {
      c.classList.remove("sortup");
      c.classList.remove("sortdown");
      if (c == selected) {
        c.classList.add((instance.sDirection ? "sortup" : "sortdown"));
      }
    }

    // (B3) MAP OUT DATA OF THE SELECTED COLUMN
    // I.E. WE NEED TO RETAIN THE INDEX POSITIONS WHILE SORTING
    let map = data[selected.innerHTML].map((v, i) => { return { i: i, v: v }; });

    // (B4) SORT ARRAY
    if (instance.sDirection) {
      map.sort((a, b) => {
        if (a.v > b.v) { return 1; }
        if (a.v < b.v) { return -1; }
        return 0;
      });
    } else {
      map.sort((a, b) => {
        if (a.v < b.v) { return 1; }
        if (a.v > b.v) { return -1; }
        return 0;
      });
    }

    // (B5) REDRAW TABLE WITH NEW SORT ORDER
    instance.sOrder = [];
    for (let idx in map) { instance.sOrder.push(map[idx].i); }
    delete(map); instance.draw();
  };

  // (C) FUNCTION TO REDRAW TABLE DATA
  instance.draw = () => {
    // (C1) REMOVE OLD SORT ORDER
    instance.body.innerHTML = "";

    // (C2) DRAW NEW SORT ORDER
    let r, c;
    for (let i of instance.sOrder) {
      r = instance.body.insertRow();
      for (let key in data) {
        c = r.insertCell();
        c.innerHTML = data[key][i];
      }
    }
  };

  // (D) ADAPT EXISTING TABLE TO SORTABLE TABLE
  if (data==undefined) {
    // (D1) GET TABLE SECTIONS
    instance.head = instance.querySelector("thead");
    instance.body = instance.querySelector("tbody");

    // (D2) GET DATA FROM HEADER
    data = {}; keys = [];
    for (let c of instance.head.rows[0].cells) {
      data[c.innerHTML] = [];
      keys.push(c.innerHTML);
    }

    // (D3) GET DATA FROM BODY
    for (let r of instance.body.rows) { for (let i=0; i<r.cells.length; i++) {
      data[keys[i]].push(r.cells[i].innerHTML);
    }}
    delete(keys);
  }

  // (E) DRAW SORTABLE TABLE FROM OBJECT
  else {
    // (E1) CREATE TABLE SECTIONS
    instance.head = instance.createTHead();
    instance.body = instance.createTBody();

    // (E2) HEADER CELLS
    let r = instance.head.insertRow();
    r = instance.head.rows[0];
    for (let key in data) {
      let c = r.insertCell();
      c.innerHTML = key;
    }

    // (E3) DEFAULT SORT ORDER & DRAW BODY
    for (let i=0; i<data[Object.keys(data)[0]].length; i++) { instance.sOrder.push(i); }
    instance.draw();
  }

  // (F) ADD CSS CLASS + ATTACH CLICK TO SORT
  instance.classList.add("sorta");
  for (let r of instance.head.rows) { for (let c of r.cells) {
    c.onclick = () => { instance.sort(c); };
  }}
}

// (G) ATTACH SORTABLE TABLE ON LOAD
window.onload = () => {
  sorta(document.getElementById("demoA"));
};
              
            
!
999px

Console