Unfortunately, I could not solve that myself. I have a list with several DIV which ALWAYS have the same ID and class. The list I sort according to the "data-sort" attributes and it works wonderfully.
Currently it looks like this:
<div id="divList">
<div id="hsdf" data-sort="1"></div>
<div class="hsdf" data-sort="1"></div>
<div class="vasfd" data-sort="2"></div>
<div id="vasfd" data-sort="2"></div>
<div id="asdfas" data-sort="3"></div>
<div class="asdfas" data-sort="3"></div>
</div>
I would like however in the list sorting first the class and then the ID comes. Like here:
<div id="divList">
<div class="hsdf" data-sort="1"></div>
<div id="hsdf" data-sort="1"></div>
<div class="vasfd" data-sort="2"></div>
<div id="vasfd" data-sort="2"></div>
<div class="asdfas" data-sort="3"></div>
<div id="asdfas" data-sort="3"></div>
</div>
The "data-sort" attribute sorting is done with the jQuery:
jQuery('#divList').find('div').sort(function (a, b) {
return jQuery(a).attr('data-sort') - jQuery(b).attr('data-sort');
}).appendTo('#divList');
Do you have any ideas for me?
You can use this in your sort function:
return jQuery(a).attr('data-sort') - jQuery(b).attr('data-sort')
|| !!jQuery(a).attr("id") - !!jQuery(a).attr("class");
This checks for the existence of an id and for a class. If the class exists, the expression after || will evalutate to -1, if the ID exists, it will evalutate to 1. This will be relevant if the expression before || evaluates to 0 (when they have the same data-sort value).
Something like this should work:
jQuery('#divList').find('div').sort(function (a, b) {
var diff = jQuery(a).attr('data-sort') - jQuery(b).attr('data-sort');
if (diff == 0) {
if (jQuery(a).attr('class') && jQuery(a).attr('class') != '') {
diff = -1;
} else {
diff = 1;
}
}
return diff;
}).appendTo('#divList');
Related
I have a list which consists of list dividers and items. I want to order items underneath a list divider alphabetically, one by one. Before I can do this, I need those to be grouped. The problem is that those are not ordened within a div, but those are on the same level. I have tried a lot but cannot find out how to group those in an array sothat I am able to loop through the groups.
The html structure I have is the following:
<div class="list">
<div class="list-divider">A list divider</div>
<div class="element">A</div>
<div class="element">B</div>
<div class="list-divider">A list divider</div>
<div class="element">G</div>
<div class="element">A</div>
<div class="element">C</div>
<div class="element">B</div>
<div class="list-divider">A list divider</div>
<div class="element">A</div>
<div class="element">X</div>
<div class="element">X</div>
<div class="element">C</div>
</div>
Is there an easy way to group those into an array?
Heres what I was able to come up with:
var sortable = [];
var newHTML = "";
$(".list > div").each(function(key, value) {
if (value.className == "list-divider"){
sortable.push([]);
}
else {
sortable[sortable.length-1].push(value);
}
})
for (i in sortable)
{
sortable[i].sort(function(a,b) {return (a.innerHTML > b.innerHTML) ? 1 : ((b.innerHTML > a.innerHTML) ? -1 : 0);} );
}
$(".list > .list-divider").each(function(key, value) {
newHTML += value.outerHTML;
for (i in sortable[key])
newHTML += sortable[key][i].outerHTML;
})
$(".list").html(newHTML)
Here is a link to my code
I'm ordering some div with jQuery/Javascript: first for an attribute, than for another.
This is my code:
<div id="parent">
<div class="item" data-title="Marco" data-count="1">Marco (1)</div>
<div class="item selected" data-title="Fabio" data-count="5">Fabio (5)</div>
<div class="item selected" data-title="Edoardo" data-count="4">Edoardo (4)</div>
<div class="item " data-title="Paolo" data-count="8">Paolo (8)</div>
<div class="item selected" data-title="Luca" data-count="0">Luca (0)</div>
<div class="item" data-title="Andrea" data-count="4">Andrea (4)</div>
</div>
Order('#parent');
function Order(type) {
var items = $(type + ' > .item');
items.sort(function (a, b) {
return +b.getAttribute('data-count') - +a.getAttribute('data-count') || a.getAttribute('data-title') > b.getAttribute('data-title');
}).detach().appendTo($(type));
}
How you can see in the example, first is ordered by data-count, than by the data-title of the content.
What I'd like to do now is to put first the elements with the selected class on top; the other later.
But each "block" must be ordered as well by data-count and data-title. So in the example the result should be:
Fabio (5)
Edoardo (4)
Luca (0)
Paolo (8)
Andrea (4)
Marco (1)
how would you add a "order by class" here?
Start by comparing the results of hasClass():
(+$(b).hasClass('selected') - +$(a).hasClass('selected'))
(+ boolean is evaluated as 1 or 0 for true or false)
Order('#parent');
function Order(type) {
var items = $(type + ' > .item');
items.sort(function(a, b) {
return (+$(b).hasClass('selected') - +$(a).hasClass('selected')) ||
(+b.getAttribute('data-count') - +a.getAttribute('data-count')) ||
(a.getAttribute('data-title').localeCompare(b.getAttribute('data-title')));
}).appendTo($(type));
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="parent">
<div class="item" data-title="Marco" data-count="1">Marco (1)</div>
<div class="item selected" data-title="Fabio" data-count="5">Fabio (5)</div>
<div class="item selected" data-title="Edoardo" data-count="4">Edoardo (4)</div>
<div class="item " data-title="Paolo" data-count="8">Paolo (8)</div>
<div class="item selected" data-title="Luca" data-count="0">Luca (0)</div>
<div class="item" data-title="Andrea" data-count="4">Andrea (4)</div>
</div>
Note the corrected string comparison (via localeCompare()), and that detach() isn't needed.
To sort on a primary and a secondary key, the general approach is to return a -1 or +1 when you detect that the primary keys are different, and only move on to the secondary key when the primary keys are the same. edit In your case you have three keys: the presence/absence of "selected" in the "class", the count, and the title:
items.sort(function (a, b) {
// test for the "selected" class
var aSel = /\bselected\b/.test(a.className), bSel = /\bselected\b/.test(b.className);
if (aSel && !bSel) return -1;
if (bSel && !aSel) return 1;
// test the counts
var prim = +b.getAttribute('data-count') - +a.getAttribute('data-count');
if (prim) return prim;
// compare titles
var aTitle = a.getAttribute('data-title'), bTitle = b.getAttribute('data-title');
return aTitle < bTitle ? -1 :
aTitle > bTitle ? 1 :
0;
}).detach().appendTo($(type));
That way, if the count on one row is larger than the count on the next, there's no need to bother with the title so we don't even compare them. But if the counts are equal, then the title comparison determines the ordering.
That can be generalized to as many keys as you wanted of course.
Order('#parent');
function Order(type) {
var items = $(type + ' > .item');
items.sort(function (a, b) {
return $(b).hasClass('selected') || a.getAttribute('data-count').toLowerCase().localeCompare(b.getAttribute('data-count').toLowerCase())
|| a.getAttribute('data-title').toLowerCase().localeCompare(b.getAttribute('data-title').toLowerCase());
}).detach().appendTo($(type));
}
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
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>
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
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.
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.
The column priority list order (first by category or by name? You decide).
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');
<div id="table1"></div>
<div id="table2"></div>
<div id="table3"></div>
<div id="table4"></div>
<div id="table5"></div>
...
How to select all elements with "table" and an ID > 4.
Something like this might help you:
var test = /table(\d+)/;
$("[id^='table']").filter(function () {
return parseInt(test.exec(this.id)[1], 10) > 4;
});
It will match all elements starting with 'table' and then filter out those ending with values smaller than or equal to 4.
You can go over each element that the id starts with table [id^='table'] and check if the rest (the number) is bigger than 4
Example here http://jsfiddle.net/fLg00oxq/2/
$("[id^='table']").each(function(){
var id = $(this).attr('id').substr(5); // remove table and leave just the number
if(id > 4 ){
// Your code here
}
});
Edit, Updated
Try
v1 (for numerically ordered id's , i.e.g., table1 -> table2)
$("[id=table4] ~ [id*=table]")
v2 (unordered id's)
$("[id*=table]").not(function() {
// return elements that _don't_ match the selector ,
// `id` cast as `Number` <= 4
return this.id.match(/\d+/)[0] <= 4
})
See
Attribute Equals Selector [name="value"]
Next Siblings Selector (“prev ~ siblings”)
Attribute Contains Selector [name*="value"]
.not()
$("[id*=table]").not(function() {
// return elements that _don't_ match the selector ,
// `id` cast as `Number` <= 4
return this.id.match(/\d+/)[0] <= 4
})
.each(function() {
this.innerText = this.id
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="table10"></div>
<div id="table2"></div>
<div id="table9"></div>
<span></span>
<div id="table6"></div>
<div id="table5"></div>
<div id="table1"></div>
<div id="table7"></div>
<div id="table8"></div>
<div></div><br />
<div id="table3"></div>
<div id="table4"></div>
If your div they ordered will in example:
Edit your HTML :
<div class="table"></div>
<div class="table"></div>
<div class="table"></div>
<div class="table"></div>
<div class="table"></div>
Scripts
$(document).ready(function() {
$('.table:gt(2)').each(function() {
// Apply your functions for each div ....
});
}
This will do it:
$("div[id^=table]").slice(4);
Basically it selects all the div elements with id starting with "table". After that it removes four first matches and returns all the remaining jQuery objects.
So this has an assumption that there is <div id="table1"> ... <div id="table2"> ... <div id="table[n+1]">.
demo
var $tables = $('[id^=table]').filter(function(){
return +this.id.replace(/\D+/,'') > 4;
});
$tables.hide(); // Use example
the above will get the number out of the selector ID and compare if greater than 4. $tables will now result in a collection of elements returned by the jQuery's filter() method.
.replace(/\D+/,'') will remove all non-numeric (D+) Characters; the unary + will convert the String result to Number and > 4 does the desired.
For a micro-Plugin extension you can go for:
// Retrieve elements from mixed ID str+number
// Returns a collection of elements with ID number greather than the num argument
$.fn.idNumGreaterThan = function(num){
return this.filter(function() {
return +this.id.replace(/\D+/,'') > num;
});
};
$('[id^=table]').idNumGreaterThan(4).hide();
// Also with class selectors (element must have a mixed alphaNumeric ID)
$('.element').idNumGreaterThan(4).hide();
You can try with this code, however i think that there are better ways to do it:
for (var i = 4; i <= 100; i++) {
$('#table'+i)...
};
You Could try the contain selector like so $('div[id~=table]')
Reference http://api.jquery.com/attribute-contains-word-selector/
I am creating ListView using my template:
HTML:
<div id="ItemTemplate" data-win-control="WinJS.Binding.Template">
<div class="ItemTemplate">
<div class="back"></div>
<div data-win-bind="innerText:Info.shortName" class="shortName"></div>
<div data-win-bind="innerText:value Converters.BeginValue" class="value"></div>
<div data-win-bind="innerText:value Converters.EndValue" class="valueEnd"></div>
<div data-win-bind="innerText:Info.longName"></div>
<img data-win-bind="src:Info.flag" class="flag" />
<div data-win-bind="innerText:change Converters.BeginChange" class="change"></div>
<div data-win-bind="innerText:change Converters.EndValue" class="changeEnd"></div>
<div data-win-bind="innerText:changePercent Converters.BeginChangePercent" class="changePercent"></div>
<div data-win-bind="innerText:changePercent Converters.EndValue" class="changePercentEnd"></div>
</div>
</div>
The issue is when I meet the very long name I need to adjust font-size.
So I do (for each element in list):
JavaScript:
template = document.getElementById('ItemTemplate');
// Adjust font - size
var name = item.data.Info.longName;
// Split by words
var parts = name.split(' ');
// Count words
var count = parts.filter(function(value) {
return value !== undefined;
}).length;
var longNameDiv = $(template).children("div").children("div").eq(4);
if (count > 2) {
// Display very long names correctly
$(longNameDiv).removeClass();
$(longNameDiv).addClass("veryLongName");
}
var rootDiv = document.createElement('div');
template.winControl.render(item.data, rootDiv);
return rootDiv;
CSS:
.veryLongName {
font-size: 10pt;
}
But it doesn't effect selectivly. Moreover seems like it is check conditions for the first time and then just apply the same setting for remaining items. But it needs to change font-size to smaller only in case if the name is too long. So what am I doing wrong? Thanks.
Try by using following code instead, but u must include jquery for it.
jsfiddle : http://jsfiddle.net/vH6G8/
You can do this using jquery's filter
$(".ItemTemplate > div").filter(function(){
return ($(this).text().length > 5);
}).addClass('more_than5');
$(".ItemTemplate > div").filter(function(){
return ($(this).text().length > 10);
}).removeClass('more_than5').addClass('more_than10');
DEMO