简体   繁体   中英

Split HTML table to fit viewport width

I have a HTML table with a lot of columns. I want to split the table to fit the viewport width like so:

The basic table

      | Column 1 | Column 2 | Column 3 | Column 4
------+----------+----------+----------+---------
Row 1 | (1,1)    | (1,2)    | (1,3)    | (1,4)
Row 2 | (2,1)    | (2,2)    | (2,3)    | (2,4)

What I want to have

      | Column 1 | Column 2
------+----------+---------
Row 1 | (1,1)    | (1,2)
Row 2 | (2,1)    | (2,2)

      | Column 3 | Column 4
------+----------+---------
Row 1 | (1,3)    | (1,4)
Row 2 | (2,3)    | (2,4)

The cells containing 'Row X' and 'Column X' are headers, which I want to maintain on the wrapped table.

Is this possible using HTML/CSS/Javascript, such that the table splits responsively?

Edit 1 My HTML:

 <table> <thead> <tr> <th></th> <th>Column 1</th> <th>Column 2</th> <th>Column 3</th> <th>Column 4</th> </tr> </thead> <tbody> <tr> <th>Row 1</th> <td>(1,1)</td> <td>(1,2)</td> <td>(1,3)</td> <td>(1,4)</td> </tr> <tr> <th>Row 2</th> <td>(2,1)</td> <td>(2,2)</td> <td>(2,3)</td> <td>(2,4)</td> </tr> </tbody>

Here's a JS solution to get you started. This one can be optimized in several ways and you can adopt it to your own project requirements. All in all, I enjoyed quick-hacking this little sample.

