SitePoint Sponsor

User Tag List

Results 1 to 22 of 22
  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,527
    Mentioned
    84 Post(s)
    Tagged
    4 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,527
    Mentioned
    84 Post(s)
    Tagged
    4 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,527
    Mentioned
    84 Post(s)
    Tagged
    4 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,527
    Mentioned
    84 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by spoilt View Post
    ok, the tables dont have IDs or classes though. can i get to the table another
    way like #container table?

    thanks
    That's nowhere near as effective. Is this not your page that you are using the scripting with?
    Without a useful identifier on the table, the script will become more brittle and vulnerable to breaking when other future changes occur to the page.
    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)
    It is my website. I have recently set up a company as a dropshipper - so I have access to all of the product data from the company who fulfil the orders.
    I have copied across all of their sizing tables

    Quote Originally Posted by paul_wilkins View Post
    That's nowhere near as effective. Is this not your page that you are using the scripting with?
    Without a useful identifier on the table, the script will become more brittle and vulnerable to breaking when other future changes occur to the page.

  12. #12
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,527
    Mentioned
    84 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by spoilt View Post
    It is my website. I have recently set up a company as a dropshipper - so I have access to all of the product data from the company who fulfil the orders.
    I have copied across all of their sizing tables
    Okay then, it shouldn't be a problem then for you to add a suitable identifier to the table then, such as:

    HTML Code:
    <table id="sizings">
    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)
    I can do but the issue is I've got over 300 size charts so I was hoping to add some code just to the template page, rather than go into each product's description and edit them individually

    Quote Originally Posted by paul_wilkins View Post
    Okay then, it shouldn't be a problem then for you to add a suitable identifier to the table then, such as:

    HTML Code:
    <table id="sizings">

  14. #14
    SitePoint Member
    Join Date
    Dec 2012
    Posts
    13
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    so although its not best practice, is it possible to use the container's id? how would this be written? and can i add / edit size names to your code, e.g. 'superdog' as well as 'super dog'.

    then at least i can fix all the tables immediately, then go into each one individually later to fix them properly.

    thanks!

  15. #15
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,527
    Mentioned
    84 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by spoilt View Post
    so although its not best practice, is it possible to use the container's id? how would this be written?
    If you want to restrict the table element to be from within the div that has a class of "container main-content" then that gets trickier when you're needing the code to remain compatable with older web browsers.

    Code javascript:
    var divs = document.getElementByTagName('div'),
        i,
        table;
    for (i = 0; i < divs.length; i += 1) {
        if (divs[i].className === 'container main-content') {
            table = divs[i].getElementsByTagName('table')[0];
            break;
        }
    }

    That is complex though, and only works if the class names don't change or get added to.

    Instead of that, it is preferable to use the querySelector that most modern web browsers now support:

    Code javascript:
    var table = document.querySelector('.container.main-content table');

    You can still use querySelector in browsers that don't support that feature, such as IE7, but you will also want to use this queryselector polyfill to add that missing querySelector functionality to web browsers that need it.

    Another alternative without an identifier, assuming that it's the first or only table on the web page, is to use getElementsByTagName to get the table itself:

    Code javascript:
    var table = document.getElementsByTagName('table')[0];

    but be wary of using the above, for it's guaranteed to break if any table appears on the web page before the one that you're wanting to target.

    With an identifier it would as simple as using the getElementById method:

    Code javascript:
    var table = document.getElementById('sizings');

    That's why using a consistent identifier allows you to very easily target the sizings table from the script.

    Quote Originally Posted by spoilt View Post
    and can i add / edit size names to your code, e.g. 'superdog' as well as 'super dog'.
    You will see in the test code that I used before, that both of those situations result in the correct behaviour. It can handle superdog with and without spaces, and with a number suffix too.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  16. #16
    SitePoint Member
    Join Date
    Dec 2012
    Posts
    13
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    paul you've been an incredible help!
    i feel like i owe you something!

    the table is the only one on the page, so to ensure the code works in IE7 do you recommend using this

    HTML Code:
    var table = document.getElementsByTagName('table')[0];
    so if i just add all of the code from before, then ... aaagh sorry i feel like an idiot but can you just help me connect the two so i can get it all working. otherwise i know i would stay up all night trying to figure out the last bit (i know its as good as complete in your mind lol)

    thanks again!!

  17. #17
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,527
    Mentioned
    84 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by spoilt View Post
    so if i just add all of the code from before, then ... aaagh sorry i feel like an idiot but can you just help me connect the two so i can get it all working. otherwise i know i would stay up all night trying to figure out the last bit (i know its as good as complete in your mind lol)
    There's a hell of a lot more that's to be done yet in terms of getting the rows, gathering up the sizes, working out what order they should be in, and actually ordering the rows of the table.
    If you don't know how to write code and want someone else to do the work for you instead, then my time that I volunteer here is too limited to allow me to do all of that work for you.

    If on the other hand you can put together an attempt at solving those problems - when you have any technical issues with them we can help to educate you about such things.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  18. #18
    SitePoint Member
    Join Date
    Dec 2012
    Posts
    13
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    as i said im a js newbie, especially as it gets more complicated.
    not asking anyone to do all the work for me... i'll give it the best shot i can

  19. #19
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,527
    Mentioned
    84 Post(s)
    Tagged
    4 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

  20. #20
    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

  21. #21
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,527
    Mentioned
    84 Post(s)
    Tagged
    4 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

  22. #22
    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
  •