0
\$\begingroup\$

Lets preface this by saying that when it comes to javascript, I am a hack. I make what I need happen by cobbling together crappy, inefficient, redundant code. I want to get better. Recently I was asked if I could make a set of elements on a page sort-able by price, A-Z and Z-A. The code on the page looks something like the below:

<select id="sorting" onchange="selectionChange()">
      <option value="Sort By">Sort Products</option>
      <option value="Sort By Price" id="price">Sort By Price</option>
      <option value="Sort By A-Z" id="az">Sort By A-Z</option>
      <option value="Sort By Z-A" id="az">Sort By Z-A</option>
</select>
<div class="products">
      <div class="product-wrapper">
            <div class="single-product">
                 <a href="item-url"><img src="path"/></a>
                 <a href="">text link</a>
                 <p>$item price</p>
            </div>
      </div>
      <div class="product-wrapper">
            <div class="single-product">
                 <a href="item-url"><img src="path"/></a>
                 <a href="">text link</a>
                 <p>$item price</p>
            </div>
      </div>
  </div>

I was able to make things work by assigning a sort class to the text link and the price "p" tag, listen for onchange from the dropdown and when it fired either the price or the name of the item was assigned as an attribute to the containing product wrapper, then the wrappers are sorted using a simple sort function. My working but embarrassing attempt is below.

jQuery(document).ready(function( $ ) {
sortPrice = function(){
$('.product-wrapper').each(function(){
    var data1 = $(this).find(".sortprice").text();
    $(this).attr('data-name', data1);

});

var $products = $('.products'),
    $singleProduct = $('.product-wrapper');

$singleProduct.sort(function(a,b){
        var an = a.getAttribute('data-name');
        var bn = b.getAttribute('data-name');
        var numberA = Number(an.replace(/[^0-9\.]+/g,""));
        var numberB = Number(bn.replace(/[^0-9\.]+/g,""));
        an = numberA;
        bn = numberB;

        if(an > bn) {
        return 1;
    }
    if(an < bn) {
        return -1;
    }
    return 0;
});

$singleProduct.detach().appendTo($products);

};


sortA = function(){
$('.product-wrapper').each(function(){
    var data1 = $(this).find(".sort1").text();
    $(this).attr('data-name', data1);

});

var $products = $('.products'),
    $singleProduct = $('.product-wrapper');

$singleProduct.sort(function(a,b){
        var an = a.getAttribute('data-name');
        var bn = b.getAttribute('data-name');

        if(an > bn) {
        return 1;
    }
    if(an < bn) {
        return -1;
    }
    return 0;
});

$singleProduct.detach().appendTo($products);

};

sortZ = function(){
$('.product-wrapper').each(function(){
    var data1 = $(this).find(".sort1").text();
    $(this).attr('data-name', data1);

});

var $products = $('.products'),
    $singleProduct = $('.product-wrapper');

$singleProduct.sort(function(a,b){
        var an = a.getAttribute('data-name');
        var bn = b.getAttribute('data-name');

        if(an > bn) {
        return -1;
    }
    if(an < bn) {
        return 1;
    }
    return 0;
});

$singleProduct.detach().appendTo($products);

};
selectionChange = function(){
var selectResult = $("#sorting").val();

if(selectResult == "Sort By Price"){
    sortPrice();

} else if(selectResult == "Sort By A-Z"){
    sortA();

} else if(selectResult == "Sort By Z-A"){
    sortZ();

}
};
});

I would be eternally indebted to whoever will show me what an elegant version would look like and maybe how and where I would add error handling.

\$\endgroup\$
5
  • \$\begingroup\$ See this: stackoverflow.com/questions/7831712/… \$\endgroup\$
    – Red2678
    Commented Mar 27, 2015 at 12:54
  • 1
    \$\begingroup\$ Someone already figured it out for you. Use plugins like TinySort : tinysort.sjeiti.com $("select").tinysort() et voila \$\endgroup\$
    – Jeremy Thille
    Commented Mar 27, 2015 at 12:57
  • \$\begingroup\$ What is $singleProduct.sort(function(a,b) ? there is no sort method in jQuery... \$\endgroup\$
    – vsync
    Commented Mar 27, 2015 at 13:13
  • 1
    \$\begingroup\$ You say this is working code but it certainly doesn't work with the html provided. Your code looks for elements that simply don't exist. .sort1, .sortprice, etc. \$\endgroup\$ Commented Apr 2, 2015 at 14:58
  • \$\begingroup\$ @vsync The jquery object exposes the native array sort though it's intended for internal use only apparently: blog.troygrosfield.com/2014/04/25/jquery-sorting \$\endgroup\$ Commented Apr 2, 2015 at 15:01

1 Answer 1

1
\$\begingroup\$

All the sort functions follow the same pattern, with a few key differences:

  • A field to extract for comparison (.sortprice, .sort1)
  • A comparison function (numeric, alphabetic, ascending and descending)

These can be parameters for a general-purpose implementation:

sortHelper = function(search, comparator) {
    $('.product-wrapper').each(function() {
        var data = $(this).find(search).text();
        $(this).attr('data-name', data);
    });

    var $products = $('.products'),
        $singleProduct = $('.product-wrapper');

    $singleProduct.sort(comparator);

    $singleProduct.detach().appendTo($products);
};

The price comparator function can be implemented simpler than you did, considering that it doesn't need to return precisely -1, 1 values, any other negative and positive values would be fine:

var cmpPrice = function(a, b) {
    var an = a.getAttribute('data-name');
    var bn = b.getAttribute('data-name');
    var numberA = Number(an.replace(/[^0-9\.]+/g, ""));
    var numberB = Number(bn.replace(/[^0-9\.]+/g, ""));
    return numberA - numberB;
};

The string comparator function can be much like it was:

var cmpName = function(a, b) {
    var an = a.getAttribute('data-name');
    var bn = b.getAttribute('data-name');
    if (an > bn) {
        return 1;
    }
    if (an < bn) {
        return -1;
    }
    return 0;
});

The string comparator in descending order can be implemented in terms of the other one in ascending order:

var cmpNameDesc = function(a, b) {
    return cmpName(b, a);
});

And then the main function can be implemented in terms of these helpers:

selectionChange = function() {
    var selectResult = $("#sorting").val();

    if (selectResult == "Sort By Price") {
        sortHelper('.sortprice', cmpPrice);
    } else if (selectResult == "Sort By A-Z") {
        sortHelper('.sort1', cmpName);
    } else if (selectResult == "Sort By Z-A") {
        sortHelper('.sort1', cmpNameDesc);
    }
};
\$\endgroup\$

Your Answer

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