简体   繁体   中英

Sorting an unordered list of articles using JavaScript

I have an unordered list of articles, each with a heading and a paragraph. I would like to sort these articles alphabetically using JavaScript, but I get an unexpected result.

The JavaScript sorting function that I am using (originally taken from: https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_sort_list ) can be seen below:

function sort() {
  var list, i, switching, b, shouldSwitch;
  list = document.getElementById("courses");
  switching = true;
  /*Make a loop that will continue until
  no switching has been done:*/
  while (switching) {
    //start by saying: no switching is done:
    switching = false;
    b = list.getElementsByTagName("LI");
    //Loop through all list-items:
    for (i = 0; i < (b.length - 1); i++) {
      //start by saying there should be no switching:
      shouldSwitch = false;
      /*check if the next item should
      switch place with the current item:*/
      if (b[i].innerHTML.toLowerCase() > b[i + 1].innerHTML.toLowerCase()) {
        /*if next item is alphabetically
        lower than current item, mark as a switch
        and break the loop:*/
        shouldSwitch= true;
        break;
      }
    }
    if (shouldSwitch) {
      /*If a switch has been marked, make the switch
      and mark the switch as done:*/
      b[i].parentNode.insertBefore(b[i + 1], b[i]);
      switching = true;
    }
  }
}

The full code can be found at: https://jsfiddle.net/MihkelPajunen/c37m0w1s/1/

The sorted order should be:

  1. CSS
  2. HTML
  3. Java
  4. JavaScript
  5. Python
  6. Ruby

My question is twofold: how come my supposedly sorted articles look the way they do, and what should I change to produce the alphabetical order that I am after?

The problem is ironically with you HTML comment, which gets treated as part of the .innerHTML . Because the first letter of the comment ( < ) is considered to be a 'lesser' ASCII character than C ( &#60; versus &#67; ), the HTML article containing the comment gets sorted to the top of the list alphabetically.

Simply removing this comment from your HTML article causes the elements to sort as expected:

 //Script borrowed from: https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_sort_list function sort() { var list, i, switching, b, shouldSwitch; list = document.getElementById("courses"); switching = true; /*Make a loop that will continue until no switching has been done:*/ while (switching) { //start by saying: no switching is done: switching = false; b = list.getElementsByTagName("LI"); //Loop through all list-items: for (i = 0; i < (b.length - 1); i++) { //start by saying there should be no switching: shouldSwitch = false; /*check if the next item should switch place with the current item:*/ if (b[i].innerHTML.toLowerCase() > b[i + 1].innerHTML.toLowerCase()) { /*if next item is alphabetically lower than current item, mark as a switch and break the loop:*/ shouldSwitch = true; break; } } if (shouldSwitch) { /*If a switch has been marked, make the switch and mark the switch as done:*/ b[i].parentNode.insertBefore(b[i + 1], b[i]); switching = true; } } } 
 article { border: 1px solid black; margin: 10px; padding: 10px; } 
 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>Sorting Articles</title> </head> <body> <button onclick="sort()">Sort list items</button> <section> <h1>All courses:</h1> <ul id="courses"> <li> <article> <h2>HTML (1)</h2> <p>Some text about the HTML course.</p> </article> </li> <li> <article> <h2>CSS (2)</h2> <p>Some text about the css course.</p> </article> </li> <li> <article> <h2>JavaScript (3)</h2> <p>Some text about the JavaScript course.</p> </article> </li> <li> <article> <h2>Ruby (4)</h2> <p>Some text about the Ruby course.</p> </article> </li> <li> <article> <h2>Python (5)</h2> <p>Some text about the Python course.</p> </article> </li> <li> <article> <h2>Java (6)</h2> <p>Some text about the Java course.</p> </article> </li> </ul> </section> </body> </html> 

When comparing text, you should not use innerHTML .

Use innerText instead. Here is an updated fiddle with this change made:

if (b[i].innerText.toLowerCase() > b[i + 1].innerText.toLowerCase()) {

I can understand how you came to use it though, since w3schools does. This is one of many reasons why you should completely avoid that website. Another glaring problem they have is the use of document.write. That aside, try to use higher quality examples in the future, and avoid w3schools if you would like to avoid bad practice.

Here are some higher quality content providers:

In addition to the other answers, here's a greatly simplified version of your code. Readability can still be improved. I've added quite a bit of extra information with links to the Mozilla Developer Network (MDN) documentation to show you how much information is missing on the W3Schools site.


Code

 function sort () { let container = document.getElementById('courses') Array.from(container.querySelectorAll('li > article > h2:first-child')) .sort((h2_a, h2_b) => h2_a.innerText.localeCompare(h2_b.innerText)) .forEach(h2 => container.appendChild(h2.parentElement.parentElement)) } 
 article { border: 1px solid black; margin: 10px; padding: 10px; } 
 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>Sorting Articles</title> </head> <body> <!-- Hit the button to sort items (at least something happens) --> <button onclick="sort()">Sort list items</button><br> <!-- This section contains six articles--> <section> <h1>All courses:</h1> <!-- The articles are structured in an unordered list--> <ul id="courses"> <li> <!-- Each list item is a complete article --> <article> <h2>HTML (1)</h2> <p>Some text about the HTML course.</p> </article> </li> <li> <article> <h2>CSS (2)</h2> <p>Some text about the css course.</p> </article> </li> <li> <article> <h2>JavaScript (3)</h2> <p>Some text about the JavaScript course.</p> </article> </li> <li> <article> <h2>Ruby (4)</h2> <p>Some text about the Ruby course.</p> </article> </li> <li> <article> <h2>Python (5)</h2> <p>Some text about the Python course.</p> </article> </li> <li> <article> <h2>Java (6)</h2> <p>Some text about the Java course.</p> </article> </li> </ul> </section> </body> </html> 


Explanation

  1. Retrieve a reference to the parent container element
    let container = document.getElementById('courses')

  2. Generate a list of references to all title elements.
    container.querySelectorAll('li > article > h2:first-child')

    The .querySelectorAll() function makes use of CSS selector syntax.

    This returns a NodeList instance, which is limited in functionality compared to an Array instance. To add the functionality described in the next two steps, you simply apply the transformation. Array.from(......)

  3. Sort the list of elements.
    .sort((h2_a, h2_b) => h2_a.innerText.localeCompare(h2_b.innerText))

    The .sort() function gets a custom comparison function (here shortened into an arrow function ) as argument, which tells the sort function to compare the .innerText of each <h2> pair instead of the HTMLElement objects themself. See the answer of Travis J for more info as to why you shouldn't use .innerHTML .

    The list only contains references to these <h2> elements, which means their content doesn't get copied around while sorting. This wasn't the case either in your original post, it's just a heads up.

  4. Reinsert the <li> elements using .appendChild() .
    .forEach(h2 => container.appendChild(h2.parentElement.parentElement))

    This first removes the <li> ( parentElement ) item from the document and reinserts it at the end of its parent, <ul> , meaning there will never be two of the same items in this list.

    The .forEach() function simply applies this action to every item of the array.


TLDR

Array.from() // #courses li > article > h2 .sort() // h2.innerText -- error was here .forEach() // appendChild() reinserts it in the end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM