foO
foO

Reputation: 13

Sort child div based on data attribute

Trying to sort children div based on data attributes

The html code below is being generated by a CM and the data can be retrieved in any random order.

the html code is

<section class="box explore">
<div id="ProductContainer"  class="row">
        <div id="1232132" data-name="B" data-category="Category_A" class="explore-cell">
         <h>B</h>
         <p>Category_A</p>
        </div>
    <div id="123" data-name="A" data-category="Category_A" class="explore-cell">
        <h>A</h>
        <p>Category_A</p>
    </div>
    <div id="1232152351" data-name="C" data-category="Category_A" class="explore-cell">
        <h>C</h>
        <p>Category_A</p>
    </div>
    <div id="12342341" data-name="E" data-category="Category_B" class="explore-cell">
        <h>E</h>
        <p>Category_B</p>
    </div>
    <div id="1325321" data-name="D" data-category="Category_B" class="explore-cell">
        <h>D</h>
        <p>Category_B</p>
    </div>

</div>

java

$('div').sort(function (a, b) {

  var contentA = $(a).attr('data-name');
  var contentB = $(b).attr('data-name');
  return (contentA < contentB) ? -1 : (contentA > contentB) ? 1 : 0;

})

Jsfiddle http://jsfiddle.net/w8gkshue/

if someone can point me in the right direct on how to best sort either by Product Name or Category.

Updated hope this gives better explination

Upvotes: 0

Views: 903

Answers (4)

axelduch
axelduch

Reputation: 10849

Why choose to sort by category or by name when you can sort by both?

I tried to write a generic multisort function generator, which should also work with the native array sort function.

JSFIDDLE HERE

A function that generates the multisort, it takes two parameters.

  1. The column priority list order (first by category or by name? You decide).
  2. I also wanted a way to provide values for columns (since you might not retrieve them the same way for each of them), it is an object that describes for each column a function to retrieve data.

Here it is

function getMultisortFn(columns, provideColumnData) {
    return function (a, b) {
        for (var i = 0, l = columns.length; i < l; i++) {
            var column = columns[i];
            var aColumnData = provideColumnData[column.name](a, column.name);
            var bColumnData = provideColumnData[column.name](b, column.name);
            if (aColumnData !== bColumnData) {
                if (column.asc) {
                    return String.prototype.localeCompare.call(aColumnData, bColumnData);
                }

                return String.prototype.localeCompare.call(bColumnData, aColumnData);    
            }
        }
    };
}

Now this is the part where you actually use the multisort generated

function retrieveDataAttribute(item, attribute) {
    return $(item).data(attribute);
}

var $container = $('#ProductContainer'); 
var $products = $container.find('div');

var multisort = getMultisortFn([{
        name: 'category',
        asc: false
    }, {
        name: 'name',
        asc: true
    }], {
        name: retrieveDataAttribute,
        category: retrieveDataAttribute
    });



$products.sort(multisort);

And finally the DOM manipulation to apply the new order

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

EDIT thanks to plalx:

  $container.detach().append($products).appendTo('section.box.explore');

Upvotes: 0

Joel Ibaceta
Joel Ibaceta

Reputation: 236

You can use .sort method like this

var $wrapper = $('#ProductContainer');

$wrapper.find('.explore-cell').sort(function (a, b) {
    return a.getAttribute('data-name') > b.getAttribute('data-name');
})
.appendTo( $wrapper );

But I don't sure about the cross browsing support

Upvotes: 1

musicinmyhead
musicinmyhead

Reputation: 1476

Calling only sort on them won't actually visually change the DOM, it just returns a sorted collection. So basically you just need to get the collection, sort it, then return it. Something like this should work:

$('#ProductContainer > div').detach().sort(function (a, b) {
  var contentA = $(a).data('name');
  var contentB = $(b).data('name');
  return (contentA < contentB) ? -1 : (contentA > contentB) ? 1 : 0;
}).appendTo('#ProductContainer');

You'll want to make sure that you use the detach() method and not remove(), as detach() will retain all of the data and events associated with the collection items.

Upvotes: 0

plalx
plalx

Reputation: 43718

EDIT: I missed the jQuery tag... leaving the answer still.

var productCt = document.getElementById('ProductContainer'),
    reInsertProductCt = tempRemove(productCt);


[].slice.call(productCt.children)
  .sort(function (a, b) {
    var aName = a.dataset.name,
        bName = b.dataset.name;
  
    return aName < bName? -1 : +(aName > bName);
  })
  .forEach(productCt.appendChild.bind(productCt));

reInsertProductCt();





function tempRemove(el) {
    var parent = el.parentNode,
        nextSibling = el.nextSibling;
  
    parent.removeChild(el);
  
    return function () {
        if (nextSibling) parent.insertBefore(el, nextSibling);
        else parent.appendChild(el);
    };
}
<div id="ProductContainer"  class="row">
        <div id="1232132" data-name="B" data-category="Category_A" class="explore-cell">
         <h>TEST NAME B</h>
         <p>TEST</p>
        </div>
    <div id="123" data-name="A" data-category="Category_A" class="explore-cell">
        <h>TEST NAME A</h>
        <p>TEST</p>
    </div>
    <div id="1232152351" data-name="C" data-category="Category_A" class="explore-cell">
        <h>TEST NAME C</h>
        <p>TEST</p>
    </div>
    <div id="12342341" data-name="E" data-category="Category_B" class="explore-cell">
        <h>TEST NAME E</h>
        <p>TEST</p>
    </div>
    <div id="1325321" data-name="D" data-category="Category_B" class="explore-cell">
        <h>TEST NAME D</h>
        <p>TEST</p>
    </div>

</div>

Upvotes: 1

Related Questions