Sort Table

Sort an HTML/CSS table using Plain Old Vanilla Javascript

by Stephen Irving

HTML

<div class="tile ttzc-tile">
		<header class="tile-header">
			<h2 class="tile-header-title">Top Ten ZIP Codes</h2>
		</header>
		<div class="ttzc-table-wrap">
			<table class="ttzc-table" id="ttzc_table">
				<thead class="ttzc-head">
					<tr class="ttzc-row">
						<th class="ttzc-header ttzc-zip" scope="col" title="Click to sort numerically">ZIP Code</th>
						<th class="ttzc-header ttzc-city" scope="col" title="Click to sort alphabetically">City</th>
						<th class="ttzc-header ttzc-state" scope="col" title="Click to sort alphabetically">State</th>
						<th class="ttzc-header ttzc-pcentvis" scope="col" title="Click to sort numerically">Percent Visits</th>
						<th class="ttzc-header ttzc-cumpcentvis" scope="col" title="Click to sort numerically">Cumulative<br>Percent<br>Visits</th>
						<th class="ttzc-header ttzc-tothouse" scope="col" title="Click to sort numerically">Total<br>Households</th>
						<th class="ttzc-header ttzc-totpop" scope="col" title="Click to sort numerically">Total<br>Population</th>
						<th class="ttzc-header ttzc-avghouseinc" scope="col" title="Click to sort numerically">Avg<br>Household<br>Income</th>
						<th class="ttzc-header ttzc-popgrow" scope="col" title="Click to sort numerically">Population<br>Growth</th>
					</tr>
				</thead>
				<tbody class="ttzc-tbody">
					<tr class="ttzc-row">
						<td class="ttzc-cell ttzc-zip">01736</td>
						<td class="ttzc-cell ttzc-city">Southwick</td>
						<td class="ttzc-cell ttzc-state">MA</td>
						<td class="ttzc-cell ttzc-pcentvis">32.8%</td>
						<td class="ttzc-cell ttzc-cumpcentvis">32.8%</td>
						<td class="ttzc-cell ttzc-tothouse">3,792</td>
						<td class="ttzc-cell ttzc-totpop">9,629</td>
						<td class="ttzc-cell ttzc-avghouseinc">$85,876</td>
						<td class="ttzc-cell ttzc-popgrow">1.1%</td>
					</tr>
					<tr class="ttzc-row">
						<td class="ttzc-cell ttzc-zip">01739</td>
						<td class="ttzc-cell ttzc-city">Winchendon</td>
						<td class="ttzc-cell...

SCSS

$fontFamily:  'Roboto',
              'Helvetica Neue',
              HelveticaNeue,
              Helvetica-Neue,
              Helvetica,
              -apple-system,
              BlinkMacSystemFont,
              BBAlpha Sans,
              'Segoe UI',
              Arial,
              'Noto Sans',
              sans-serif;

$latoFamily:  'Lato',
              Verdana,
              Geneva,
              -apple-system,
              BlinkMacSystemFont,
              sans-serif;

// Colors
$darkBlue   : #2A3C68;
$blue       : #4FA2D9;
$lightBlue  : #8ED8F8;
$lightPurple: #EDEDF6;
$lightYellow: #FCE6A3;
$black      : #000;
$offBlack   : #414042;
$gray       : #6D6E71;
$silver     : #BCBEC0;
$offWhite   : #F1F2F2;
$white      : #FFF;
$brown      : #A29490;

// PinPoint Colors
$darkRed    : #A11D27;
$red        : #D51F27;
$lightRed   : lighten($red, 15%);
$almond     : #ECD8C0;


$gutters: (
  'lg': 1.6%,
  'md': 1%,
  'sm': .75%
);

$h-gutter-lg: 1.6%;
$h-gutter-md: 1%;
$h-gutter-sm: .75%;
$v-gutter: .75%;

// _helpers.scss

$rem-base: 16 !default;

@function strip-unit($num) {
  @return $num / ($num * 0 + 1);
}

@function conv-to-rem($pixels) {
  $rem-vals: ();

  @each $val in $pixels {
    $val-in-rems: strip-unit($val) / $rem-base * 1rem;
    $rem-vals: append($rem-vals, $val-in-rems);
  }

  @if length($rem-vals) == 1 {
    @return nth($rem-vals, 1);
  }
  @else {
    @return $rem-vals;
  }
}

@function conv-to-px($rems) {
  $px-vals: ();

  @each $val in $rems {
    $val-in-pixels: strip-unit($val) * $rem-base * 1px;
    $px-vals: append($px-vals, $val-in-pixels)
  }

  @if length($px-vals) == 1 {
    @return nth($px-vals, 1);
  }
  @else {
    @return $px-vals;
  }
}

/// Replace `$search` with `$replace` in `$string`
/// @param {String} $string - Initial string
/// @param {String} $search - Substring to replace
/// @param {String} $replace ('') - New value
/// @return {String} - Updated string
@function str-replace($string, $search, $replace: '') {
 ...

JavaScript

/**
 * @name sortTable
 * @description Takes a given table and column, as well as whether or not the
 * column has already been reversed, as input, and then sorts the table column
 *
 * @param {HTMLElement} table
 * @param {HTMLElement} col
 * @param {number} reverse - 1 for true 0 for false
 * @todo Refactor logic for optimization & implement a datetime sorting solution
 */
function sortTable(table, col, reverse) {
  const T_BODY = table.tBodies[0], // ignore `<thead>` and `<tfoot>` rows
        IS_NUM = /^[0-9]*\.?[0-9]+$/;

  let tRows = Array.prototype.slice.call(T_BODY.rows, 0); // Put rows into array

  reverse = -((+reverse) || -1);

  tRows = tRows.sort(function(a, b) { // Sort rows
    let tempA = a.cells[col].textContent.trim(),
        tempB = b.cells[col].textContent.trim();

    if (tempA.slice(-1) === '%' || tempB.slice(-1) === '%') {
      if (tempA.slice(-1) === '%') {
        tempA = tempA.slice(0, tempA.length - 1);
      }
      if (tempB.slice(-1) === '%') {
        tempB = tempB.slice(0, tempB.length - 1);
      }
      [tempA, tempB] = fixComparison(tempA, tempB);
    }
    else if (tempA.substr(0, 1) === '$' || tempB.substr(0, 1) === '$') {
      if (tempA.substr(0, 1) === '$') {
        tempA = tempA.slice(1, tempA.length);
      }
      if (tempB.substr(0, 1) === '$') {
        tempB = tempB.slice(1, tempB.length);
      }
      [tempA, tempB] = fixComparison(tempA, tempB);
    }
    else if (IS_NUM.test(stripCommas(tempA)) || IS_NUM.test(stripCommas(tempB))) {
      if (IS_NUM.test(stripCommas(tempA))) {
        tempA = stripCommas(tempA) + '';
      }
      if (IS_NUM.test(stripCommas(tempB))) {
        tempB = stripCommas(tempB) + '';
      }
      [tempA, tempB] = fixComparison(tempA, tempB);
    }

    return reverse * (tempA.localeCompare(tempB));
  });

  for (let i = 0; i < tRows.length; ++i) {
    T_BODY.appendChild(tRows[i]); // append each row in order
  }
}

/**
 * @name makeSortable
 * @description Makes a table sortable
 *
...