I am trying to sort DOM based on a data-*
attribute. The DOM looks like this, albeit repeated and with incrementing ids and different image source:
<div data-id="1" data-filter="Owned">
<div >
<img alt="Default Template" src="abc.jpg">
<div >
<a role="button" target="_blank" href="organization.html/template.html?id=1">Preview</a>
</div>
<div >
<h5 >Default Template</h5>
<p >Owned</p>
</div>
</div>
</div>
I'm doing the sorting via a <select />
element where the <option />
values is a JSON literal that hold the field and direction, e.g.:
{
"field": "id",
"direction": "asc"
}
In the <select />
's change event, I have the following:
const templateSort_Change = (e) => {
const templateSort = e.currentTarget;
const templateSortValue = templateSort.value;
const sort = JSON.parse(templateSortValue);
const cols = document.querySelectorAll('div.col');
const sortedCols = Array.from(cols).sort((a, b) => {
if (sort.direction === 'desc') {
return a.dataset[sort.field].toLowerCase() - b.dataset[sort.field].toLowerCase();
} else {
return b.dataset[sort.field].toLowerCase() - a.dataset[sort.field].toLowerCase();
}
});
for (let col of cols) {
col.parentNode.appendChild(col);
}
}
The issue is that when it goes to do the for/of loop, the result of cols
is the same order every time and looking at the documentation I'm not exactly sure why. I get that I'm doing string comparison, but these ids are still single digits (e.g. 1, 2, 3, etc.).
What am I doing wrong in my sort
function that is causing the array to return the same order every time?
CodePudding user response:
For sorting strings
you need localeCompare
method, the minus ( - )
for sorting numbers not string.
const sortedCols = Array.from(cols).sort((a, b) => {
if (sort.direction === 'desc') {
return b.dataset[sort.field].localeCompare(a.dataset[sort.field])
} else {
return a.dataset[sort.field].localeCompare(b.dataset[sort.field);
}
});
CodePudding user response:
When you do string comparison with -
, it will return you a NaN
.
console.log('abc' - 'abd');
console.log('a' - 'b');
What you want to do is to use localeCompare()
.
console.log('abc'.localeCompare('abd'));
console.log('abd'.localeCompare('abc'));
console.log('a'.localeCompare('b'));
console.log('b'.localeCompare('a'));
CodePudding user response:
A few issues: First, your asc/desc sorting was backward, so I've fixed that. Second, most important, your for (let col of cols)
loop should iterate over sortedCols
, not cols
. Third, I'm not sure how you intended to update the DOM but I've done it by adding a wrapper <div>
to all your cols.
const templateSort_Change = (e) => {
const templateSort = e.currentTarget;
const templateSortValue = templateSort.value;
const sort = JSON.parse(templateSortValue);
const cols = document.querySelectorAll('div.col');
const sortedCols = Array.from(cols).sort((a, b) => {
if (sort.direction === 'asc') {
return a.dataset[sort.field].toLowerCase() - b.dataset[sort.field].toLowerCase();
} else {
return b.dataset[sort.field].toLowerCase() - a.dataset[sort.field].toLowerCase();
}
});
document.getElementById('container').innerHTML = '';
for (let col of sortedCols) {
document.getElementById('container').appendChild(col);
}
}
document.querySelector('select').addEventListener('change', templateSort_Change);
<div id="container">
<div data-id="1" data-filter="Owned">
<div >
<img alt="Default Template" src="abc1.jpg">
<div >
<a role="button" target="_blank" href="organization.html/template.html?id=1">Preview</a>
</div>
<div >
<h5 >Default Template 1</h5>
<p >Owned 1</p>
</div>
</div>
</div>
<div data-id="2" data-filter="Owned">
<div >
<img alt="Default Template" src="abc2.jpg">
<div >
<a role="button" target="_blank" href="organization.html/template.html?id=1">Preview</a>
</div>
<div >
<h5 >Default Template 2</h5>
<p >Owned 2</p>
</div>
</div>
</div>
<div data-id="3" data-filter="Owned">
<div >
<img alt="Default Template" src="abc3.jpg">
<div >
<a role="button" target="_blank" href="organization.html/template.html?id=1">Preview</a>
</div>
<div >
<h5 >Default Template 3</h5>
<p >Owned 3</p>
</div>
</div>
</div>
<div data-id="4" data-filter="Owned">
<div >
<img alt="Default Template" src="abc4.jpg">
<div >
<a role="button" target="_blank" href="organization.html/template.html?id=1">Preview</a>
</div>
<div >
<h5 >Default Template 4</h5>
<p >Owned 4</p>
</div>
</div>
</div>
</div>
<select>
<option value='{
"field": "id",
"direction": "asc"
}'>Asc</option>
<option value='{
"field": "id",
"direction": "desc"
}'>Desc</option>
</select>