DataTables presenting with a card view

DataTables presenting with a card view

jmwitherjmwither Posts: 3Questions: 0Answers: 0

I have seen lots of discussion on trying to get datatables to present in a "card" or "panel" type view. I see there are lots of workarounds utilizing the various callbacks and events. I believe i have found the simplest method yet.

My method relies solely on CSS. Heres a few screen shots and a link to a sample page.
azguys.com/datatables/index.html

I have tested it out on Chrome and IE, they both seem agreeable to the CSS, no funny quirks. Give it a try and let me know what you think, and if it may have problems of other browsers.

«1

Replies

  • allanallan Posts: 52,179Questions: 1Answers: 7,942 Site admin

    Genius! Absolutely brilliant in fact. Thanks for sharing this with us - I'm really impressed and that works very well.

    Two areas that could possibly be improved:

    1. I wonder if the column titles could be injected using CSS pseudo elements
    2. Flexbox might make the layout more flexible.

    This is absolutely something I will explore in more detail.

    Thank you :smile:
    Allan

  • jmwitherjmwither Posts: 3Questions: 0Answers: 0

    Thanks. Pseudo classes work only for adding text to the DOM. So if you wanted a label, so that you can adjust its appearance, you have to use the render or some additional js for that. Also I personally try to keep design intent within one location. That's why i like the column definitions in a separate object, that way I can swap column configurations for different cases

  • allanallan Posts: 52,179Questions: 1Answers: 7,942 Site admin

    Good point - if you wanted an image in the header there would be no way to do that with a ::before/::after. Possibly one for a plug-in that can dynamically inject the header text in that case.

    Regardless, I really like this - thanks again.

    Allan

  • MarcusSMarcusS Posts: 1Questions: 0Answers: 0

    Wow!!! :*
    Allan is right- you are really a genius
    You saved us days of work!!!!
    Thank you very much
    :)
    Marcus

  • lenamtllenamtl Posts: 169Questions: 45Answers: 1

    Excellent!

  • dnasistemasdnasistemas Posts: 1Questions: 0Answers: 0

    hello jmwhiter, how are you?

    I would like to ask you for a copy of the code that converts the datatable plugin to a card view.

  • venkatrchandravenkatrchandra Posts: 1Questions: 0Answers: 0

    Hi...i was also looking for code to present datatable as a card. please help!

  • Loren MaxwellLoren Maxwell Posts: 272Questions: 60Answers: 9

    Looks like the page is unfortunately gone, but here's the latest version the Way Back Machine has:
    http://web.archive.org/web/20170605170544/http://azguys.com/datatables/index.html

    You'll need to pick through the page source code to separate out the Way Back Machine stuff.

    However, I think this would make a great plugin.

  • Loren MaxwellLoren Maxwell Posts: 272Questions: 60Answers: 9
    edited December 2017

    Ok, I've used the code from the Way Back Machine make some other adjustments in an attempt to improve this exceptional effort by @jmwither:

    My results can be seen here: http://ghsfha.org/datatables_cards.html

    I have:
    - Updated to Datatables 1.10.16 and Select 1.2.4
    - Updated JQuery to 3.2.1
    - Updated to Bootstrap 4 beta 3
    - Included Font Awesome 4.7.0
    - Streamlined some of the css and add a js 'deselect' to appropriately remove the row data from the alert on the bottom

    Of note to anyone interested, the only css that is absolutely necessary to get this effect is:

        .cards tbody tr {
            float: left;
        }
        .cards tbody td {
            display: block;
        }
    

    Everything else is simply for aesthetics.

    @allan, instead of the render functions being this way:
    render: function (data, type, full, meta) { return '<label>Extn:</label>' + data; }

    is there a way to automatically grab the column header using the meta.col parameter?

    I tried:
    render: function (data, type, full, meta) { return '<label>'+ table.column(meta.col).title() +':</label>' + data; }

    But I get TypeError: table is undefined. I suppose the error is because table is in the process of still being defined.

    Any thoughts?

    Also, the "cards" are not quite responsive, so when the screen is resized they don't dynamically adjust. They will refresh correctly, but not while the screen size is being changed.

    Any ideas on that?

    Finally -- any way to capture the button toggle as part of saveState?

  • Loren MaxwellLoren Maxwell Posts: 272Questions: 60Answers: 9
    edited December 2017

    Not quite compatible with the Scroller extension:
    http://ghsfha.org/datatables_cards_scroller.html

    -- Ignore the formatting of the odd rows. That part is a known issue: https://datatables.net/forums/discussion/44685

    The real issue is that when swapping to Cards view, 1) the table that holds the Scroller header is still present and 2) when a filter or a sort is being executed, the left-to-right format of the cards "breaks" into a single column.

    I suspect these issues are related to how @allan has programmed the Scroller, but maybe some minor adjustments to Scroller or a full-fledged cards extension could make it work.

    Anyway, here's another option (plus an updated look) that shows all the rows (or cards) at once:
    http://ghsfha.org/datatables_cards_all.html

    It's basically achieved with a dom of 'fit' and a pageLen of -1.

    You could also update the verbiage (or icons) from "Display as table" to "Display as cards" or whatever.

    This would also be a great place to use the Buttons extension to better integrate the toggle with the DataTable code.

    Another item that could be integrated is the Editor, with inline editing being particularly nice. As a matter of fact, you could largely sidestep the standalone editor as a whole depending on how the data might be displayed . . .

  • kthorngrenkthorngren Posts: 9,705Questions: 25Answers: 2,234

    Thanks for the link and updated code. Looks good.

    is there a way to automatically grab the column header using the meta.col parameter?

    column().title() is not a Datatable method. Maybe something like this will work for what you want?

    render: function (data, type, full, meta) { 
          var title = $('#example').DataTable().column(meta.col).header();
          return '<label>'+ $(title).html() +':</label>' + data; 
    }
    

    Instead of using table you can just get an instance of the API.

    Kevin

  • Loren MaxwellLoren Maxwell Posts: 272Questions: 60Answers: 9
    edited December 2017

    @kthorngren

    Thanks Kevin, it doesn't quite work.

    I don't get an error, so something is there, but nothing is returned now.

    Using this same approach I've also tried text() (got nothing again) and innerHTML (got an error).

    Maybe because the table isn't drawn yet?

  • kthorngrenkthorngren Posts: 9,705Questions: 25Answers: 2,234
    edited December 2017

    Maybe because the table isn't drawn yet?

    Seems so. This example works with the table being built before the JS executes:
    http://live.datatables.net/fobicoge/1/edit

    Of course I didn't replicate the card view code, etc. Just the render function.

    Kevin

  • Loren MaxwellLoren Maxwell Posts: 272Questions: 60Answers: 9

    Ok, upon troubleshooting further, maybe it's because I didn't have any <th>s in my table :blush:

    Since I've added them it works perfect.

    However, now I'm curious as to how DataTables knew to use the Title fields . . .

  • Loren MaxwellLoren Maxwell Posts: 272Questions: 60Answers: 9
    edited December 2017

    Another quick note -- the search picks up the labels since they are technically in the <td> along with the data.

    For example, typing "x" in the search box doesn't remove any entries since "Extn" is one of the labels.

    This is true even when the data is viewed as a traditional table and the labels are hidden.

  • kthorngrenkthorngren Posts: 9,705Questions: 25Answers: 2,234
    edited January 2018

    You can render the label for display only using orthogonal data. Try something like this in your render functions:

    return type === 'display' ? '<label>'+ $(title).html() +':</label>' + data : data;

    The display data will have the label. Sorting and searching will use the original data without the label.

    Kevin

  • Loren MaxwellLoren Maxwell Posts: 272Questions: 60Answers: 9
    edited January 2018

    Thanks, Kevin. That did the job!

    Also, I've added an actual button to the table using the DataTables Button extension along with a little animate.css to make it stand out:
    http://ghsfha.org/datatables_cards_button.html

    So to put the cards into a table you pretty much just add the CSS along with the button.

  • Loren MaxwellLoren Maxwell Posts: 272Questions: 60Answers: 9
    edited January 2018

    BTW, do you know a simple way to change the text property on the button every time it's pressed?

    I'd like use a card icon when the data is viewed as a table and a table icon when the data is viewed as cards, but it seems like I'd have to check each time as to what is currently displayed.

  • Loren MaxwellLoren Maxwell Posts: 272Questions: 60Answers: 9

    There are actually several drawbacks to this approach, one of which is that when the cards are not evenly sized the format is thrown off.

    However I've made an update so that the height of all the cards (actually <tr>s) is changed to the height of the maximum card (<tr>) so that the spacing works out:
    http://ghsfha.org/datatables_cards_button.html

    This is still not quite as good as something like Bootstrap's card-deck class where it will size all the cards by the row, but it's a step in the right direction.

    The power of this approach continues to be it's ease of implementation and the retention of all the DataTables functionality like filtering and ordering.

    I wouldn't necessarily recommend it for all use cases, but there's definitely potential to cover many with fairly minimal effort.

  • Loren MaxwellLoren Maxwell Posts: 272Questions: 60Answers: 9
    edited January 2018

    Revisited this again, to get rid of the <label> inside of each cell and add a data-label attribute that is shown through CSS when in card view. This is actually mentioned in @allan's initial feedback to the OP.

    I think it worked out really well: http://ghsfha.org/datatables_cards_labels.html

    I used this article: https://florianziegler.com/css-tutorial-style-table-rows-to-look-like-cards/ plus there are a couple of other similar examples out there as well.

    The key changes were to the button that toggles the view between the traditional table and cards.

    Basically when the table goes into card view, the headers are used to populate the data-label attribute for each cell:

    // Create an array of labels containing all table headers
    var labels = [];
    $('#register').find('thead th').each(function() {
        labels.push($(this).text());
    });
    
    // Add data-label attribute to each cell
    $('#register').find('tbody tr').each(function() {
        $(this).find('td').each(function(column) {
            $(this).attr('data-label', labels[column]);
        });
    });
    

    Then some CSS places it properly:

    .cards td:before {
        content: attr(data-label);
        display: inline;
        position: relative;
        font-size: 85%;
        top: -0.5rem;
        float: left;
        color: #808080;
        min-width: 4rem;
        margin-left: 0;
        margin-right: 1rem;
        text-align: left;
    }
    tr.selected td:before {
        color: #404040;
    }
    

    Finally, although I suppose this might not be necessary, when the table goes back into the traditional view I remove the data-label attribute:

    // Remove data-label attribute from each cell
    $('#register').find('td').each(function() {
        $(this).removeAttr('data-label');
    });
    

    A possible future improvement would be to allow a different label for the card than what the table has for a header. For example, originally the header for the first column in the table was "Photo", but I removed it altogether because I don't want that on the cards.

  • allanallan Posts: 52,179Questions: 1Answers: 7,942 Site admin

    That is really impressive! Thanks for your updates on this - I'll be linking to this thread in future I'm certain!

    Allan

  • MaicoMaico Posts: 1Questions: 0Answers: 0

    Congratulation guys this is great job !
    Many thanks,
    Mike

  • lenamtllenamtl Posts: 169Questions: 45Answers: 1

    Hi,

    I'm wondering if this is possible to print PDF the result in card view
    or do we need to change Make PDF code or CSS to make it display the same in PDF?

  • allanallan Posts: 52,179Questions: 1Answers: 7,942 Site admin

    You'd need to change the document structure that the Buttons library uses to create the table. To be honest, I would just create a custom button that would build the pdfmake document you want rather than attempting to modify the built in one.

    Allan

  • ZandarkoadZandarkoad Posts: 1Questions: 0Answers: 0

    This is really beautiful indeed. I hope to use a modified version of this. I don't actually want the cards shown in a row like that. I'd like them to me responsively displayed vertically inside the content above the table.

  • gyrocodegyrocode Posts: 120Questions: 6Answers: 30

    I posted an article about this interesting approach at jQuery DataTables: Card view. I cleaned up the source code a little bit to make it more generalized. I am planning to improve the script based on feedback.

  • hm123hm123 Posts: 67Questions: 17Answers: 1

    This update to the card view is excellent

    However, when card view is enabled, it seems to break the clipboard export when using the following code to show only visible cells:

    exportOptions: { columns: [':visible'], rows: [':visible']

    Note that the clipboard copy works when out of card view again. Is there a way to make this work on card view as well?

    sample:
    https://jsfiddle.net/gL1o9f0h/

  • kthorngrenkthorngren Posts: 9,705Questions: 25Answers: 2,234

    Maybe this is what you are looking for:
    https://jsfiddle.net/h7usbfn0/

    It uses exportOptions: { rows: [':visible'] } to export the rows on the page.

    Kevin

  • hm123hm123 Posts: 67Questions: 17Answers: 1

    Almost,

    While exportOptions: { rows: [':visible']} does indeed export all visible rows on the page to clipboard, it also exports hidden columns.

    The previous code exportOptions { columns: [':visible'], rows: [':visible']} exports only what is visible when outside of card view as expected, but does not copy anything at all with card view enabled.

    Any solution to this problem?

    sample:
    https://jsfiddle.net/ea3fbLk8/1/

  • gyrocodegyrocode Posts: 120Questions: 6Answers: 30
    edited February 24

    @hm123 Using the following CSS rule for table head instead of display: none seems to solve the issue. Just need to figure out how to prevent hidden thead from occupying the space.

    .cards thead {
       visibility:hidden;
    }
    

    See updated example for code and demonstration.

Sign In or Register to comment.