SitePoint Sponsor

User Tag List

Results 1 to 22 of 22

Hybrid View

  1. #1
    SitePoint Member
    Join Date
    Dec 2012
    Posts
    13
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Question Custom reorder table rows with jquery - not alphabetically

    Hi there!

    I've got a lot of tables displaying sizes, where the rows need sorting based on the size, e.g. X small row first, then small, medium, etc.

    Not all tables have all the same sizes, and some sizes are formatted slightly differently, e.g 'X small' or 'XSmall' or 'XS'.

    The table has no classes or IDs.

    The size text is the first word in the first td, first tr after thead.

    Here is the link: http://site-1111.myshopify.com/produ...te-dog-t-shirt

    The other thing that's not too important, but I'd like to know if its possible to remove any text that starts with a certain string. Looking at the same link above, I would like to remove all the breed guidelines in the tables, so text that starts with 'breed guidelines'.

    I'm very much a js and jquery rookie so any help would be much appreciated!!!

    Thank you,

    James

  2. #2
    SitePoint Member
    Join Date
    Dec 2012
    Posts
    13
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hello everyone,

    I know it's quite an unusual request, but I'd love to know if this is even possible and if someone could steer me in the right direction so I can get started on implementing this.

    I really hope someone can help ))

    Thanks very much in advance!

    James

  3. #3
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,526
    Mentioned
    83 Post(s)
    Tagged
    3 Thread(s)
    Quote Originally Posted by spoilt View Post
    Hello everyone,

    I know it's quite an unusual request, but I'd love to know if this is even possible and if someone could steer me in the right direction so I can get started on implementing this.

    I really hope someone can help ))
    The reason why you may not be getting much help with this is that changes to the content are normally better achieved from the backend that's serving the data instead.

    To resolve your issue though, it seems that the most important piece of the puzzle is to come up with a function that accepts the sizing string, and returns an appropriate index number for how large it is.

    So XS or XSmall or X small or x-small or x-s, or such variations would result in 0, large would be 4, and superdog 2 would be 8

    I'll take a look at what can be done with that in a few hours.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  4. #4
    SitePoint Member
    Join Date
    Dec 2012
    Posts
    13
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by paul_wilkins View Post
    The reason why you may not be getting much help with this is that changes to the content are normally better achieved from the backend that's serving the data instead.

    To resolve your issue though, it seems that the most important piece of the puzzle is to come up with a function that accepts the sizing string, and returns an appropriate index number for how large it is.

    So XS or XSmall or X small or x-small or x-s, or such variations would result in 0, large would be 4, and superdog 2 would be 8

    I'll take a look at what can be done with that in a few hours.
    Paul that sounds great!

    Thanks for replying, let me know how you get on

    James

  5. #5
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,526
    Mentioned
    83 Post(s)
    Tagged
    3 Thread(s)
    Today ended up being busier than expected, but I'm back to things now.

    Here's a basic start, where we attempt to use a regex to capture common parts from the size string.

    Code javascript:
    function getSizeParts(size) {
        var sizeRx = /(.*)(small|medium|large|super|s|m|l)(.*)/,
            match = sizeRx.exec(size.toLowerCase()),
            parts = {prefix: '', size: '', suffix: ''};
     
        if (match) {
            parts.prefix = match[1];
            parts.size = match[2];
            parts.suffix = match[3];
        }
     
        return parts;
    }
     
    function valueOfSize(size) {
        var parts = getSizeParts(size),
            size = 0;
     
        // magic happens here
        // ...
        size = parts.size;
     
        return size; // need to instead return numeric value of size
    }
     
    var sizesToTest = ['XS', 'XSmall', 'X small', 'x-small', 'x-s', 'medium', 'm', 'large', 'l', 'xl', 'xxl', 'super', 'superdog', 'super dog 1', 'superdog2'],
        i,
        size, value;
    for (i = 0; i < sizesToTest.length; i += 1) {
        size = sizesToTest[i];
        value = valueOfSize(size);
        console.log('size: ', size, ', value: ', value);
    }

    Which mostly works, except for situations such as this:

    Code:
    size: X small, value: l
    The reason why it's showing the wrong size is due to regular expressions being greedy by default. The first (.*) capture group grabs the whole string, but the next capture group then has nothing, so the regex removes one character from the first capture group and looks for a match from the second capture group, which it finds with the 'l' character from the end of the word "small".

    That's not how we want the regex to work. Instead of the regex being greedy, we want the first capture group to be lazy instead. We can do that by adding a question mark after the asterisk, so that we instead end up with (.*?)

    Code javascript:
    var sizeRx = /(.*?)(small|medium|large|super|s|m|l)(.*)/,

    Now we end up getting the right size for each test.
    From here it's just a matter of turning the size in to a numeric number for ordering, and of dealing with special situations.

    We can use a simple switch statement to provide different size values:

    Code javascript:
    function valueOfSize(size) {
        var parts = sizeParts(size),
            size = -1;
     
        switch (parts.size) {
        case 'small':
            // fall through
        case 's':
            size = 1;
            break;
        case 'medium':
            // fall through
        case 'm':
            size = 2;
            break;
        case 'large':
            // fall through
        case 'l':
            size = 3;
            break;
        case 'super':
            size = 6;
            break;
        default:
            // leave size at default value
        }
     
        return size;
    }

    Now we just need the prefix to affect the small and large ones, and the suffix to affect the super ones.

    Which is done with:

    Code javascript:
    var parts = sizeParts(size),
        prefixMatch = parts.prefix.match(/x/g),
        numOfXs = prefixMatch && prefixMatch.length || 0,
        suffixNumber = parseInt(parts.suffix.match(/\d/), 10) || 0,
        size = -1;
    ...
    case 's':
        size = 1 - Math.min(numOfXs, 1); // no more than one X for small
    ...
    case 'l':
        size = 3 + Math.min(numOfXs, 2); // no more than two X's for large
    ...
    case 'super':
        size = 6 + suffixNumber;

    Which results in the following final code for working out the size of different sizing strings:

    Code javascript:
    function sizeParts(size) {
        var sizeRx = /(.*?)(small|medium|large|super|s|m|l)(.*)/,
            match = sizeRx.exec(size.toLowerCase()),
            parts = {
                prefix: '',
                size: '',
                suffix: ''
            };
     
        if (match) {
            parts.prefix = match[1];
            parts.size = match[2];
            parts.suffix = match[3];
        }
     
        return parts;
    }
     
    function valueOfSize(size) {
        var parts = sizeParts(size),
            prefixMatch = parts.prefix.match(/x/g),
            numOfXs = prefixMatch && prefixMatch.length || 0,
            suffixNumber = parseInt(parts.suffix.match(/\d/), 10) || 0,
            size = -1;
     
        switch (parts.size) {
        case 'small':
            // fall through
        case 's':
            size = 1 - Math.min(numOfXs, 1); // no more than one X for small
            break;
        case 'medium':
            // fall through
        case 'm':
            size = 2;
            break;
        case 'large':
            // fall through
        case 'l':
            size = 3 + Math.min(numOfXs, 2); // no more than two X's for large
            break;
        case 'super':
            size = 6 + suffixNumber;
            break;
        default:
            // leave size at default value
        }
     
        return size;
    }
     
    var sizesToTest = ['XS', 'XSmall', 'unknown', 'X small', 'x-small', 'x-s', 'small', 'medium', 'm', 'large', 'l', 'xl', 'xxl', 'super', 'superdog', 'super dog 1', 'superdog2'],
        i, size, value;
    for (i = 0; i < sizesToTest.length; i += 1) {
        size = sizesToTest[i];
        value = valueOfSize(size);
        console.log('size: ', size, ', value: ', value);
    }

    The test code results in the following output:

    Code:
    size:  XS , value:  0
    size:  XSmall , value:  0
    size:  unknown , value:  -1
    size:  X small , value:  0
    size:  x-small , value:  0
    size:  x-s , value:  0
    size:  small , value:  1
    size:  medium , value:  2
    size:  m , value:  2
    size:  large , value:  3
    size:  l , value:  3
    size:  xl , value:  4
    size:  xxl , value:  5
    size:  super , value:  6
    size:  superdog , value:  6
    size:  super dog 1 , value:  7
    size:  superdog2 , value:  8
    Last edited by paul_wilkins; Dec 4, 2012 at 13:55.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  6. #6
    SitePoint Member
    Join Date
    Dec 2012
    Posts
    13
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    wow!! thanks so much paul, that's amazing

    I'm trying my best to understand it, thanks for explaining as you go through so I can actually try to grasp an understanding too.

    Am I right in saying that this code will get the sizes to equal numbers, but it won't actually reorder the table yet? Is there another step still to do..? Sorry for my (very) minimal understanding..
    Last edited by paul_wilkins; Dec 4, 2012 at 13:58. Reason: entire needlessly quoted post removed

  7. #7
    SitePoint Member
    Join Date
    Dec 2012
    Posts
    13
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    sorry - you did explain what the code would output.
    so would i then hide the original table.. and be able to add styling to the output html, or insert it into a new table..?
    thanks again

  8. #8
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,526
    Mentioned
    83 Post(s)
    Tagged
    3 Thread(s)
    Quote Originally Posted by spoilt View Post
    sorry - you did explain what the code would output.
    so would i then hide the original table.. and be able to add styling to the output html, or insert it into a new table..?
    thanks again
    The original table can remain, for a simple way to reorder the table is to loop through those difference sizes in numerical order, from biggest to smallest, and move each row to the top of the table. That way as you work your way from the largest ones down to the smallest ones, the table ends up having the smallest ones placed on top of the larger ones below it.

    So to achieve that, you would need to first gain a list of the different table rows, which can be most easily done by placing an id attribute on the table itself and using document.getElementsByTagName to get the rows.

    After you have the rows, you need to get the first td value from each row, which can be done in a similar way.

    So first, get the rows, then the sizes from those rows, then sort those rows based on the numeric value of the size from that row.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  9. #9
    SitePoint Member
    Join Date
    Dec 2012
    Posts
    13
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    ok, the tables dont have IDs or classes though. can i get to the table another
    way like #container table?

    thanks

    Quote Originally Posted by paul_wilkins View Post
    The original table can remain, for a simple way to reorder the table is to loop through those difference sizes in numerical order, from biggest to smallest, and move each row to the top of the table. That way as you work your way from the largest ones down to the smallest ones, the table ends up having the smallest ones placed on top of the larger ones below it.

    So to achieve that, you would need to first gain a list of the different table rows, which can be most easily done by placing an id attribute on the table itself and using document.getElementsByTagName to get the rows.

    After you have the rows, you need to get the first td value from each row, which can be done in a similar way.

    So first, get the rows, then the sizes from those rows, then sort those rows based on the numeric value of the size from that row.

  10. #10
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,526
    Mentioned
    83 Post(s)
    Tagged
    3 Thread(s)
    Actually, I should be able to take the time to go through this, due to the rest of it being made fairly simple by combining some built-in techniques.

    Starting with the table content, we want to get the tbody rows and sort them.

    Code javascript:
    var tbody = document.querySelector('.main-content tbody'),
        rows = tbody.getElementsByTagName('tr');
     
    sortRows(rows, compareSizes);

    In fact, that can be made even simpler, by using querySelectorAll to get the rows themself.

    Code javascript:
    var rows = document.querySelectorAll('.main-content tbody tr');
     
    sortRows(rows, compareSizes);

    There are a number of different ways to sort things but in this case, given that they are an HTML collection, the easiest way to sort them is to create an array from those HTML elements by using the Array's slice method to convert non-array items in to an array. That way the array sort method can be used to sort the rows in that array.

    Then, we can then loop through the sorted array and move each row to the bottom. The sortRows function that results in the rows all being nicely sorted, is:

    Code javascript:
    function sortRows(rows, compare) {
        var rowsArr = Array.prototype.slice.call(rows, 0);
     
        rowsArr.sort(compare);
        rowsArr.forEach(function (row) {
            row.parentNode.appendChild(row);
        });
    }

    If you also need to support web browsers that don't know about the forEach array method, you can add support for that with the following code:

    Code javascript:
    // Array.forEach polyfill, from [url]https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach[/url]
    if ( !Array.prototype.forEach ) {
      Array.prototype.forEach = function(fn, scope) {
        for(var i = 0, len = this.length; i < len; ++i) {
          fn.call(scope, this[i], i, this);
        }
      }
    }

    The sort method that arrays have is what we'll use to sort the rows, so that compareSizes function that we give to the sort method accepts two items being compared, and returns either -1/0/+1 or false/true to indicate which one is larger than the other.

    Code javascript:
    function compareSizes(a, b) {
        var aSize = a.querySelector('td').innerHTML,
            bSize = b.querySelector('td').innerHTML;
     
        return valueOfSize(aSize) > valueOfSize(bSize);
    }

    And that's all that you should need.

    Keeping things nice and easy, we can create a globally accessible function called tableSizes, which accepts a selector for where to get the rows to be sorted.
    It could be called as:

    Code javascript:
    window.tableSizes.sort('.main-content');

    Or if you're feeling lazy, as just:

    Code javascript:
    tableSizes.sort('.main-content');

    A nice way to create that is with:

    Code javascript:
    window.tableSizes = (function () {
        'use strict';
     
        // functions and code in here
        // ...
     
        return {
            sort: function (sourceSelector) {
                var rows = document.querySelector(sourceSelector + ' tbody tr');
     
                sortRows(rows, compareSizes);
            }
        };
    }());

    Some example code for the above can be seen at http://jsfiddle.net/pmw57/yZrcr/

    To help demonstrate things in action, I've added some code to randomize things too, to http://jsfiddle.net/pmw57/yZrcr/2/
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  11. #11
    SitePoint Member
    Join Date
    Dec 2012
    Posts
    13
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    hi paul - thanks so much for doing this, it all looks great once its come together

    ive just copied and pasted the jsfiddle code onto a page that i can test it on, then make it match the IDs and classes on my page, and tweak anything, etc, but surprisingly even a straight copy / paste doesn't work.

    ive checked there are no ID or class conflicts (just had to change the class 'main-content'), but not sure why its running on jsfiddle but not on my page. perhaps a jquery conflict i dont know.. really sorry i cant figure it out - i have been trying and ill keep looking. here is the link http://site-1111.myshopify.com/pages/new

  12. #12
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,526
    Mentioned
    83 Post(s)
    Tagged
    3 Thread(s)
    Quote Originally Posted by spoilt View Post
    hi paul - thanks so much for doing this, it all looks great once its come together

    ive just copied and pasted the jsfiddle code onto a page that i can test it on, then make it match the IDs and classes on my page, and tweak anything, etc, but surprisingly even a straight copy / paste doesn't work.

    ive checked there are no ID or class conflicts (just had to change the class 'main-content'), but not sure why its running on jsfiddle but not on my page. perhaps a jquery conflict i dont know.. really sorry i cant figure it out - i have been trying and ill keep looking. here is the link http://site-1111.myshopify.com/pages/new
    There seems to be a hidden unicode character at the end of the script that you pasted. That can happen when you copy everything from jsfiddle

    Code javascript:
    document.getElementById('randomize').click();
    &#8203;</script>

    Delete that hidden unicode character.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  13. #13
    SitePoint Member
    Join Date
    Dec 2012
    Posts
    13
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Talking

    amazing! works like a charm

    thanks again paul!! really appreciate your help


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •