Before you read ahead: ES6 may not be used.
Consider a web-page showing a report of some data that is structured as follows:
<button id='sort-by-category'>Category</button>
<button id='sort-by-timestamp'>Timestamp</button>
<section id='report-body'>
<section class='report-section' data-category-name='fruit' data-item-id='1' data-timestamp='2016-09-12T15:47:45.000Z'>
...
</section>
<section class='report-section' data-category-name='fruit' data-item-id='2' data-timestamp='2016-06-11T15:47:45.000Z'>
...
</section>
<section class='report-section' data-category-name='vegetable' data-item-id='1' data-timestamp='2016-09-10T15:47:45.000Z'>
...
</section>
</section>
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 < string2) {
return -1;
} else if (string1 > 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 < 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.
1 Answer 1
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);
}());
<button id='sort-by-category'>Category</button>
<button id='sort-by-timestamp'>Timestamp</button>
<section id='report-body'>
<section class='report-section' data-category-name='fruit' data-item-id='1' data-timestamp='2016-09-12T15:47:45.000Z'>
..a
</section>
<section class='report-section' data-category-name='fruit' data-item-id='2' data-timestamp='2016-06-11T15:47:45.000Z'>
..b
</section>
<section class='report-section' data-category-name='vegetable' data-item-id='1' data-timestamp='2016-09-10T15:47:45.000Z'>
..c
</section>
</section>
} else if (string1 > string2) {
does not require theelse
. Markup looks fine, is it being build dynamically on the server? \$\endgroup\$addRearrangeCallbackToElement
\$\endgroup\$