Rearranging DOM elements using inconsistent criteria Before you read ahead: ES6 may not be used.

Consider a web-page showing a report of some data that is structured as follows:

&lt;button id='sort-by-category'&gt;Category&lt;/button&gt;
&lt;button id='sort-by-timestamp'&gt;Timestamp&lt;/button&gt;
&lt;section id='report-body'&gt;
    &lt;section class='report-section' data-category-name='fruit' data-item-id='1' data-timestamp='2016-09-12T15:47:45.000Z'&gt;
        ...
    &lt;/section&gt;
    &lt;section class='report-section' data-category-name='fruit' data-item-id='2' data-timestamp='2016-06-11T15:47:45.000Z'&gt;
        ...
    &lt;/section&gt;
    &lt;section class='report-section' data-category-name='vegetable' data-item-id='1' data-timestamp='2016-09-10T15:47:45.000Z'&gt;
        ...
    &lt;/section&gt;
&lt;/section&gt;


Such that each section in the report body needs to be sorted according to the custom data- attributes. The catch is that not all attributes are created equal; for example: sorting by timestamp is straightforward, but sorting by item-id isn't because we also want to group items of the same category together.

Here's the implementation of that in JS:

(function () {
    "use strict";

    var categoryOrder = {
        "fruit" : 0,
        "vegetable" : 1
    };


    function compareStrings(string1, string2) {
        if (string1 &lt; string2) {
            return -1;
        } else if (string1 &gt; string2) {
            return 1;
        }
        return 0;
    }


    function compareCategories(element1, element2) {
        var category1 = element1.getAttribute("data-category-name");
        var category2 = element2.getAttribute("data-category-name");

        if (category1 !== category2) {
            return categoryOrder[category1] - categoryOrder[category2];
        }

        category1 = +element1.getAttribute("data-item-id");
        category2 = +element2.getAttribute("data-item-id");

        return category1 - category2;
    }


    // dates are ISO-8601
    function compareTimestamps(element1, element2) {
        var timeStamp1 = element1.getAttribute("data-timestamp");
        var timeStamp2 = element2.getAttribute("data-timestamp");
        return compareStrings(timeStamp1, timeStamp2);
    }


    function getElementsByClassName(className) {
        var result = document.getElementsByClassName(className);
        return Array.prototype.slice.call(result);
    }


    function makeRearrangeCallback(comparator, reportBody) {
        return function() {
            var reportBlocks = getElementsByClassName("report-section");

            reportBlocks.sort(comparator);

            for (var i = 0; i &lt; reportBlocks.length; ++i) {
                reportBody.appendChild(reportBlocks[i]);
            }
        };
    }


    function addRearrangeCallbackToElement(elementId, comparator, reportBody) {
        var element = document.getElementById(elementId);

        element.addEventListener(
            "click",
            makeRearrangeCallback(comparator, reportBody),
            false);
    }


    function main() {
        var reportBody = document.getElementById("report-body");
        addRearrangeCallbackToElement("sort-by-category", compareCategories, reportBody);
        addRearrangeCallbackToElement("sort-by-timestamp", compareTimestamps, reportBody);
    }


    window.addEventListener("load", main, false);
}());


Note that I'm not looking just for commentary on the JS part. If you believe that there could be a better way to store the information in the markup, feel free to mention it.
If I were to make any changes then this would be the code, using ES5, and reduce the number of functions a little, and variable assignments.



(function() {
  'use strict';

  var slice = Function.prototype.call.bind(Array.prototype.slice);
  var categoryOrder = {
    fruit: 0,
    vegetable: 1
  };

  function compareCategories(element1, element2) {
    var category1 = element1.getAttribute('data-category-name');
    var category2 = element2.getAttribute('data-category-name');
    if (category1 !== category2) {
      return categoryOrder[category1] - categoryOrder[category2];
    }
    return element1.getAttribute('data-item-id') - element2.getAttribute('data-item-id');
  }

  // dates are ISO-8601
  function compareTimestamps(element1, element2) {
    return element1.getAttribute('data-timestamp')
      .localeCompare(element2.getAttribute('data-timestamp'));
  }

  function makeRearrangeCallback(comparator, reportBody) {
    return function() {
      slice(document.getElementsByClassName('report-section'))
        .sort(comparator)
        .forEach(function(reportBlock) {
          reportBody.appendChild(reportBlock);
        });
    };
  }

  function addRearrangeCallbackToElement(elementId, comparator, reportBody) {
    document.getElementById(elementId)
      .addEventListener('click', makeRearrangeCallback(comparator, reportBody), false);
  }

  function main() {
    var reportBody = document.getElementById('report-body');
    addRearrangeCallbackToElement('sort-by-category', compareCategories, reportBody);
    addRearrangeCallbackToElement('sort-by-timestamp', compareTimestamps, reportBody);
  }

  window.addEventListener('load', main, false);
}());
&lt;button id='sort-by-category'&gt;Category&lt;/button&gt;
&lt;button id='sort-by-timestamp'&gt;Timestamp&lt;/button&gt;
&lt;section id='report-body'&gt;
  &lt;section class='report-section' data-category-name='fruit' data-item-id='1' data-timestamp='2016-09-12T15:47:45.000Z'&gt;
    ..a
  &lt;/section&gt;
  &lt;section class='report-section' data-category-name='fruit' data-item-id='2' data-timestamp='2016-06-11T15:47:45.000Z'&gt;
    ..b
  &lt;/section&gt;
  &lt;section class='report-section' data-category-name='vegetable' data-item-id='1' data-timestamp='2016-09-10T15:47:45.000Z'&gt;
    ..c
  &lt;/section&gt;
&lt;/section&gt;