Dennis
Dennis

Reputation: 538

Sorting my list doesn't work properly in jQuery

I've created a 4 way sorting in jQuery on my data-attributes. But when I click on a sort option, it always sorts alphabetically from A-Z for one time. When I click another time on a sorting, it doesn't do anything.

When I do a console log to see on which item is click, I get the following results:

za
az

or

1-10
za
az

So it always sorts from A-Z at the end. But honestly don't know why.

This is the example of my HTML:

<div class="sort">
    <span class="sort__item" id="js-az">A-Z</span>
    <span class="sort__item" id="js-za">Z-A</span>
    <span class="sort__item" id="js-1-10">1-10</span>
    <span class="sort__item" id="js-10-1">10-1</span>
</div>

<div class="row js-songs-sortable" id="all-song-list">
    <div class="col-md-4 col-sm-12" data-count="5" data-name="Strange Days">
        content
    </div>
    <div class="col-md-4 col-sm-12" data-count="1" data-name="Rock Bottom">
        content
    </div>
    <div class="col-md-4 col-sm-12" data-count="1" data-name="Moutain">
        content
    </div>
    <div class="col-md-4 col-sm-12" data-count="3" data-name="Mad Sun">
        content
    </div>
    <div class="col-md-4 col-sm-12" data-count="10" data-name="Another Ugly Tune">
        content
    </div>
</div>

And this is my jQuery code:

jQuery('body').on('click', '.sort #js-az', function(){
    jQuery(".js-songs-sortable > div").sort(sort_li).appendTo('.js-songs-sortable');

    function sort_li(a,b){
        return (jQuery(b).data('name')) < (jQuery(a).data('name')) ? 1 : -1;    
    }
});

jQuery('body').on('click', '.sort #js-za', function(){
    jQuery(".js-songs-sortable > div").sort(sort_li).appendTo('.js-songs-sortable');

    function sort_li(b,a){
        return (jQuery(b).data('name')) < (jQuery(a).data('name')) ? 1 : -1;    
    }
});

jQuery('body').on('click', '.sort #js-1-10', function(){
    jQuery(".js-songs-sortable > div").sort(sort_li).appendTo('.js-songs-sortable');

    function sort_li(a,b){
        return (jQuery(b).data('count')) < (jQuery(a).data('count')) ? 1 : -1;    
    }
});

jQuery('body').on('click', '.sort #js-10-1', function(){
    jQuery(".js-songs-sortable > div").sort(sort_li).appendTo('.js-songs-sortable');

    function sort_li(b,a){
        return (jQuery(b).data('count')) < (jQuery(a).data('count')) ? 1 : -1;    
    }
});

You can find the live preview here: https://mpdb.space/mp-songs/

Upvotes: 1

Views: 101

Answers (3)

Kalimah
Kalimah

Reputation: 11447

The issue with your code is that you are using .data whereas you need to use .attr().

jQuery('body').on('click', '.sort #js-az', function() {
  jQuery(".js-songs-sortable > div").sort(sort_li).appendTo('.js-songs-sortable');

  function sort_li(a, b) {
    return (jQuery(b).attr('data-name')) < (jQuery(a).attr('data-name')) ? 1 : -1;
  }
});

jQuery('body').on('click', '.sort #js-za', function() {
  jQuery(".js-songs-sortable > div").sort(sort_li).appendTo('.js-songs-sortable');

  function sort_li(b, a) {
    return (jQuery(b).attr('data-name')) < (jQuery(a).attr('data-name')) ? 1 : -1;
  }
});

jQuery('body').on('click', '.sort #js-1-10', function() {
  jQuery(".js-songs-sortable > div").sort(sort_li).appendTo('.js-songs-sortable');

  function sort_li(a, b) {
    return (parseInt(jQuery(b).attr('data-count'))) < parseInt((jQuery(a).attr('data-count'))) ? 1 : -1;
  }
});

jQuery('body').on('click', '.sort #js-10-1', function() {
  jQuery(".js-songs-sortable > div").sort(sort_li).appendTo('.js-songs-sortable');

  function sort_li(b, a) {
    return parseInt(jQuery(b).attr('data-count')) < parseInt(jQuery(a).attr('data-count')) ? 1 : -1;
  }
});
.sort {
  border-radius: 5px;
  background-color: #eee;
  display: inline-block;
  margin-bottom: 1rem;
}