Here's the code pen: http://codepen.io/anon/pen/RPLvyN . (Resize the screen horizontally to see the effect).

 function FlexTable(el, container) { if(!(this instanceof FlexTable)) { return new FlexTable(el, container); } this.$el = $(el); this.$container = $(container); this.tables = [{ $el: this.$el, columns: [] }]; this.getInfo(); this.extractData(); this.extractLabels(); this.handleResizing(); this.restructure(); } FlexTable.prototype.getInfo = function() { this.columns = []; this.$el.find("thead th").each(function(key, el) { this.columns.push($(el).outerWidth()); }.bind(this)); this.tables[0].columns = this.columns.slice(1); }; FlexTable.prototype.extractData = function() { this.data = []; this.rows = -1; $("tbody td").each(function(index, element) { if(index % (this.columns.length - 1) === 0) { this.data[++this.rows] = []; } this.data[this.rows].push($(element).text().trim()); }.bind(this)); }; FlexTable.prototype.extractLabels = function() { this.columnLabels = []; this.rowLabels = []; this.$el.find("thead > tr > :nth-of-type(n + 2)").each(function(index, element) { this.columnLabels.push($(element).text().trim()); }.bind(this)).end().find("tbody > tr > :first-child") .each(function(index, element) { this.rowLabels.push($(element).text().trim()); }.bind(this)); }; FlexTable.prototype.handleResizing = function() { this.$container.resize(function() { this.restructure(); }.bind(this)); }; FlexTable.prototype.availableSpace = function(flatten) { return flatten ? +Infinity : this.$container.outerWidth(); }; FlexTable.prototype.possibleTables = function() { var space = this.availableSpace(); var total = 0; var column = 0; var numTables = 0; var tables = []; while(true) { total = this.columns[0]; tables[numTables] = { $el: null, columns: [] }; do { total += this.columns[++column]; tables[numTables].columns.push(column - 1); } while((total + this.columns[column + 1]) < space); numTables++; if(column === this.columns.length - 1) { break; } } return tables; }; FlexTable.prototype.sameTablesAsBefore = function(tables) { if(this.tables.length !== tables.length) { return false; } for(var i = 0, len = tables.length; i < len; i++) { if(tables[i].columns.length !== this.tables[i].columns.length) { return false; } } return true; }; FlexTable.prototype.clearExistingTables = function() { this.tables.forEach(function(table) { table.$el.remove(); }); }; FlexTable.prototype.tableTemplate = function() { var $table = $("<table></table>"); var $tbody = $("<tbody></tbody>"); $table.append("<thead><tr><th></th></tr></thead>"); this.data.forEach(function(element, index) { this.cellTemplate(true) .text(this.rowLabels[index]) .appendTo($("<tr></tr>").appendTo($tbody)); }.bind(this)); return $table.append($tbody); }; FlexTable.prototype.cellTemplate = function(th) { return $(th ? "<th></th>" : "<td></td>"); }; FlexTable.prototype.buildTable = function(table, column) { var $table = this.tableTemplate(); for(var i = 0, len = table.columns.length; i < len; i++, column++) { this.cellTemplate(true) .text(this.columnLabels[column]) .appendTo($table.find("thead tr")); for(var j = 0, lenj = this.data.length; j < lenj; j++) { this.cellTemplate() .text(this.data[j][table.columns[i]]) .appendTo($table.find("tbody tr").eq(j)); } } return $table; }; FlexTable.prototype.restructure = function() { var tables = this.possibleTables(); if(this.sameTablesAsBefore(tables)) { return; } this.clearExistingTables(); for(var i = 0, column = 0, len = tables.length; i < len; column += tables[i++].columns.length) { var $table = this.buildTable(tables[i], column); tables[i].$el = $table; $(".tables").append($table); } this.tables = tables; }; $(function() { FlexTable(".tables table:first", window); });
 * { margin: 0; padding: 0; } body { padding: 10px 20px; } table { border-collapse: collapse; } table + table { margin-top: 15px; } table th, table td { white-space: nowrap; border: 1px solid #ccc; padding: 5px 10px; text-align: left; } table tr th:first-of-type { border: 0; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> HTML: <div class="tables"> <table> <thead> <tr> <th></th> <th>Name</th> <th>Position</th> <th>Office</th> <th>Age</th> <th>Start date</th> <th>Salary</th> </tr> </thead> <tbody> <tr> <th></th> <td>Tiger Nixon</td> <td>System Architect</td> <td>Edinburgh</td> <td>61</td> <td>2011/04/25</td> <td>$320,800</td> </tr> <tr> <th></th> <td>Garrett Winters</td> <td>Accountant</td> <td>Tokyo</td> <td>63</td> <td>2011/07/25</td> <td>$170,750</td> </tr> <tr> <th></th> <td>Ashton Cox</td> <td>Junior Technical Author</td> <td>San Francisco</td> <td>66</td> <td>2009/01/12</td> <td>$86,000</td> </tr> <tr> <th></th> <td>Cedric Kelly</td> <td>Senior Javascript Developer</td> <td>Edinburgh</td> <td>22</td> <td>2012/03/29</td> <td>$433,060</td> </tr> <tr> <th></th> <td>Airi Satou</td> <td>Accountant</td> <td>Tokyo</td> <td>33</td> <td>2008/11/28</td> <td>$162,700</td> </tr> <tr> <th></th> <td>Brielle Williamson</td> <td>Integration Specialist</td> <td>New York</td> <td>61</td> <td>2012/12/02</td> <td>$372,000</td> </tr> <tr> <th></th> <td>Herrod Chandler</td> <td>Sales Assistant</td> <td>San Francisco</td> <td>59</td> <td>2012/08/06</td> <td>$137,500</td> </tr> <tr> <th></th> <td>Rhona Davidson</td> <td>Integration Specialist</td> <td>Tokyo</td> <td>55</td> <td>2010/10/14</td> <td>$327,900</td> </tr> <tr> <th></th> <td>Colleen Hurst</td> <td>Javascript Developer</td> <td>San Francisco</td> <td>39</td> <td>2009/09/15</td> <td>$205,500</td> </tr> <tr> <th></th> <td>Sonya Frost</td> <td>Software Engineer</td> <td>Edinburgh</td> <td>23</td> <td>2008/12/13</td> <td>$103,600</td> </tr> <tr> <th></th> <td>Jena Gaines</td> <td>Office Manager</td> <td>London</td> <td>30</td> <td>2008/12/19</td> <td>$90,560</td> </tr> <tr> <th></th> <td>Quinn Flynn</td> <td>Support Lead</td> <td>Edinburgh</td> <td>22</td> <td>2013/03/03</td> <td>$342,000</td> </tr> <tr> <th></th> <td>Charde Marshall</td> <td>Regional Director</td> <td>San Francisco</td> <td>36</td> <td>2008/10/16</td> <td>$470,600</td> </tr> <tr> <th></th> <td>Haley Kennedy</td> <td>Senior Marketing Designer</td> <td>London</td> <td>43</td> <td>2012/12/18</td> <td>$313,500</td> </tr> <tr> <th></th> <td>Tatyana Fitzpatrick</td> <td>Regional Director</td> <td>London</td> <td>19</td> <td>2010/03/17</td> <td>$385,750</td> </tr> <tr> <th></th> <td>Michael Silva</td> <td>Marketing Designer</td> <td>London</td> <td>66</td> <td>2012/11/27</td> <td>$198,500</td> </tr> <tr> <th></th> <td>Paul Byrd</td> <td>Chief Financial Officer (CFO)</td> <td>New York</td> <td>64</td> <td>2010/06/09</td> <td>$725,000</td> </tr> <tr> <th></th> <td>Gloria Little</td> <td>Systems Administrator</td> <td>New York</td> <td>59</td> <td>2009/04/10</td> <td>$237,500</td> </tr> <tr> <th></th> <td>Bradley Greer</td> <td>Software Engineer</td> <td>London</td> <td>41</td> <td>2012/10/13</td> <td>$132,000</td> </tr> <tr> <th></th> <td>Dai Rios</td> <td>Personnel Lead</td> <td>Edinburgh</td> <td>35</td> <td>2012/09/26</td> <td>$217,500</td> </tr> <tr> <th></th> <td>Jenette Caldwell</td> <td>Development Lead</td> <td>New York</td> <td>30</td> <td>2011/09/03</td> <td>$345,000</td> </tr> <tr> <th></th> <td>Yuri Berry</td> <td>Chief Marketing Officer (CMO)</td> <td>New York</td> <td>40</td> <td>2009/06/25</td> <td>$675,000</td> </tr> <tr> <th></th> <td>Caesar Vance</td> <td>Pre-Sales Support</td> <td>New York</td> <td>21</td> <td>2011/12/12</td> <td>$106,450</td> </tr> <tr> <th></th> <td>Doris Wilder</td> <td>Sales Assistant</td> <td>Sidney</td> <td>23</td> <td>2010/09/20</td> <td>$85,600</td> </tr> <tr> <th></th> <td>Angelica Ramos</td> <td>Chief Executive Officer (CEO)</td> <td>London</td> <td>47</td> <td>2009/10/09</td> <td>$1,200,000</td> </tr> <tr> <th></th> <td>Gavin Joyce</td> <td>Developer</td> <td>Edinburgh</td> <td>42</td> <td>2010/12/22</td> <td>$92,575</td> </tr> <tr> <th></th> <td>Jennifer Chang</td> <td>Regional Director</td> <td>Singapore</td> <td>28</td> <td>2010/11/14</td> <td>$357,650</td> </tr> <tr> <th></th> <td>Brenden Wagner</td> <td>Software Engineer</td> <td>San Francisco</td> <td>28</td> <td>2011/06/07</td> <td>$206,850</td> </tr> <tr> <th></th> <td>Fiona Green</td> <td>Chief Operating Officer (COO)</td> <td>San Francisco</td> <td>48</td> <td>2010/03/11</td> <td>$850,000</td> </tr> <tr> <th></th> <td>Shou Itou</td> <td>Regional Marketing</td> <td>Tokyo</td> <td>20</td> <td>2011/08/14</td> <td>$163,000</td> </tr> <tr> <th></th> <td>Michelle House</td> <td>Integration Specialist</td> <td>Sidney</td> <td>37</td> <td>2011/06/02</td> <td>$95,400</td> </tr> <tr> <th></th> <td>Suki Burks</td> <td>Developer</td> <td>London</td> <td>53</td> <td>2009/10/22</td> <td>$114,500</td> </tr> <tr> <th></th> <td>Prescott Bartlett</td> <td>Technical Author</td> <td>London</td> <td>27</td> <td>2011/05/07</td> <td>$145,000</td> </tr> <tr> <th></th> <td>Gavin Cortez</td> <td>Team Leader</td> <td>San Francisco</td> <td>22</td> <td>2008/10/26</td> <td>$235,500</td> </tr> <tr> <th></th> <td>Martena Mccray</td> <td>Post-Sales support</td> <td>Edinburgh</td> <td>46</td> <td>2011/03/09</td> <td>$324,050</td> </tr> <tr> <th></th> <td>Unity Butler</td> <td>Marketing Designer</td> <td>San Francisco</td> <td>47</td> <td>2009/12/09</td> <td>$85,675</td> </tr> <tr> <th></th> <td>Howard Hatfield</td> <td>Office Manager</td> <td>San Francisco</td> <td>51</td> <td>2008/12/16</td> <td>$164,500</td> </tr> <tr> <th></th> <td>Hope Fuentes</td> <td>Secretary</td> <td>San Francisco</td> <td>41</td> <td>2010/02/12</td> <td>$109,850</td> </tr> <tr> <th></th> <td>Vivian Harrell</td> <td>Financial Controller</td> <td>San Francisco</td> <td>62</td> <td>2009/02/14</td> <td>$452,500</td> </tr> <tr> <th></th> <td>Timothy Mooney</td> <td>Office Manager</td> <td>London</td> <td>37</td> <td>2008/12/11</td> <td>$136,200</td> </tr> <tr> <th></th> <td>Jackson Bradshaw</td> <td>Director</td> <td>New York</td> <td>65</td> <td>2008/09/26</td> <td>$645,750</td> </tr> <tr> <th></th> <td>Olivia Liang</td> <td>Support Engineer</td> <td>Singapore</td> <td>64</td> <td>2011/02/03</td> <td>$234,500</td> </tr> <tr> <th></th> <td>Bruno Nash</td> <td>Software Engineer</td> <td>London</td> <td>38</td> <td>2011/05/03</td> <td>$163,500</td> </tr> <tr> <th></th> <td>Sakura Yamamoto</td> <td>Support Engineer</td> <td>Tokyo</td> <td>37</td> <td>2009/08/19</td> <td>$139,575</td> </tr> <tr> <th></th> <td>Thor Walton</td> <td>Developer</td> <td>New York</td> <td>61</td> <td>2013/08/11</td> <td>$98,540</td> </tr> <tr> <th></th> <td>Finn Camacho</td> <td>Support Engineer</td> <td>San Francisco</td> <td>47</td> <td>2009/07/07</td> <td>$87,500</td> </tr> <tr> <th></th> <td>Serge Baldwin</td> <td>Data Coordinator</td> <td>Singapore</td> <td>64</td> <td>2012/04/09</td> <td>$138,575</td> </tr> <tr> <th></th> <td>Zenaida Frank</td> <td>Software Engineer</td> <td>New York</td> <td>63</td> <td>2010/01/04</td> <td>$125,250</td> </tr> <tr> <th></th> <td>Zorita Serrano</td> <td>Software Engineer</td> <td>San Francisco</td> <td>56</td> <td>2012/06/01</td> <td>$115,000</td> </tr> <tr> <th></th> <td>Jennifer Acosta</td> <td>Junior Javascript Developer</td> <td>Edinburgh</td> <td>43</td> <td>2013/02/01</td> <td>$75,650</td> </tr> <tr> <th></th> <td>Cara Stevens</td> <td>Sales Assistant</td> <td>New York</td> <td>46</td> <td>2011/12/06</td> <td>$145,600</td> </tr> <tr> <th></th> <td>Hermione Butler</td> <td>Regional Director</td> <td>London</td> <td>47</td> <td>2011/03/21</td> <td>$356,250</td> </tr> <tr> <th></th> <td>Lael Greer</td> <td>Systems Administrator</td> <td>London</td> <td>21</td> <td>2009/02/27</td> <td>$103,500</td> </tr> <tr> <th></th> <td>Jonas Alexander</td> <td>Developer</td> <td>San Francisco</td> <td>30</td> <td>2010/07/14</td> <td>$86,500</td> </tr> <tr> <th></th> <td>Shad Decker</td> <td>Regional Director</td> <td>Edinburgh</td> <td>51</td> <td>2008/11/13</td> <td>$183,000</td> </tr> <tr> <th></th> <td>Michael Bruce</td> <td>Javascript Developer</td> <td>Singapore</td> <td>29</td> <td>2011/06/27</td> <td>$183,000</td> </tr> <tr> <th></th> <td>Donna Snider</td> <td>Customer Support</td> <td>New York</td> <td>27</td> <td>2011/01/25</td> <td>$112,000</td> </tr> </tbody> </table> </div>

If you are using just one table, then I doubt that your specs be accomplished without using JavaScript to basically restructure the HTML. The latter, in my opinion would not be the best way to go. If you are willing to adjust the markup from the get go, then, yes, you can accomplish the functionality that you are seeking using only CSS.

Here's just one example: http://jsfiddle.net/ktrz74sm/ . (Resize the screen to see the effect).

HTML:

<div id = "wrapper">
    <table>
        <tr>
            <td></td>
            <td>Column 1</td>
            <td>Column 2</td>
        </tr>
        <tr>
            <td>Row 1</td>
            <td>(1, 1)</td>
            <td>(1, 2)</td>
        </tr>
        <tr>
            <td>Row 2</td>
            <td>(2, 1)</td>
            <td>(2, 2)</td>
        </tr>
    </table>
    <table>
        <tr>
            <td></td>
            <td>Column 3</td>
            <td>Column 4</td>
        </tr>
        <tr>
            <td>Row 1</td>
            <td>(1, 3)</td>
            <td>(1, 4)</td>
        </tr>
        <tr>
            <td>Row 2</td>
            <td>(2, 3)</td>
            <td>(2, 4)</td>
        </tr>
    </table>
</div>

CSS:

table td {
    text-align: center;
}

@media screen and (min-width: 400px) {
    table {
        display: inline-table;    
    }

    table:nth-of-type(2) td:first-of-type {
        display: none;    
    }   
}

And, here's a solution that uses counters to eliminate naming rows and columns: http://jsfiddle.net/nm5bybf2/ .

HTML:

<div id = "wrapper">
    <table>
        <tr>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td>(1, 1)</td>
            <td>(1, 2)</td>
        </tr>
        <tr>
            <td></td>
            <td>(2, 1)</td>
            <td>(2, 2)</td>
        </tr>
    </table>
    <table>
        <tr>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td>(1, 3)</td>
            <td>(1, 4)</td>
        </tr>
        <tr>
            <td></td>
            <td>(2, 3)</td>
            <td>(2, 4)</td>
        </tr>
    </table>
</div>

CSS:

body {
    counter-reset: column-counter;
}   

table td {
    text-align: center;
}

table {
    counter-reset: row-counter;
}

table tr:nth-of-type(n + 2) td:first-of-type:before {
    counter-increment: row-counter;
    content: "Row " counter(row-counter);
}

table tr:first-of-type td:nth-of-type(n + 2):before {
    counter-increment: column-counter;
    content: "Column " counter(column-counter);
}

@media screen and (min-width: 400px) {
    table {
        display: inline-table;
    }

    table:nth-of-type(2) td:first-of-type {
        display: none;    
    }
}

Basically you're trying to make a responsive table, without getting the columns that don't fit in the view space cut off by the edge of the the screen, correct?

If so, why not just make two side-by-side tables? Let me explain.

I actually had to find a solution to this same problem just this past week for a client of the company I work for who wanted a 9 by 9 table on his site, which is no big deal, except it needed to be responsive.

So, here's what I came up with.

I created two sideby side tables. The first containing only one column, with 9 rows. Then immediately to the right of it I created a second table, this one with 8 columns and 9 rows. I set both tables to a fixed hight so they lined up properly. Next, use bootstrap's responsive table class and apply it to the large table that you want to scroll. Use CSS media querys to change the widths of the tables using max-width (and set the media queries accordingly for your table at various page widths). This allows the two tables to look like one table, but makes the left column(aka table) fixed. This eliminates the need to repeat the first column over and over when displaying the rest of the columns.

Check out the table on the bottom of this page on a mobile device or narrow screen to see what I'm talking about and see if this setup would work for your use. Pure CSS powered mobile friendly table with lots of columns.

http://www.cleanplanetdetailing.com/test-gtechniq-pricing.html

If that method does work for what you're trying to do, and you want more details on how to do it, post a comment and I'll be happy to make a write-up on it if you need one.

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