0

I've made a simple code for sorting the parent divs by their title, however when I tried to do the same with downloads nothing has happened, nor a error or any action of the code.

I have two functions, each of them are called by button with onclick(). The first one works perfectly fine, however I didn't get how to properly sort the parent divs by the download count although I assume it should have the similar code.

Here's the example:

// Sort by name with a click of a button
function sortbyname() {
  let parent = $("#content");
  let divs = parent.children();
  var OrderedDivsByName = divs.sort(function(a, b) {
    return $(a).find(".name").text() >= $(b).find(".name").text();
  });
  parent.append(OrderedDivsByName);
}

// Let's try and sort all of the divs by download count with a click of a button
function sortbymostdwnl() {
  let parent = $("#content");
  let divs = parent.children();
  var OrderedDivsByDwnl = divs.sort(function(a, b) {
    return $(a).find(".downloads").text() - $(b).find(".downloads").text();
  })
  parent.append(OrderedDivsByDwnl);
}
<div id="content">
  <div class="wrapper">
    <div class="name">Test</div>
    <div class="downloads">
      45
      <em class="download_icon"></em>
    </div>
  </div>
  <div class="wrapper">
    <div class="name">Awesome Test</div>
    <div class="downloads">
      15
      <em class="download_icon"></em>
    </div>
  </div>
</div>

Can somebody give me an example of how to properly sort it by numbers inside the div and explain why it doesn't work like that? I am not very experienced with javascript and I've never found someone having a similar issue.

Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100
jeys
  • 15
  • 4
  • https://stackoverflow.com/questions/282670/easiest-way-to-sort-dom-nodes – epascarello Jan 20 '23 at 13:43
  • 1
    Seems to work fine when jQuery and the function is called. My guess is your example and what you have is a bit different? Add code and debug it. `console.log($(a).find(".downloads").text() - $(b).find(".downloads").text());` – epascarello Jan 20 '23 at 13:46
  • 1
    you are using the raw values from `text()` to make the comparison like they were numbers. You should have passed those values through `parseInt` and then use those numbers to make the comparison – Diego D Jan 20 '23 at 14:01

3 Answers3

2

You are using the raw values coming from the div content (via .text()) to make the comparison inside your sort callback like they were numbers.

You should parse first those values with parseInt and then use those numbers to make the comparison.

Here I added to your snippet the buttons bound to the 2 sorting functions, and inside the sortbymostdwnl function, I added the diagnostic to show on console the culprit and better tuned the comparison expression to make it meaningful.

*I also added an item to the list to better show the sorting

Adding noise to the html

Following the latest comments I added some noise in the .downloads elements with some <script> blocks both before and after the text content inside the .download divs.

As pointed out, the previous strategy wasn't working because the .text() method doesn't limit its action at returning the content of the children text nodes only.

So following the suggestion I found here:

Using .text() to retrieve only text not nested in child tags

I added this function:

function fetchTextNodesContent($target){
  return $target
          .clone()    //clone the element
          .children() //select all the children
          .remove()   //remove all the children
          .end()      //again go back to selected element
          .text();
}

// Sort by name with a click of a button
function sortbyname() {
  let parent = $("#content");
  let divs = parent.children();
  var OrderedDivsByName = divs.sort(function(a, b) {
    return $(a).find(".name").text() >= $(b).find(".name").text();
  });
  parent.append(OrderedDivsByName);
}

// Let's try and sort all of the divs by download count with a click of a button
function sortbymostdwnl() {
  let parent = $("#content");
  let divs = parent.children();
  var OrderedDivsByDwnl = divs.sort(function(a, b) {    
    //fetches the raw values from a and b
    const aRaw = fetchTextNodesContent($(a).find(".downloads"));
    const bRaw = fetchTextNodesContent($(b).find(".downloads"));    
    //shows them on console
    //console.log('raw values:', `a: "${aRaw}"`, `b: "${bRaw}"`);
    //parses the values
    const aParsed = parseInt(aRaw);
    const bParsed = parseInt(bRaw);
    //show them on console
    console.log('parsed values:', `a: "${aParsed}"`, `b: "${bParsed}"`);
    //performs the comparison on numbers
    return bParsed - aParsed;
  })
  parent.append(OrderedDivsByDwnl);
}

function fetchTextNodesContent($target){
  return $target
          .clone()    //clone the element
          .children() //select all the children
          .remove()   //remove all the children
          .end()      //again go back to selected element
          .text();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="content">
  <div class="wrapper">
    <div class="name">Test</div>
    <div class="downloads">
      <!-- Adding noise before -->
      <script>/**/</script>
      45
      <!-- Adding noise after -->
      <script>/**/</script>
      <em class="download_icon"></em>
    </div>
  </div>
  <div class="wrapper">
    <div class="name">111Awesome Test</div>
    <div class="downloads">
      <!-- Adding noise before -->
      <script>/**/</script>
      7
      <!-- Adding noise after -->
      <script>/**/</script>
      <em class="download_icon"></em>
    </div>
  </div>
  <div class="wrapper">
    <div class="name">Awesome Test</div>
    <div class="downloads">
      <!-- Adding noise before -->
      <script>/**/</script>
      15
      <!-- Adding noise after -->
      <script>/**/</script>
      </script>
      <em class="download_icon"></em>
    </div>
  </div>
</div>

<button onclick="sortbyname()">SortByName</button>
<button onclick="sortbymostdwnl()">SortByMostDownloaded</button>
Diego D
  • 6,156
  • 2
  • 17
  • 30
  • Seems like I have some issue, forgot to mention I have – jeys Jan 20 '23 at 14:56
  • I added ` – Diego D Jan 20 '23 at 15:13
  • Strange, I've tested it and it returns a NaN and function name inside it - `raw values: a: "display('61')40" b:"display('24')50"`. The display function is in tag is just ``. I just need to grab exactly the number that script outputs on page load so you can sort by that number but for some reason it grabs the function itself as well – jeys Jan 20 '23 at 15:38
  • please write here on a comment the exact content of your `
    ` .. one only will be enough. Ok I got it.. it's the noise before not after :) one moment
    – Diego D Jan 20 '23 at 15:40
  • 1
    I edited the answer adding a strategy to return the cleaned text nodes content from the given element as suggested in another answer on SO. It seems to work now despite the added noise before and after the content – Diego D Jan 20 '23 at 15:52
2

Besides the other answers, i will suggest a CSS based re-ordering.

If you set the container to be display: flex the you can use order: n for its children with css to re-order them (in view, be careful if you have other CSS or JS that depends on the DOM order)

Additionally, you can use Intl.Collator to make a compare function that can handle both strings and numbers correctly

function sortElementsBySelector(elements, selector) {
  const collator = new Intl.Collator('en', {
    numeric: true
  });
  const sortableElements = Array.from(elements, (element) => ({
    element,
    content: element.querySelector(selector).textContent.trim()
  }));
  
  sortableElements.sort((a, b) => {
    return collator.compare(a.content, b.content);
  });
  
  sortableElements.forEach(({element}, index) => element.style.order = index);
}


// Sort by name with a click of a button
function sortbyname() {
  const elements = document.querySelector("#content").children;
  sortElementsBySelector(elements, '.name');
}

// Let's try and sort all of the divs by download count with a click of a button
function sortbymostdwnl() {
  const elements = document.querySelector("#content").children;
  sortElementsBySelector(elements, '.downloads');
}


document.querySelector('#by-name').addEventListener('click', sortbyname);
document.querySelector('#by-downloads').addEventListener('click', sortbymostdwnl);
#content{
  display: flex;
  flex-direction: column;
}
<div id="content">
  <div class="wrapper">
    <div class="name">Awesome Test</div>
    <div class="downloads">
      45
      <em class="download_icon"></em>
    </div>
  </div>
  <div class="wrapper">
    <div class="name">Zoom Test</div>
    <div class="downloads">
      15
      <em class="download_icon"></em>
    </div>
  </div>
  <div class="wrapper">
    <div class="name">Binary Test</div>
    <div class="downloads">
      1
      <em class="download_icon"></em>
    </div>
  </div>
</div>

<button id="by-name">sort by name</button>
<button id="by-downloads">sort by downloads</button>
Gabriele Petrioli
  • 191,379
  • 34
  • 261
  • 317
1

You are nearly there. Here I added some buttons and sorted by different things - I added one for data sort you can "hide" that value but still sort.

// Sort by name with a click of a button
function sortbyname() {
  console.log('sorting Name');
  let parent = $("#content");
  let divs = parent.children();
  let OrderedDivsByName = divs.sort(function(a, b) {
    return $(a).find(".name").text() > $(b).find(".name").text() ? 1 : -1;
  });
  parent.append(OrderedDivsByName);

}

function sortbymostdwnl() {
  console.log('sorting Download');
  let parent = $("#content");
  let divs = parent.children();
  let OrderedDivsByDwnl = divs.sort(function(a, b) {
    return $(a).find(".downloads").text() - $(b).find(".downloads").text();
  });
  parent.append(OrderedDivsByDwnl);
}

function sortByData() {
  console.log('sorting data');
  let parent = $("#content");
  let divs = parent.children();
  let sorted = divs.sort(function(a, b) {
    return $(a).data("sortme") > $(b).data("sortme") ? 1 : -1;;
  });
  parent.append(sorted);
}

$('#sorts-container')
  .on('click', '.sort-things[data-sorttype="by-downloads"]', sortbymostdwnl)
  .on('click', '.sort-things[data-sorttype="by-names"]', sortbyname)
  .on('click', '.sort-things[data-sorttype="by-data"]', sortByData);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="content">
  <div class="wrapper" data-sortme="apples">
    <div class="name">Test</div>
    <div class="downloads">
      45
      <em class="download_icon"></em>
    </div>
  </div>
  <div class="wrapper" data-sortme="zippers">
    <div class="name">Awesome Test</div>
    <div class="downloads">
      15
      <em class="download_icon"></em>
    </div>
  </div>
  <div class="wrapper" data-sortme="gouda">
    <div class="name">Cheese gouda</div>
    <div class="downloads">
      7
      <em class="download_icon"></em>
    </div>
  </div>
  <div class="wrapper" data-sortme="chedder">
    <div class="name">Cheese - chedder</div>
    <div class="downloads">
      4
      <em class="download_icon"></em>
    </div>
  </div>
</div>
<div id="sorts-container">
  <button class="sort-things by-name" data-sorttype="by-names" type="button">Sort By Name</button>
  <button class="sort-things" data-sorttype="by-downloads" type="button">Sort By Downloads</button>
  <button class="sort-things" data-sorttype="by-data" type="button">Sort By data</button>
</div>
Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100