.sort span {
  padding: 1rem;
  cursor: pointer;
  transition: all 0.3s;
  display: inline-block;
}

.sort span:hover {
  background-color: rgba(0, 0, 0, 0.1);
}

.row>div {
  padding: 0.5rem;
}

.row>div::after {
  content: "data-count= " attr(data-count) " data-name= " attr(data-name);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="sort">
  <span class="sort__item" id="js-az">A-Z</span>
  <span class="sort__item" id="js-za">Z-A</span>
  <span class="sort__item" id="js-1-10">1-10</span>
  <span class="sort__item" id="js-10-1">10-1</span>
</div>

<div class="row js-songs-sortable" id="all-song-list">
  <div class="col-md-4 col-sm-12" data-count="5" data-name="Strange Days">

  </div>
  <div class="col-md-4 col-sm-12" data-count="1" data-name="Rock Bottom">

  </div>
  <div class="col-md-4 col-sm-12" data-count="1" data-name="Moutain">

  </div>
  <div class="col-md-4 col-sm-12" data-count="3" data-name="Mad Sun">

  </div>
  <div class="col-md-4 col-sm-12" data-count="10" data-name="Another Ugly Tune">

  </div>
</div>

Also, you can change your code little bit for maintainability and readability. See this example:

jQuery('body').on('click', '.sort__item', function() {
  const id = jQuery(this).attr("id");

  jQuery(".js-songs-sortable > div").sort(function(a, b) {
    let firstEl = jQuery(a);
    let secondEl = jQuery(b);
    let $return = 0;

    switch (id) {
      case "js-az":
        $return = (firstEl.attr('data-name') > secondEl.attr('data-name')) ? 1 : -1;
        break;

      case "js-za":
        $return = (firstEl.attr('data-name') < secondEl.attr('data-name')) ? 1 : -1;
        break;

      case "js-1-10":
        $return = (parseInt(firstEl.attr('data-count')) > parseInt(secondEl.attr('data-count'))) ? 1 : -1;
        break;

      case "js-10-1":
        $return = (parseInt(firstEl.attr('data-count')) < parseInt(secondEl.attr('data-count'))) ? 1 : -1;
        break;
    }

    return $return;
  }).appendTo('.js-songs-sortable');
});
.sort {
  border-radius: 5px;
  background-color: #eee;
  display: inline-block;
  margin-bottom: 1rem;
}

.sort span {
  padding: 1rem;
  cursor: pointer;
  transition: all 0.3s;
  display: inline-block;
}

.sort span:hover {
  background-color: rgba(0, 0, 0, 0.1);
}

.row>div {
  padding: 0.5rem;
}

.row>div::after {
  content: "data-count= " attr(data-count) " data-name= " attr(data-name);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="sort">
  <span class="sort__item" id="js-az">A-Z</span>
  <span class="sort__item" id="js-za">Z-A</span>
  <span class="sort__item" id="js-1-10">1-10</span>
  <span class="sort__item" id="js-10-1">10-1</span>
</div>

<div class="row js-songs-sortable" id="all-song-list">
  <div class="col-md-4 col-sm-12" data-count="5" data-name="Strange Days">

  </div>
  <div class="col-md-4 col-sm-12" data-count="1" data-name="Rock Bottom">

  </div>
  <div class="col-md-4 col-sm-12" data-count="1" data-name="Moutain">

  </div>
  <div class="col-md-4 col-sm-12" data-count="3" data-name="Mad Sun">

  </div>
  <div class="col-md-4 col-sm-12" data-count="10" data-name="Another Ugly Tune">

  </div>
</div>

Upvotes: 0

OPTIMUS PRIME
OPTIMUS PRIME

Reputation: 1335

Data-attributes always return a string (by the way, input fields as well, common mistake), which comparison result differs from numbers comparison:

console.log( "9" > "111" ) // true
console.log( "310" > "31" ) // true

You have to convert them into number with unary +:

( +$(a).data('count') < +$(b).data('count') ) ? -1 : 1;

But because you need to return any positive or negative number, can just subtract them. The - operator will automatically turn them into numbers.

$('#js-az').on('click', function() {
  $(".js-songs-sortable > div").sort(sort_li).appendTo('.js-songs-sortable');
  
  function sort_li(a, b) {
    return ( $(a).data('name') < $(b).data('name') ) ? -1 : 1;
  }
});

$('#js-za').on('click', function() {
  $(".js-songs-sortable > div").sort(sort_li).appendTo('.js-songs-sortable');

  function sort_li(a, b) {
    return ( $(a).data('name') > $(b).data('name') ) ? -1 : 1;
  }
});

$('#js-1-10').on('click', function() {
  $(".js-songs-sortable > div").sort(sort_li).appendTo('.js-songs-sortable');
  
  function sort_li(a, b) {
    return $(a).data('count') - $(b).data('count');
  }
});

$('#js-10-1').on('click', function() {
  $(".js-songs-sortable > div").sort(sort_li).appendTo('.js-songs-sortable');

  function sort_li(a, b) {
    return $(b).data('count') - $(a).data('count')
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="sort">
  <span class="sort__item" id="js-az">A-Z</span>
  <span class="sort__item" id="js-za">Z-A</span>
  <span class="sort__item" id="js-1-10">1-10</span>
  <span class="sort__item" id="js-10-1">10-1</span>
</div>

<div class="row js-songs-sortable" id="all-song-list">
  <div data-count="5" data-name="Strange Days">data-count="5" data-name="Strange Days"</div>
  <div data-count="1" data-name="Rock Bottom">data-count="1" data-name="Rock Bottom"</div>
  <div data-count="1" data-name="Moutain">data-count="1" data-name="Moutain"</div>
  <div data-count="3" data-name="Mad Sun">data-count="3" data-name="Mad Sun"</div>
  <div data-count="10" data-name="Another Ugly Tune">data-count="10" data-name="Another Ugly Tune"</div>
</div>

And shortened version of your code:

var funcStorage = {
  az: (a, b) => $(a).data('name') < $(b).data('name') ? -1 : 1,
  za: (a, b) => $(a).data('name') > $(b).data('name') ? -1 : 1,
  "1-10": (a, b) => $(a).data('count') - $(b).data('count'),
  "10-1": (a, b) => $(b).data('count') - $(a).data('count'),
}; // If this seems `Alien-code`, Google → JS objects, JS arrow functions

//          (a,b) => a - b               The same
// function (a,b) { return a - b }       thig.

$('.sort__item').each(function(){
  $(this).on('click', function(){
    var func = funcStorage[ this.id.replace("js-","") ];
    // in fact, you don't need id, could store the sort key in data-sort
    $(".js-songs-sortable > div").sort(func).appendTo('.js-songs-sortable');
    
    $('.sort__item.active').removeClass('active');
    $(this).addClass('active');
  });
});
.sort__item {
  display: inline-block;
  cursor: pointer;
  border: 2px solid orange;
  margin: 5px;
  padding: 5px;
}

.sort__item.active { background-color: orange; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="sort">
  <span class="sort__item" id="js-az">A-Z</span>
  <span class="sort__item" id="js-za">Z-A</span>
  <span class="sort__item" id="js-1-10">1-10</span>
  <span class="sort__item" id="js-10-1">10-1</span>
</div>

<div class="row js-songs-sortable" id="all-song-list">
  <div data-count="5" data-name="Strange Days">data-count="5" data-name="Strange Days"</div>
  <div data-count="1" data-name="Rock Bottom">data-count="1" data-name="Rock Bottom"</div>
  <div data-count="1" data-name="Moutain">data-count="1" data-name="Moutain"</div>
  <div data-count="3" data-name="Mad Sun">data-count="3" data-name="Mad Sun"</div>
  <div data-count="10" data-name="Another Ugly Tune">data-count="10" data-name="Another Ugly Tune"</div>
</div>

Upvotes: 1

Floppy52
Floppy52

Reputation: 184

As documented here, by default, the sort() method sorts the values as strings in alphabetical and ascending order. However you can modify its behaviour by passing a compare function. You have not specified the result you are wanting to achieve so you will have to create a function with the desired sorting logic.

Upvotes: 1

Related Questions