0
\$\begingroup\$

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.

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Sep 19, 2016 at 18:55
\$\endgroup\$
8
  • \$\begingroup\$ Any event listeners for the buttons? } else if (string1 > string2) { does not require the else. Markup looks fine, is it being build dynamically on the server? \$\endgroup\$ Commented Sep 19, 2016 at 21:25
  • \$\begingroup\$ @Xotic750 the event listeners for the buttons are added in addRearrangeCallbackToElement \$\endgroup\$ Commented Sep 19, 2016 at 21:28
  • \$\begingroup\$ @Xotic750 and this page is being built dynamically, yes. \$\endgroup\$ Commented Sep 19, 2016 at 21:30
  • \$\begingroup\$ So they are. Nothing seemed to happen when I built a fiddle, so I assumed they were missing. No wrap needed to be switched on. :) \$\endgroup\$ Commented Sep 19, 2016 at 21:32
  • \$\begingroup\$ @Xotic750 it's all good, I appreciate the feedback! \$\endgroup\$ Commented Sep 19, 2016 at 21:42

1 Answer 1

1
\$\begingroup\$

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>

answered Sep 19, 2016 at 22:45
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.