Home > Blockchain >  JavaScript Sort DOM
JavaScript Sort DOM

Time:08-04

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>

  • Related