├── LICENSE ├── README.md ├── bower.json ├── dist ├── css │ ├── reflow-table.css │ └── reflow-table.min.css └── js │ ├── reflow-table.js │ └── reflow-table.min.js ├── docs ├── dist │ ├── css │ │ └── reflow-table.css │ └── js │ │ └── reflow-table.js ├── index.html └── narrow-jumbotron.css ├── img ├── demo-mobile.png └── demo-origin.png ├── package.json └── src ├── css └── reflow-table.css └── js └── reflow-table.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Nick Tsai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

RWD Reflow Table

6 |
7 |

8 | 9 | [![npm version](https://img.shields.io/npm/v/jquery-reflow-table.svg)](https://www.npmjs.com/package/jquery-reflow-table) 10 | [![License](https://img.shields.io/github/license/yidas/jquery-reflow-table.svg)](https://github.com/yidas/jquery-reflow-table/blob/master/LICENSE) 11 | 12 | RWD reflow table switch for mobile UI/UX by collapsing columns 13 | 14 | FEATURES 15 | -------- 16 | 17 | - ***High performance** and **data consistency** with Mobile UI/UX* 18 | 19 | - ***Pseudo** element implementation* 20 | 21 | - ***[DataTables](https://datatables.net/)** library Integration* 22 | 23 | 24 | --- 25 | 26 | OUTLINE 27 | ------- 28 | 29 | - [Demonstration](#demonstration) 30 | - [Requirements](#requirements) 31 | - [Installation](#installation) 32 | - [Bower Installation](#bower-installation) 33 | - [Assets include](#assets-include) 34 | - [Initailize via JavaScript](#initailize-via-javascript) 35 | - [Options](#options) 36 | - [Implementation](#implementation) 37 | - [Update for Dynamic Content](#update-for-dynamic-content) 38 | - [DataTables Integration](#dataTables-integration) 39 | - [Customize Styles](#customize-styles) 40 | - [Events](#events) 41 | - [API Usage](#api-usage) 42 | 43 | --- 44 | 45 | DEMONSTRATION 46 | ------------- 47 | 48 | [https://yidas.github.io/jquery-reflow-table/](https://yidas.github.io/jquery-reflow-table/) 49 | 50 | 51 | 52 | 53 | After applying Reflow-Table, the table will be responsive with reflow mode, which can be switch by browser's width or customized events. 54 | 55 | --- 56 | 57 | REQUIREMENTS 58 | ------------ 59 | This library requires the following: 60 | 61 | - jQuery 1.11.0+ | 2.0+ | 3.0+ 62 | 63 | --- 64 | 65 | INSTALLATION 66 | ------------ 67 | 68 | ### Bower Installation 69 | 70 | ``` 71 | bower install jquery-reflow-table 72 | ``` 73 | 74 | > You could also download by [NPM](https://www.npmjs.com/package/jquery-reflow-table) or directly copy [`dist`](https://github.com/yidas/jquery-reflow-table/tree/master/dist) assets. ([Last Release for download](https://github.com/yidas/jquery-reflow-table/releases)) 75 | 76 | ### Assets include 77 | 78 | Add CSS file into the ``: 79 | 80 | ```html 81 | 82 | ``` 83 | 84 | Add JavaScript file either into the ``, or the bottom of ``: 85 | 86 | ```html 87 | 88 | ``` 89 | 90 | 91 | ### Markup 92 | 93 | Add the classes `.table` to the tables as usual when using Bootstrap, then wrap them with a identity such as `.freeze-table` 94 | 95 | ```html 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 |
...
...
104 | ``` 105 | 106 | ### Initailize via JavaScript 107 | 108 | You can initialize Table Reflow by jQuery extension call: 109 | 110 | ```html 111 | 116 | ``` 117 | 118 | Or initialize an element by newing object from Table Reflow class: 119 | 120 | ```html 121 | 126 | ``` 127 | 128 | > The parameter `{}` is [options](#options) configuration 129 | 130 | 131 | 132 | ### Options 133 | 134 | Options could be passed via JavaScript with object. 135 | 136 | |Name |Type |Default |Description| 137 | |:-- |:-- |:-- |:-- | 138 | |namespace |string |'reflow-table' |Table namespace for unbind| 139 | |autoWidth |integer |736 |Detected width like `@media`, `null` for disabling auto detection| 140 | |widthRatio |string |'50' |Set pseudo heads' width ratio in mobile mode. (`15`, `20`, `25`, `30`,`40`,`50`)| 141 | |widthSize |string |null |Set pseudo heads' width size in mobile mode. (`xs`, `sm`, `md`, `lg`)| 142 | |thead |element |thead element of current table |The `thead` element with table titles used to build mobile mode for current `tbody`, you could specific from another table.| 143 | |eventMobileOn |callable|null |[Event](#events) callback function referred by `reflow-table.mobile.on` event| 144 | |eventMobileOff|callable|null |[Event](#events) callback function referred by `reflow-table.mobile.off` event| 145 | 146 | --- 147 | 148 | IMPLEMENTATION 149 | -------------- 150 | 151 | ### Update for Dynamic Content 152 | 153 | There is an update method which you can call when the table content has changed like page switching. The method will update or re-build table to ensure that everything is alright for mobile mode. 154 | 155 | ```javascript 156 | $('.reflow-table').reflowTable('update'); 157 | ``` 158 | 159 | Or using API usage to update: 160 | 161 | ```javascript 162 | var reflowTable = new ReflowTable('.reflow-table'); 163 | // Update Freeze Table while the original table is distorted 164 | function afterTablePageChanged() { 165 | reflowTable.update(); 166 | } 167 | ``` 168 | 169 | #### DataTables Integration 170 | 171 | [DataTables](https://datatables.net/) is a jQuery library to add advanced interaction controls to your HTML tables the free & easy way. 172 | 173 | To integrate with [DataTables](https://datatables.net/), you can apply a table with reflowTable before or after applying DataTables, set Reflow Table's `update` into DataTables callback such as `drawCallback` to keep updating data. For example: 174 | 175 | ```javascript 176 | var datatables = $('#table-datatables').DataTable({ 177 | 178 | "drawCallback": function( settings ) { 179 | 180 | $("#table-datatables").reflowTable('update'); 181 | } 182 | }); 183 | 184 | var reflowDataTable = $("#table-datatables").reflowTable(); 185 | ``` 186 | 187 | *Example: [DataTables Library Integration](https://yidas.github.io/jquery-reflow-table/#datatables)* 188 | 189 | > For DataTables integration, we choose `drawCallback` to implement Reflow Table update. After all, DataTables `page.dt` event fires too early before drawing, while `createdRow` has a lack of flexibility. 190 | 191 | ### Customize Styles 192 | 193 | You could customize pseudo `td` styles by defining CSS for Table Reflow: 194 | 195 | ```html 196 | 203 | ``` 204 | 205 | --- 206 | 207 | EVENTS 208 | ------ 209 | 210 | This library exposes a few events for hooking into reflow-table functionality. 211 | 212 | |Event Type |Description| 213 | |:-- |:-- | 214 | |reflow-table.mobile.on |This event fires immediately when the table is changed to mobile mode, this can be triggerd by [mobileMode()](#mobilemode) method call.| 215 | |reflow-table.mobile.off|This event fires immediately when the table is changed back from mobile mode, this can be triggerd by [mobileMode()](#mobilemode) method call.| 216 | 217 | *Example:* 218 | 219 | ```javascript 220 | $("#table").on('reflow-table.mobile.on', function() { 221 | alert('Mobile mode is now ON'); 222 | }); 223 | ``` 224 | 225 | --- 226 | 227 | API USAGE 228 | --------- 229 | 230 | ### update() 231 | 232 | Update or re-build each Reflow Table row th title for dynamic table content 233 | 234 | *See [Update for Dynamic Content](#update-for-dynamic-content)* 235 | 236 | ### mobileMode() 237 | 238 | Switch table to mobile mode or not 239 | 240 | ```javascript 241 | function boolean mobileMode(enable=true) 242 | ``` 243 | 244 | *Example: [Toggle Mobile Mode by Button](https://yidas.github.io/jquery-reflow-table/#toggle-mode)* 245 | 246 | ```javascript 247 | var reflowTable = $('#myTable').reflowTable(); 248 | 249 | $(".btn-mobile-on").click(function () { 250 | reflowTable.mobileMode(true); // Switch to mobile mode 251 | }); 252 | 253 | $(".btn0-mobile-off").click(function () { 254 | reflowTable.mobileMode(false); // Switch back to non-mobile mode 255 | }); 256 | ``` 257 | 258 | ### destroy() 259 | 260 | Destroy Freeze Table by same namespace 261 | 262 | *Example:* 263 | 264 | ```javascript 265 | var reflowTable = $('#myTable').reflowTable(); 266 | 267 | $('#tableDestroy').on( 'click', function () { 268 | reflowTable.destroy(); 269 | } ); 270 | ``` 271 | 272 | ### unbind() 273 | 274 | Unbind all events by same namespace 275 | 276 | 277 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-reflow-table", 3 | "description": "RWD reflow table switch for mobile UI/UX by collapsing columns", 4 | "version": "1.0.0", 5 | "keywords": [ 6 | "RWD", 7 | "responsive", 8 | "table", 9 | "switch-table", 10 | "table-reflow", 11 | "mobile-ui", 12 | "datatables" 13 | ], 14 | "homepage": "https://github.com/yidas/jquery-reflow-table", 15 | "authors": [ 16 | "Nick Tsai " 17 | ], 18 | "repository": { 19 | "type": "git", 20 | "url": "git://github.com/yidas/jquery-reflow-table.git" 21 | }, 22 | "license": "MIT", 23 | "main": [ 24 | "dist/js/reflow-table.js" 25 | ], 26 | "ignore": [ 27 | ".*", 28 | "docs" 29 | ], 30 | "dependencies": { 31 | "jquery": ">=1.10.1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /dist/css/reflow-table.css: -------------------------------------------------------------------------------- 1 | /** 2 | * yidas/jquery-reflow-table CSS style-sheet 3 | * 4 | * @author Nick Tsai 5 | * @since 1.0.3 6 | * @see https://github.com/yidas/jquery-reflow-table 7 | */ 8 | 9 | /* Mobile Mode: table */ 10 | table.reflow-table.reflow-table-mobile-mode { 11 | width: 100% !important; 12 | } 13 | 14 | /* Mobile Mode: Hide thead & tfoot */ 15 | .reflow-table.reflow-table-mobile-mode thead, .reflow-table.reflow-table-mobile-mode tfoot { 16 | display: none; 17 | } 18 | 19 | /* Mobile Mode: Mobile mode for td */ 20 | .reflow-table.reflow-table-mobile-mode td { 21 | /* overflow: hidden; */ 22 | width: 100%; 23 | display: block; 24 | } 25 | 26 | /* Mobile Mode: Dynamically fulfilling pseudo content */ 27 | .reflow-table.reflow-table-mobile-mode td:before { 28 | content: attr(data-th); 29 | } 30 | 31 | /* Pseudo pre-style */ 32 | .reflow-table td:before { 33 | display: inline-block; 34 | font-weight: bold; 35 | } 36 | 37 | /* Width Ratio style-sheet */ 38 | .reflow-table.reflow-table-w-15 td:before { 39 | min-width: 15%; 40 | } 41 | .reflow-table.reflow-table-w-20 td:before { 42 | min-width: 20%; 43 | } 44 | .reflow-table.reflow-table-w-25 td:before { 45 | min-width: 25%; 46 | } 47 | .reflow-table.reflow-table-w-30 td:before { 48 | min-width: 30%; 49 | } 50 | .reflow-table.reflow-table-w-40 td:before { 51 | min-width: 40%; 52 | } 53 | .reflow-table.reflow-table-w-50 td:before { 54 | min-width: 50%; 55 | } 56 | 57 | /* Width Size style-sheet */ 58 | .reflow-table.reflow-table-xs td:before { 59 | min-width: 100px; 60 | } 61 | .reflow-table.reflow-table-sm td:before { 62 | min-width: 150px; 63 | } 64 | .reflow-table.reflow-table-md td:before { 65 | min-width: 200px; 66 | } 67 | .reflow-table.reflow-table-lg td:before { 68 | min-width: 250px; 69 | } 70 | 71 | /** 72 | * DataTables library integration 73 | */ 74 | /* Fix DataTables Bootstrap 4 border problem for Mobile mode */ 75 | table.dataTable.reflow-table.reflow-table-mobile-mode td, table.dataTable.reflow-table.reflow-table-mobile-mode th { 76 | border-right: none; 77 | box-sizing: border-box; 78 | } -------------------------------------------------------------------------------- /dist/css/reflow-table.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * yidas/jquery-reflow-table CSS style-sheet 3 | * 4 | * @author Nick Tsai 5 | * @since 1.0.2 6 | * @see https://github.com/yidas/jquery-reflow-table 7 | */ 8 | table.reflow-table.reflow-table-mobile-mode{width:100%!important}.reflow-table.reflow-table-mobile-mode tfoot,.reflow-table.reflow-table-mobile-mode thead{display:none}.reflow-table.reflow-table-mobile-mode td{width:100%;display:block}.reflow-table.reflow-table-mobile-mode td:before{content:attr(data-th)}.reflow-table td:before{display:inline-block;font-weight:700}.reflow-table.reflow-table-w-15 td:before{min-width:15%}.reflow-table.reflow-table-w-20 td:before{min-width:20%}.reflow-table.reflow-table-w-25 td:before{min-width:25%}.reflow-table.reflow-table-w-30 td:before{min-width:30%}.reflow-table.reflow-table-w-40 td:before{min-width:40%}.reflow-table.reflow-table-w-50 td:before{min-width:50%}.reflow-table.reflow-table-xs td:before{min-width:100px}.reflow-table.reflow-table-sm td:before{min-width:150px}.reflow-table.reflow-table-md td:before{min-width:200px}.reflow-table.reflow-table-lg td:before{min-width:250px}table.dataTable.reflow-table.reflow-table-mobile-mode td,table.dataTable.reflow-table.reflow-table-mobile-mode th{border-right:none;box-sizing:border-box} -------------------------------------------------------------------------------- /dist/js/reflow-table.js: -------------------------------------------------------------------------------- 1 | /** 2 | * RWD reflow table switch for mobile UI/UX by collapsing columns 3 | * 4 | * @author Nick Tsai 5 | * @version 1.0.0 6 | * @see https://github.com/yidas/jquery-reflow-table 7 | */ 8 | (function ($, window) { 9 | 10 | 'use strict'; 11 | 12 | // Counter for this library 13 | var count = 0; 14 | 15 | /** 16 | * Main object 17 | * 18 | * @param {element} element 19 | * @param {object} options 20 | */ 21 | var ReflowTable = function(element, options) { 22 | 23 | // Target element initialization 24 | this.$table = $(element).first(); 25 | 26 | // Options 27 | this.options = options || {}; 28 | this.namespace; 29 | this.thead; 30 | this.autoWidth; 31 | this.widthRatio; 32 | this.widthSize; 33 | 34 | // Static properties 35 | this.className = 'reflow-table'; 36 | this.mobileModeClassName = 'reflow-table-mobile-mode'; 37 | this.titleAttr = 'data-th'; 38 | this.widthRatioClassName; 39 | this.widthWidthClassName; 40 | this.eventMobileOn = 'reflow-table.mobile.on'; 41 | this.eventMobileOff = 'reflow-table.mobile.off'; 42 | 43 | this.init(); 44 | 45 | return this; 46 | } 47 | 48 | /** 49 | * Initialization 50 | */ 51 | ReflowTable.prototype.init = function() { 52 | 53 | /** 54 | * Generate a namespace for the element 55 | */ 56 | var getNamespace = (function () { 57 | 58 | var ns; 59 | 60 | // First, try to get existent namespace if is init before 61 | var options = this.$table.data(this.className) || {}; 62 | ns = options.namespace || null; 63 | // Second, try to get element ID 64 | ns = ns || this.$table.attr('id'); 65 | // At least, enable unique ID generator 66 | if (!ns) { 67 | 68 | count += 1; 69 | ns = this.className + '-' + count; 70 | } 71 | 72 | return ns; 73 | 74 | }).bind(this); 75 | 76 | /** 77 | * Initialize and save options 78 | */ 79 | var setOptions = (function() { 80 | 81 | var options = this.options; 82 | this.namespace = this.options.namespace || getNamespace(); 83 | this.thead = this.options.thead || this.$table.find("thead"); 84 | this.autoWidth = (typeof options.autoWidth !== 'undefined') ? options.autoWidth : 736; 85 | this.widthRatio = (typeof options.widthRatio !== 'undefined') ? options.widthRatio : '50'; 86 | this.widthSize = (typeof options.widthSize !== 'undefined') ? options.widthSize : false; 87 | // Static properties 88 | this.widthRatioClassName = this.className + '-w-' + this.widthRatio; 89 | this.widthWidthClassName= this.className + '-' + this.widthSize; 90 | 91 | // Save options 92 | this.$table.data(this.className, this.options); 93 | 94 | }).bind(this); 95 | 96 | /** 97 | * Restore options 98 | */ 99 | var restoreOptions = (function() { 100 | 101 | this.options = this.$table.data(this.className) || {}; 102 | // Reset and save options 103 | setOptions(); 104 | 105 | return this.options; 106 | 107 | }).bind(this); 108 | 109 | // Element check 110 | if (!this.$table.length) 111 | throw "No element selected"; 112 | if (!this.$table.is("table")) 113 | throw "The element must be a table dom"; 114 | 115 | /** 116 | * Update Mode 117 | */ 118 | if (this.options==='update') { 119 | 120 | restoreOptions(); 121 | // Life cycle for update only, there is no performance difference for deleting self object so just keeping update object. 122 | return this.update(); 123 | } 124 | 125 | /** 126 | * Options Setting 127 | */ 128 | setOptions(); 129 | 130 | // Destroy the table element before initializing 131 | this.destroy(); 132 | 133 | // Establish every th text for mobile mode 134 | this.build(); 135 | 136 | // 137 | if (typeof this.options.eventMobileOn === 'function') { 138 | this.$table.on(this.eventMobileOn, this.options.eventMobileOn); 139 | } 140 | if (typeof this.options.eventMobileOff === 'function') { 141 | this.$table.on(this.eventMobileOff, this.options.eventMobileOff); 142 | } 143 | 144 | // Add class for Table Reflow 145 | this.$table.addClass(this.className); 146 | // Width Ratio setting 147 | if (this.widthRatio) { 148 | this.$table.addClass(this.widthRatioClassName); 149 | } 150 | // Width Size setting 151 | if (this.widthSize) { 152 | this.$table.addClass(this.widthWidthClassName); 153 | } 154 | // AutoWidth setting 155 | if (this.autoWidth) { 156 | 157 | var that = this; 158 | 159 | /** 160 | * Listener - Window resize detection for Table Reflow 161 | * 162 | * To support mode switch, this library doesn't use CSS @media to implement detection. 163 | */ 164 | $(window).on('resize.'+that.namespace, function() { 165 | 166 | // Detect mode 167 | if ($(window).width() <= that.autoWidth) { 168 | // Mobile mode 169 | that.mobileMode(true); 170 | } 171 | else { 172 | // Non-Mobile mode 173 | that.mobileMode(false); 174 | } 175 | }); 176 | 177 | // Trigger at the start 178 | $(window).trigger('resize.'+this.namespace); 179 | } 180 | } 181 | 182 | /** 183 | * Establishing th title to prepared td DOMs for mobile mode 184 | * 185 | * @return {self} 186 | */ 187 | ReflowTable.prototype.build = function() { 188 | 189 | var $table = this.$table; 190 | var titleAttr = this.titleAttr; 191 | 192 | // every th text to td data for mobile mode 193 | $(this.thead).find("th").each(function(key, thRow) { 194 | 195 | $table.find("tbody tr td:nth-child("+(key+1)+")") 196 | .attr(titleAttr, $(thRow).text()); 197 | }); 198 | 199 | return this; 200 | } 201 | 202 | /** 203 | * Switch table to mobile mode or not 204 | * 205 | * @param {boolean} enable Enable mobile mode or not, default is true 206 | * @return {self} 207 | */ 208 | ReflowTable.prototype.mobileMode = function(enable) { 209 | 210 | enable = (typeof enable !== 'undefined') ? enable : true; 211 | var isMobileMode = this.isMobileMode(); 212 | 213 | // Detect enabled mode 214 | if (enable) { 215 | // Mobile mode 216 | this.$table.addClass(this.mobileModeClassName); 217 | // Event trigger of first mobile on 218 | if (!isMobileMode) 219 | this.$table.trigger($.Event(this.eventMobileOn)); 220 | } 221 | else { 222 | // non-Mobile mode 223 | this.$table.removeClass(this.mobileModeClassName); 224 | // Event trigger of first mobile off 225 | if (isMobileMode) 226 | this.$table.trigger($.Event(this.eventMobileOff)); 227 | } 228 | 229 | return this; 230 | } 231 | 232 | /** 233 | * Switch table between mobile mode or original mode 234 | * 235 | * @return {self} 236 | */ 237 | ReflowTable.prototype.switchMode = function() { 238 | 239 | // Detect the reflow table is in mobile mode or not 240 | if (this.isMobileMode()) 241 | this.mobileMode(false); 242 | else 243 | this.mobileMode(true); 244 | 245 | return this; 246 | } 247 | 248 | /** 249 | * Check is in mobile mode 250 | * 251 | * @return {boolean} 252 | */ 253 | ReflowTable.prototype.isMobileMode = function() { 254 | 255 | return (this.$table.hasClass(this.mobileModeClassName)) ? true : false; 256 | } 257 | 258 | /** 259 | * Update or re-build each Reflow Table row th title for dynamic table content 260 | * 261 | * @return {self} 262 | */ 263 | ReflowTable.prototype.update = function() { 264 | 265 | // Re-build only without any options 266 | this.build(); 267 | return this; 268 | } 269 | 270 | /** 271 | * Unbind all events by same namespace 272 | * 273 | * @return {self} 274 | */ 275 | ReflowTable.prototype.unbind = function() { 276 | 277 | // Unbind resize listener 278 | $(window).off('resize.'+this.namespace); 279 | 280 | // Unbind all events of the table 281 | this.$table.off(); 282 | 283 | return this; 284 | } 285 | 286 | /** 287 | * Destroy Table Reflow by same namespace 288 | */ 289 | ReflowTable.prototype.destroy = function() { 290 | 291 | this.unbind(); 292 | // Switch mode back 293 | this.mobileMode(false); 294 | // Remove class for Table Reflow 295 | this.$table.removeClass(this.className); 296 | // Remove Width Ratio setting 297 | if (this.widthRatio) { 298 | this.$table.removeClass(this.widthRatioClassName); 299 | } 300 | // Remove Width Size setting 301 | if (this.widthSize) { 302 | this.$table.removeClass(this.widthWidthClassName); 303 | } 304 | } 305 | 306 | /** 307 | * Interface 308 | */ 309 | // Class for single element 310 | window.ReflowTable = ReflowTable; 311 | // jQuery interface 312 | $.fn.reflowTable = function (options) { 313 | 314 | // Single/Multiple mode 315 | if (this.length === 1) { 316 | 317 | return new ReflowTable(this, options) 318 | } 319 | else if (this.length > 1) { 320 | 321 | var result = []; 322 | // Multiple elements bundle 323 | this.each(function () { 324 | result.push(new ReflowTable(this, options)); 325 | }); 326 | 327 | return result; 328 | } 329 | 330 | return false; 331 | } 332 | 333 | })(jQuery, window); -------------------------------------------------------------------------------- /dist/js/reflow-table.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * RWD reflow table switch for mobile UI/UX by collapsing columns 3 | * 4 | * @author Nick Tsai 5 | * @version 1.0.0 6 | * @see https://github.com/yidas/jquery-reflow-table 7 | */ 8 | !function(t,i){"use strict";var e=0,s=function(i,e){return this.$table=t(i).first(),this.options=e||{},this.namespace,this.thead,this.autoWidth,this.widthRatio,this.widthSize,this.className="reflow-table",this.mobileModeClassName="reflow-table-mobile-mode",this.titleAttr="data-th",this.widthRatioClassName,this.widthWidthClassName,this.eventMobileOn="reflow-table.mobile.on",this.eventMobileOff="reflow-table.mobile.off",this.init(),this};s.prototype.init=function(){var s=function(){var t;return(t=(t=(this.$table.data(this.className)||{}).namespace||null)||this.$table.attr("id"))||(e+=1,t=this.className+"-"+e),t}.bind(this),h=function(){var t=this.options;this.namespace=this.options.namespace||s(),this.thead=this.options.thead||this.$table.find("thead"),this.autoWidth=void 0!==t.autoWidth?t.autoWidth:736,this.widthRatio=void 0!==t.widthRatio?t.widthRatio:"50",this.widthSize=void 0!==t.widthSize&&t.widthSize,this.widthRatioClassName=this.className+"-w-"+this.widthRatio,this.widthWidthClassName=this.className+"-"+this.widthSize,this.$table.data(this.className,this.options)}.bind(this),o=function(){return this.options=this.$table.data(this.className)||{},h(),this.options}.bind(this);if(!this.$table.length)throw"No element selected";if(!this.$table.is("table"))throw"The element must be a table dom";if("update"===this.options)return o(),this.update();if(h(),this.destroy(),this.build(),"function"==typeof this.options.eventMobileOn&&this.$table.on(this.eventMobileOn,this.options.eventMobileOn),"function"==typeof this.options.eventMobileOff&&this.$table.on(this.eventMobileOff,this.options.eventMobileOff),this.$table.addClass(this.className),this.widthRatio&&this.$table.addClass(this.widthRatioClassName),this.widthSize&&this.$table.addClass(this.widthWidthClassName),this.autoWidth){var a=this;t(i).on("resize."+a.namespace,function(){t(i).width()<=a.autoWidth?a.mobileMode(!0):a.mobileMode(!1)}),t(i).trigger("resize."+this.namespace)}},s.prototype.build=function(){var i=this.$table,e=this.titleAttr;return t(this.thead).find("th").each(function(s,h){i.find("tbody tr td:nth-child("+(s+1)+")").attr(e,t(h).text())}),this},s.prototype.mobileMode=function(i){i=void 0===i||i;var e=this.isMobileMode();return i?(this.$table.addClass(this.mobileModeClassName),e||this.$table.trigger(t.Event(this.eventMobileOn))):(this.$table.removeClass(this.mobileModeClassName),e&&this.$table.trigger(t.Event(this.eventMobileOff))),this},s.prototype.switchMode=function(){return this.isMobileMode()?this.mobileMode(!1):this.mobileMode(!0),this},s.prototype.isMobileMode=function(){return!!this.$table.hasClass(this.mobileModeClassName)},s.prototype.update=function(){return this.build(),this},s.prototype.unbind=function(){return t(i).off("resize."+this.namespace),this.$table.off(),this},s.prototype.destroy=function(){this.unbind(),this.mobileMode(!1),this.$table.removeClass(this.className),this.widthRatio&&this.$table.removeClass(this.widthRatioClassName),this.widthSize&&this.$table.removeClass(this.widthWidthClassName)},i.ReflowTable=s,t.fn.reflowTable=function(t){if(1===this.length)return new s(this,t);if(this.length>1){var i=[];return this.each(function(){i.push(new s(this,t))}),i}return!1}}(jQuery,window); -------------------------------------------------------------------------------- /docs/dist/css/reflow-table.css: -------------------------------------------------------------------------------- 1 | /** 2 | * yidas/jquery-reflow-table CSS style-sheet 3 | * 4 | * @author Nick Tsai 5 | * @since 1.0.3 6 | * @see https://github.com/yidas/jquery-reflow-table 7 | */ 8 | 9 | /* Mobile Mode: table */ 10 | table.reflow-table.reflow-table-mobile-mode { 11 | width: 100% !important; 12 | } 13 | 14 | /* Mobile Mode: Hide thead & tfoot */ 15 | .reflow-table.reflow-table-mobile-mode thead, .reflow-table.reflow-table-mobile-mode tfoot { 16 | display: none; 17 | } 18 | 19 | /* Mobile Mode: Mobile mode for td */ 20 | .reflow-table.reflow-table-mobile-mode td { 21 | /* overflow: hidden; */ 22 | width: 100%; 23 | display: block; 24 | } 25 | 26 | /* Mobile Mode: Dynamically fulfilling pseudo content */ 27 | .reflow-table.reflow-table-mobile-mode td:before { 28 | content: attr(data-th); 29 | } 30 | 31 | /* Pseudo pre-style */ 32 | .reflow-table td:before { 33 | display: inline-block; 34 | font-weight: bold; 35 | } 36 | 37 | /* Width Ratio style-sheet */ 38 | .reflow-table.reflow-table-w-15 td:before { 39 | min-width: 15%; 40 | } 41 | .reflow-table.reflow-table-w-20 td:before { 42 | min-width: 20%; 43 | } 44 | .reflow-table.reflow-table-w-25 td:before { 45 | min-width: 25%; 46 | } 47 | .reflow-table.reflow-table-w-30 td:before { 48 | min-width: 30%; 49 | } 50 | .reflow-table.reflow-table-w-40 td:before { 51 | min-width: 40%; 52 | } 53 | .reflow-table.reflow-table-w-50 td:before { 54 | min-width: 50%; 55 | } 56 | 57 | /* Width Size style-sheet */ 58 | .reflow-table.reflow-table-xs td:before { 59 | min-width: 100px; 60 | } 61 | .reflow-table.reflow-table-sm td:before { 62 | min-width: 150px; 63 | } 64 | .reflow-table.reflow-table-md td:before { 65 | min-width: 200px; 66 | } 67 | .reflow-table.reflow-table-lg td:before { 68 | min-width: 250px; 69 | } 70 | 71 | /** 72 | * DataTables library integration 73 | */ 74 | /* Fix DataTables Bootstrap 4 border problem for Mobile mode */ 75 | table.dataTable.reflow-table.reflow-table-mobile-mode td, table.dataTable.reflow-table.reflow-table-mobile-mode th { 76 | border-right: none; 77 | box-sizing: border-box; 78 | } -------------------------------------------------------------------------------- /docs/dist/js/reflow-table.js: -------------------------------------------------------------------------------- 1 | /** 2 | * RWD reflow table switch for mobile UI/UX by collapsing columns 3 | * 4 | * @author Nick Tsai 5 | * @version 1.0.0 6 | * @see https://github.com/yidas/jquery-reflow-table 7 | */ 8 | (function ($, window) { 9 | 10 | 'use strict'; 11 | 12 | // Counter for this library 13 | var count = 0; 14 | 15 | /** 16 | * Main object 17 | * 18 | * @param {element} element 19 | * @param {object} options 20 | */ 21 | var ReflowTable = function(element, options) { 22 | 23 | // Target element initialization 24 | this.$table = $(element).first(); 25 | 26 | // Options 27 | this.options = options || {}; 28 | this.namespace; 29 | this.thead; 30 | this.autoWidth; 31 | this.widthRatio; 32 | this.widthSize; 33 | 34 | // Static properties 35 | this.className = 'reflow-table'; 36 | this.mobileModeClassName = 'reflow-table-mobile-mode'; 37 | this.titleAttr = 'data-th'; 38 | this.widthRatioClassName; 39 | this.widthWidthClassName; 40 | this.eventMobileOn = 'reflow-table.mobile.on'; 41 | this.eventMobileOff = 'reflow-table.mobile.off'; 42 | 43 | this.init(); 44 | 45 | return this; 46 | } 47 | 48 | /** 49 | * Initialization 50 | */ 51 | ReflowTable.prototype.init = function() { 52 | 53 | /** 54 | * Generate a namespace for the element 55 | */ 56 | var getNamespace = (function () { 57 | 58 | var ns; 59 | 60 | // First, try to get existent namespace if is init before 61 | var options = this.$table.data(this.className) || {}; 62 | ns = options.namespace || null; 63 | // Second, try to get element ID 64 | ns = ns || this.$table.attr('id'); 65 | // At least, enable unique ID generator 66 | if (!ns) { 67 | 68 | count += 1; 69 | ns = this.className + '-' + count; 70 | } 71 | 72 | return ns; 73 | 74 | }).bind(this); 75 | 76 | /** 77 | * Initialize and save options 78 | */ 79 | var setOptions = (function() { 80 | 81 | var options = this.options; 82 | this.namespace = this.options.namespace || getNamespace(); 83 | this.thead = this.options.thead || this.$table.find("thead"); 84 | this.autoWidth = (typeof options.autoWidth !== 'undefined') ? options.autoWidth : 736; 85 | this.widthRatio = (typeof options.widthRatio !== 'undefined') ? options.widthRatio : '50'; 86 | this.widthSize = (typeof options.widthSize !== 'undefined') ? options.widthSize : false; 87 | // Static properties 88 | this.widthRatioClassName = this.className + '-w-' + this.widthRatio; 89 | this.widthWidthClassName= this.className + '-' + this.widthSize; 90 | 91 | // Save options 92 | this.$table.data(this.className, this.options); 93 | 94 | }).bind(this); 95 | 96 | /** 97 | * Restore options 98 | */ 99 | var restoreOptions = (function() { 100 | 101 | this.options = this.$table.data(this.className) || {}; 102 | // Reset and save options 103 | setOptions(); 104 | 105 | return this.options; 106 | 107 | }).bind(this); 108 | 109 | // Element check 110 | if (!this.$table.length) 111 | throw "No element selected"; 112 | if (!this.$table.is("table")) 113 | throw "The element must be a table dom"; 114 | 115 | /** 116 | * Update Mode 117 | */ 118 | if (this.options==='update') { 119 | 120 | restoreOptions(); 121 | // Life cycle for update only, there is no performance difference for deleting self object so just keeping update object. 122 | return this.update(); 123 | } 124 | 125 | /** 126 | * Options Setting 127 | */ 128 | setOptions(); 129 | 130 | // Destroy the table element before initializing 131 | this.destroy(); 132 | 133 | // Establish every th text for mobile mode 134 | this.build(); 135 | 136 | // 137 | if (typeof this.options.eventMobileOn === 'function') { 138 | this.$table.on(this.eventMobileOn, this.options.eventMobileOn); 139 | } 140 | if (typeof this.options.eventMobileOff === 'function') { 141 | this.$table.on(this.eventMobileOff, this.options.eventMobileOff); 142 | } 143 | 144 | // Add class for Table Reflow 145 | this.$table.addClass(this.className); 146 | // Width Ratio setting 147 | if (this.widthRatio) { 148 | this.$table.addClass(this.widthRatioClassName); 149 | } 150 | // Width Size setting 151 | if (this.widthSize) { 152 | this.$table.addClass(this.widthWidthClassName); 153 | } 154 | // AutoWidth setting 155 | if (this.autoWidth) { 156 | 157 | var that = this; 158 | 159 | /** 160 | * Listener - Window resize detection for Table Reflow 161 | * 162 | * To support mode switch, this library doesn't use CSS @media to implement detection. 163 | */ 164 | $(window).on('resize.'+that.namespace, function() { 165 | 166 | // Detect mode 167 | if ($(window).width() <= that.autoWidth) { 168 | // Mobile mode 169 | that.mobileMode(true); 170 | } 171 | else { 172 | // Non-Mobile mode 173 | that.mobileMode(false); 174 | } 175 | }); 176 | 177 | // Trigger at the start 178 | $(window).trigger('resize.'+this.namespace); 179 | } 180 | } 181 | 182 | /** 183 | * Establishing th title to prepared td DOMs for mobile mode 184 | * 185 | * @return {self} 186 | */ 187 | ReflowTable.prototype.build = function() { 188 | 189 | var $table = this.$table; 190 | var titleAttr = this.titleAttr; 191 | 192 | // every th text to td data for mobile mode 193 | $(this.thead).find("th").each(function(key, thRow) { 194 | 195 | $table.find("tbody tr td:nth-child("+(key+1)+")") 196 | .attr(titleAttr, $(thRow).text()); 197 | }); 198 | 199 | return this; 200 | } 201 | 202 | /** 203 | * Switch table to mobile mode or not 204 | * 205 | * @param {boolean} enable Enable mobile mode or not, default is true 206 | * @return {self} 207 | */ 208 | ReflowTable.prototype.mobileMode = function(enable) { 209 | 210 | enable = (typeof enable !== 'undefined') ? enable : true; 211 | var isMobileMode = this.isMobileMode(); 212 | 213 | // Detect enabled mode 214 | if (enable) { 215 | // Mobile mode 216 | this.$table.addClass(this.mobileModeClassName); 217 | // Event trigger of first mobile on 218 | if (!isMobileMode) 219 | this.$table.trigger($.Event(this.eventMobileOn)); 220 | } 221 | else { 222 | // non-Mobile mode 223 | this.$table.removeClass(this.mobileModeClassName); 224 | // Event trigger of first mobile off 225 | if (isMobileMode) 226 | this.$table.trigger($.Event(this.eventMobileOff)); 227 | } 228 | 229 | return this; 230 | } 231 | 232 | /** 233 | * Switch table between mobile mode or original mode 234 | * 235 | * @return {self} 236 | */ 237 | ReflowTable.prototype.switchMode = function() { 238 | 239 | // Detect the reflow table is in mobile mode or not 240 | if (this.isMobileMode()) 241 | this.mobileMode(false); 242 | else 243 | this.mobileMode(true); 244 | 245 | return this; 246 | } 247 | 248 | /** 249 | * Check is in mobile mode 250 | * 251 | * @return {boolean} 252 | */ 253 | ReflowTable.prototype.isMobileMode = function() { 254 | 255 | return (this.$table.hasClass(this.mobileModeClassName)) ? true : false; 256 | } 257 | 258 | /** 259 | * Update or re-build each Reflow Table row th title for dynamic table content 260 | * 261 | * @return {self} 262 | */ 263 | ReflowTable.prototype.update = function() { 264 | 265 | // Re-build only without any options 266 | this.build(); 267 | return this; 268 | } 269 | 270 | /** 271 | * Unbind all events by same namespace 272 | * 273 | * @return {self} 274 | */ 275 | ReflowTable.prototype.unbind = function() { 276 | 277 | // Unbind resize listener 278 | $(window).off('resize.'+this.namespace); 279 | 280 | // Unbind all events of the table 281 | this.$table.off(); 282 | 283 | return this; 284 | } 285 | 286 | /** 287 | * Destroy Table Reflow by same namespace 288 | */ 289 | ReflowTable.prototype.destroy = function() { 290 | 291 | this.unbind(); 292 | // Switch mode back 293 | this.mobileMode(false); 294 | // Remove class for Table Reflow 295 | this.$table.removeClass(this.className); 296 | // Remove Width Ratio setting 297 | if (this.widthRatio) { 298 | this.$table.removeClass(this.widthRatioClassName); 299 | } 300 | // Remove Width Size setting 301 | if (this.widthSize) { 302 | this.$table.removeClass(this.widthWidthClassName); 303 | } 304 | } 305 | 306 | /** 307 | * Interface 308 | */ 309 | // Class for single element 310 | window.ReflowTable = ReflowTable; 311 | // jQuery interface 312 | $.fn.reflowTable = function (options) { 313 | 314 | // Single/Multiple mode 315 | if (this.length === 1) { 316 | 317 | return new ReflowTable(this, options) 318 | } 319 | else if (this.length > 1) { 320 | 321 | var result = []; 322 | // Multiple elements bundle 323 | this.each(function () { 324 | result.push(new ReflowTable(this, options)); 325 | }); 326 | 327 | return result; 328 | } 329 | 330 | return false; 331 | } 332 | 333 | })(jQuery, window); -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | yidas/jquery-reflow-table Demo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 26 | 27 | 28 | 29 |
30 |
31 | 41 |

jQuery Reflow Table

42 |
43 | 44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 |
CompanyLast TradeTrade TimeChangePrev CloseOpenBidAsk1y Target EstLoremIpsum
GOOG Google Inc.597.7412:12PM14.81 (2.54%)582.93597.95597.73 x 100597.91 x 300731.10Spanning cell
AAPL Apple Inc.378.9412:22PM5.74 (1.54%)373.20381.02378.92 x 300378.99 x 100505.94Spanning cell
AMZN Amazon.com Inc.191.5512:23PM3.16 (1.68%)188.39194.99191.52 x 300191.58 x 100240.32Spanning cell
ORCL Oracle Corporation31.1512:44PM1.41 (4.72%)29.7430.6731.14 x 650031.15 x 320036.11Spanning cell
MSFT Microsoft Corporation25.5012:27PM0.66 (2.67%)24.8425.3725.50 x 7110025.51 x 1780031.50Non-spanningNon-spanning
CSCO Cisco Systems, Inc.18.6512:45PM0.97 (5.49%)17.6818.2318.65 x 1030018.66 x 2400021.12Non-spanningNon-spanning
YHOO Yahoo! Inc.15.8112:25PM0.11 (0.67%)15.7015.9415.79 x 610015.80 x 1700018.16Non-spanningNon-spanning
151 |
152 | 153 | 154 |
155 | 156 |
157 |
Example Code
158 |
159 |
$("#table-basic").reflowTable();
160 |
161 |
162 | 163 | 164 |
165 | 166 | 167 |
Toggle Mobile Mode by Button
168 | 169 |

170 | 171 | 172 | 173 |

174 |
175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 |
CompanyLast TradeTrade TimeChangePrev CloseOpenBidAsk1y Target Est
GOOG Google Inc.597.7412:12PM14.81 (2.54%)582.93597.95597.73 x 100597.91 x 300731.10
AAPL Apple Inc.378.9412:22PM5.74 (1.54%)373.20381.02378.92 x 300378.99 x 100505.94
AMZN Amazon.com Inc.191.5512:23PM3.16 (1.68%)188.39194.99191.52 x 300191.58 x 100240.32
ORCL Oracle Corporation31.1512:44PM1.41 (4.72%)29.7430.6731.14 x 650031.15 x 320036.11
MSFT Microsoft Corporation25.5012:27PM0.66 (2.67%)24.8425.3725.50 x 7110025.51 x 1780031.50
CSCO Cisco Systems, Inc.18.6512:45PM0.97 (5.49%)17.6818.2318.65 x 1030018.66 x 2400021.12
YHOO Yahoo! Inc.15.8112:25PM0.11 (0.67%)15.7015.9415.79 x 610015.80 x 1700018.16
269 |
270 | 271 |
272 | 273 |
274 |
Example Code
275 |
276 |
var reflowTable = $("#table-toggle").reflowTable({
277 |   'autoWidth': false,
278 | });
279 | // Add click listener for mobile-on
280 | $(".btn-reflow-toggle-mobile-on").click(function () {
281 |   reflowTable.mobileMode(true);
282 | });
283 | // Add click listener for mobile-off
284 | $(".btn-reflow-toggle-mobile-off").click(function () {
285 |   reflowTable.mobileMode(false);
286 | });
287 | // Add click listener for mobile switcher
288 | $(".btn-reflow-toggle-mobile-switch").click(function () {
289 |   reflowTable.switchMode();
290 | });
291 |
292 |
293 | 294 | 295 |
296 | 297 | 298 |
Mobile Mode Only
299 | 300 |
301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 |
CompanyLast TradeTrade TimeChangePrev CloseOpenBidAsk1y Target Est
GOOG Google Inc.597.7412:12PM14.81 (2.54%)582.93597.95597.73 x 100597.91 x 300731.10
AAPL Apple Inc.378.9412:22PM5.74 (1.54%)373.20381.02378.92 x 300378.99 x 100505.94
AMZN Amazon.com Inc.191.5512:23PM3.16 (1.68%)188.39194.99191.52 x 300191.58 x 100240.32
ORCL Oracle Corporation31.1512:44PM1.41 (4.72%)29.7430.6731.14 x 650031.15 x 320036.11
362 |
363 | 364 |
365 | 366 |
367 |
Example Code
368 |
369 |
$("#table-mobile-only").reflowTable({
370 |   'autoWidth': false,
371 | }).mobileMode(true);
372 |
373 |
374 | 375 | 376 |
377 | 378 | 379 |
Customized CSS Styles
380 | 381 |
382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 |
CompanyLast TradeTrade TimeChangePrev CloseOpenBidAsk1y Target Est
GOOG Google Inc.597.7412:12PM14.81 (2.54%)582.93597.95597.73 x 100597.91 x 300731.10
AAPL Apple Inc.378.9412:22PM5.74 (1.54%)373.20381.02378.92 x 300378.99 x 100505.94
421 |
422 | 423 |
424 | 425 |
426 |
Example Code
427 |
428 |
$("#table-customized-css").addClass('reflow-table-customized-style')
429 | .reflowTable({
430 |   'autoWidth': false,
431 | }).mobileMode(true);
432 |
433 |
434 | 435 | 436 |
437 | 438 | 439 |
Reflow Table with Events
440 | 441 |

442 | 443 | 444 | 445 |

446 |
447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 |
CompanyLast TradeTrade TimeChangePrev CloseOpenBidAsk1y Target Est
GOOG Google Inc.597.7412:12PM14.81 (2.54%)582.93597.95597.73 x 100597.91 x 300731.10
AAPL Apple Inc.378.9412:22PM5.74 (1.54%)373.20381.02378.92 x 300378.99 x 100505.94
486 |
487 | 488 |
489 | 490 |
491 |
Example Code
492 |
493 |
// Reflow Table with Events
494 | var reflowEvents = $("#table-events").reflowTable({
495 |   // 'autoWidth': false,
496 |   'eventMobileOn': function() {
497 |     alert('This table is in Mobile mode now');
498 |   },
499 |   'eventMobileOff': function() {
500 |     alert('This table is in Desktop mode now');
501 |   },
502 | });
503 | // Event Binding
504 | $("#table-events").on('reflow-table.mobile.on', function() {
505 |   console.log('This table is in Mobile mode now (Trigger by Event binding)');
506 | });
507 | $("#table-events").on('reflow-table.mobile.off', function() {
508 |   console.log('This table is in Desktop mode now (Trigger by Event binding)');
509 | });
510 |
511 |
512 | 513 | 514 |
515 | 516 | 517 |
DataTables Library Integration
518 | 519 |

520 | 521 | 522 | 523 |

524 |
525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 |
CompanyLast TradeTrade TimeChangePrev CloseOpenBidAsk1y Target Est
GOOG Google Inc.597.7412:12PM14.81 (2.54%)582.93597.95597.73 x 100597.91 x 300731.10
AAPL Apple Inc.378.9412:22PM5.74 (1.54%)373.20381.02378.92 x 300378.99 x 100505.94
AMZN Amazon.com Inc.191.5512:23PM3.16 (1.68%)188.39194.99191.52 x 300191.58 x 100240.32
ORCL Oracle Corporation31.1512:44PM1.41 (4.72%)29.7430.6731.14 x 650031.15 x 320036.11
MSFT Microsoft Corporation25.5012:27PM0.66 (2.67%)24.8425.3725.50 x 7110025.51 x 1780031.50
CSCO Cisco Systems, Inc.18.6512:45PM0.97 (5.49%)17.6818.2318.65 x 1030018.66 x 2400021.12
YHOO Yahoo! Inc.15.8112:25PM0.11 (0.67%)15.7015.9415.79 x 610015.80 x 1700018.16
GOOG Google Inc.597.7412:12PM14.81 (2.54%)582.93597.95597.73 x 100597.91 x 300731.10
AAPL Apple Inc.378.9412:22PM5.74 (1.54%)373.20381.02378.92 x 300378.99 x 100505.94
AMZN Amazon.com Inc.191.5512:23PM3.16 (1.68%)188.39194.99191.52 x 300191.58 x 100240.32
ORCL Oracle Corporation31.1512:44PM1.41 (4.72%)29.7430.6731.14 x 650031.15 x 320036.11
MSFT Microsoft Corporation25.5012:27PM0.66 (2.67%)24.8425.3725.50 x 7110025.51 x 1780031.50
CSCO Cisco Systems, Inc.18.6512:45PM0.97 (5.49%)17.6818.2318.65 x 1030018.66 x 2400021.12
YHOO Yahoo! Inc.15.8112:25PM0.11 (0.67%)15.7015.9415.79 x 610015.80 x 1700018.16
GOOG Google Inc.597.7412:12PM14.81 (2.54%)582.93597.95597.73 x 100597.91 x 300731.10
AAPL Apple Inc.378.9412:22PM5.74 (1.54%)373.20381.02378.92 x 300378.99 x 100505.94
AMZN Amazon.com Inc.191.5512:23PM3.16 (1.68%)188.39194.99191.52 x 300191.58 x 100240.32
ORCL Oracle Corporation31.1512:44PM1.41 (4.72%)29.7430.6731.14 x 650031.15 x 320036.11
MSFT Microsoft Corporation25.5012:27PM0.66 (2.67%)24.8425.3725.50 x 7110025.51 x 1780031.50
CSCO Cisco Systems, Inc.18.6512:45PM0.97 (5.49%)17.6818.2318.65 x 1030018.66 x 2400021.12
YHOO Yahoo! Inc.15.8112:25PM0.11 (0.67%)15.7015.9415.79 x 610015.80 x 1700018.16
775 |
776 | 777 |
778 | 779 |
780 |
Example Code
781 |
782 |
/**
783 |  * DataTables integration
784 |  * 
785 |  * For this integration, we choose `drawCallback` to implement Reflow Table update. 
786 |  * DataTables `page.dt` event is triggered too early before draw, while `createdRow` is lacked 
787 |  * of flexibility.
788 |  * 
789 |  * @see https://datatables.net/reference/option/drawCallback 
790 |  */
791 | var datatables = $('#table-datatables').DataTable({
792 |  
793 |   "drawCallback": function( settings ) {
794 |     // Bind a draw callback to re-build row th title for Reflow Table after
795 |     $("#table-datatables").reflowTable('update');
796 |   }
797 | });
798 | // Initialize Reflow Table (Before or after DataTables are both accepted)
799 | var reflowDataTable = $("#table-datatables").reflowTable({
800 |   'autoWidth': false,
801 | });
802 |
803 |
804 | 805 | 806 | 809 | 810 |
811 | 812 | 813 | 814 | 815 | 816 | 817 | 914 | 915 | -------------------------------------------------------------------------------- /docs/narrow-jumbotron.css: -------------------------------------------------------------------------------- 1 | /* Space out content a bit */ 2 | body { 3 | padding-top: 1.5rem; 4 | padding-bottom: 1.5rem; 5 | } 6 | 7 | /* Everything but the jumbotron gets side spacing for mobile first views */ 8 | .header, 9 | .marketing, 10 | .footer { 11 | padding-right: 1rem; 12 | padding-left: 1rem; 13 | } 14 | 15 | /* Custom page header */ 16 | .header { 17 | padding-bottom: 1rem; 18 | border-bottom: .05rem solid #e5e5e5; 19 | } 20 | /* Make the masthead heading the same height as the navigation */ 21 | .header h3 { 22 | margin-top: 0; 23 | margin-bottom: 0; 24 | line-height: 3rem; 25 | } 26 | 27 | /* Custom page footer */ 28 | .footer { 29 | padding-top: 1.5rem; 30 | color: #777; 31 | border-top: .05rem solid #e5e5e5; 32 | } 33 | 34 | /* Customize container */ 35 | @media (min-width: 48em) { 36 | .container { 37 | max-width: 46rem; 38 | } 39 | } 40 | .container-narrow > hr { 41 | margin: 2rem 0; 42 | } 43 | 44 | /* Main marketing message and sign up button */ 45 | .jumbotron { 46 | text-align: center; 47 | border-bottom: .05rem solid #e5e5e5; 48 | } 49 | .jumbotron .btn { 50 | padding: .75rem 1.5rem; 51 | font-size: 1.5rem; 52 | } 53 | 54 | /* Supporting marketing content */ 55 | .marketing { 56 | margin: 3rem 0; 57 | } 58 | .marketing p + h4 { 59 | margin-top: 1.5rem; 60 | } 61 | 62 | /* Responsive: Portrait tablets and up */ 63 | @media screen and (min-width: 48em) { 64 | /* Remove the padding we set earlier */ 65 | .header, 66 | .marketing, 67 | .footer { 68 | padding-right: 0; 69 | padding-left: 0; 70 | } 71 | /* Space out the masthead */ 72 | .header { 73 | margin-bottom: 2rem; 74 | } 75 | /* Remove the bottom border on the jumbotron for visual effect */ 76 | .jumbotron { 77 | border-bottom: 0; 78 | } 79 | } -------------------------------------------------------------------------------- /img/demo-mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yidas/jquery-reflow-table/983e103ac3e03e60df1d27254612c97648967055/img/demo-mobile.png -------------------------------------------------------------------------------- /img/demo-origin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yidas/jquery-reflow-table/983e103ac3e03e60df1d27254612c97648967055/img/demo-origin.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-reflow-table", 3 | "description": "RWD reflow table switch for mobile UI/UX by collapsing columns", 4 | "version": "1.0.3", 5 | "keywords": [ 6 | "RWD", 7 | "responsive", 8 | "table", 9 | "switch-table", 10 | "table-reflow", 11 | "mobile-ui", 12 | "datatables" 13 | ], 14 | "homepage": "https://github.com/yidas/jquery-reflow-table", 15 | "authors": [ 16 | "Nick Tsai " 17 | ], 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/yidas/jquery-reflow-table.git" 21 | }, 22 | "bugs": { 23 | "url": "https://github.com/yidas/jquery-reflow-table/issues" 24 | }, 25 | "license": { 26 | "type": "MIT", 27 | "url": "https://github.com/yidas/js-reflow-table/blob/master/LICENSE" 28 | }, 29 | "main": "dist/js/reflow-table.js" 30 | } 31 | -------------------------------------------------------------------------------- /src/css/reflow-table.css: -------------------------------------------------------------------------------- 1 | /** 2 | * yidas/jquery-reflow-table CSS style-sheet 3 | * 4 | * @author Nick Tsai 5 | * @since 1.0.3 6 | * @see https://github.com/yidas/jquery-reflow-table 7 | */ 8 | 9 | /* Mobile Mode: table */ 10 | table.reflow-table.reflow-table-mobile-mode { 11 | width: 100% !important; 12 | } 13 | 14 | /* Mobile Mode: Hide thead & tfoot */ 15 | .reflow-table.reflow-table-mobile-mode thead, .reflow-table.reflow-table-mobile-mode tfoot { 16 | display: none; 17 | } 18 | 19 | /* Mobile Mode: Mobile mode for td */ 20 | .reflow-table.reflow-table-mobile-mode td { 21 | /* overflow: hidden; */ 22 | width: 100%; 23 | display: block; 24 | } 25 | 26 | /* Mobile Mode: Dynamically fulfilling pseudo content */ 27 | .reflow-table.reflow-table-mobile-mode td:before { 28 | content: attr(data-th); 29 | } 30 | 31 | /* Pseudo pre-style */ 32 | .reflow-table td:before { 33 | display: inline-block; 34 | font-weight: bold; 35 | } 36 | 37 | /* Width Ratio style-sheet */ 38 | .reflow-table.reflow-table-w-15 td:before { 39 | min-width: 15%; 40 | } 41 | .reflow-table.reflow-table-w-20 td:before { 42 | min-width: 20%; 43 | } 44 | .reflow-table.reflow-table-w-25 td:before { 45 | min-width: 25%; 46 | } 47 | .reflow-table.reflow-table-w-30 td:before { 48 | min-width: 30%; 49 | } 50 | .reflow-table.reflow-table-w-40 td:before { 51 | min-width: 40%; 52 | } 53 | .reflow-table.reflow-table-w-50 td:before { 54 | min-width: 50%; 55 | } 56 | 57 | /* Width Size style-sheet */ 58 | .reflow-table.reflow-table-xs td:before { 59 | min-width: 100px; 60 | } 61 | .reflow-table.reflow-table-sm td:before { 62 | min-width: 150px; 63 | } 64 | .reflow-table.reflow-table-md td:before { 65 | min-width: 200px; 66 | } 67 | .reflow-table.reflow-table-lg td:before { 68 | min-width: 250px; 69 | } 70 | 71 | /** 72 | * DataTables library integration 73 | */ 74 | /* Fix DataTables Bootstrap 4 border problem for Mobile mode */ 75 | table.dataTable.reflow-table.reflow-table-mobile-mode td, table.dataTable.reflow-table.reflow-table-mobile-mode th { 76 | border-right: none; 77 | box-sizing: border-box; 78 | } -------------------------------------------------------------------------------- /src/js/reflow-table.js: -------------------------------------------------------------------------------- 1 | /** 2 | * RWD reflow table switch for mobile UI/UX by collapsing columns 3 | * 4 | * @author Nick Tsai 5 | * @version 1.0.0 6 | * @see https://github.com/yidas/jquery-reflow-table 7 | */ 8 | (function ($, window) { 9 | 10 | 'use strict'; 11 | 12 | // Counter for this library 13 | var count = 0; 14 | 15 | /** 16 | * Main object 17 | * 18 | * @param {element} element 19 | * @param {object} options 20 | */ 21 | var ReflowTable = function(element, options) { 22 | 23 | // Target element initialization 24 | this.$table = $(element).first(); 25 | 26 | // Options 27 | this.options = options || {}; 28 | this.namespace; 29 | this.thead; 30 | this.autoWidth; 31 | this.widthRatio; 32 | this.widthSize; 33 | 34 | // Static properties 35 | this.className = 'reflow-table'; 36 | this.mobileModeClassName = 'reflow-table-mobile-mode'; 37 | this.titleAttr = 'data-th'; 38 | this.widthRatioClassName; 39 | this.widthWidthClassName; 40 | this.eventMobileOn = 'reflow-table.mobile.on'; 41 | this.eventMobileOff = 'reflow-table.mobile.off'; 42 | 43 | this.init(); 44 | 45 | return this; 46 | } 47 | 48 | /** 49 | * Initialization 50 | */ 51 | ReflowTable.prototype.init = function() { 52 | 53 | /** 54 | * Generate a namespace for the element 55 | */ 56 | var getNamespace = (function () { 57 | 58 | var ns; 59 | 60 | // First, try to get existent namespace if is init before 61 | var options = this.$table.data(this.className) || {}; 62 | ns = options.namespace || null; 63 | // Second, try to get element ID 64 | ns = ns || this.$table.attr('id'); 65 | // At least, enable unique ID generator 66 | if (!ns) { 67 | 68 | count += 1; 69 | ns = this.className + '-' + count; 70 | } 71 | 72 | return ns; 73 | 74 | }).bind(this); 75 | 76 | /** 77 | * Initialize and save options 78 | */ 79 | var setOptions = (function() { 80 | 81 | var options = this.options; 82 | this.namespace = this.options.namespace || getNamespace(); 83 | this.thead = this.options.thead || this.$table.find("thead"); 84 | this.autoWidth = (typeof options.autoWidth !== 'undefined') ? options.autoWidth : 736; 85 | this.widthRatio = (typeof options.widthRatio !== 'undefined') ? options.widthRatio : '50'; 86 | this.widthSize = (typeof options.widthSize !== 'undefined') ? options.widthSize : false; 87 | // Static properties 88 | this.widthRatioClassName = this.className + '-w-' + this.widthRatio; 89 | this.widthWidthClassName= this.className + '-' + this.widthSize; 90 | 91 | // Save options 92 | this.$table.data(this.className, this.options); 93 | 94 | }).bind(this); 95 | 96 | /** 97 | * Restore options 98 | */ 99 | var restoreOptions = (function() { 100 | 101 | this.options = this.$table.data(this.className) || {}; 102 | // Reset and save options 103 | setOptions(); 104 | 105 | return this.options; 106 | 107 | }).bind(this); 108 | 109 | // Element check 110 | if (!this.$table.length) 111 | throw "No element selected"; 112 | if (!this.$table.is("table")) 113 | throw "The element must be a table dom"; 114 | 115 | /** 116 | * Update Mode 117 | */ 118 | if (this.options==='update') { 119 | 120 | restoreOptions(); 121 | // Life cycle for update only, there is no performance difference for deleting self object so just keeping update object. 122 | return this.update(); 123 | } 124 | 125 | /** 126 | * Options Setting 127 | */ 128 | setOptions(); 129 | 130 | // Destroy the table element before initializing 131 | this.destroy(); 132 | 133 | // Establish every th text for mobile mode 134 | this.build(); 135 | 136 | // 137 | if (typeof this.options.eventMobileOn === 'function') { 138 | this.$table.on(this.eventMobileOn, this.options.eventMobileOn); 139 | } 140 | if (typeof this.options.eventMobileOff === 'function') { 141 | this.$table.on(this.eventMobileOff, this.options.eventMobileOff); 142 | } 143 | 144 | // Add class for Table Reflow 145 | this.$table.addClass(this.className); 146 | // Width Ratio setting 147 | if (this.widthRatio) { 148 | this.$table.addClass(this.widthRatioClassName); 149 | } 150 | // Width Size setting 151 | if (this.widthSize) { 152 | this.$table.addClass(this.widthWidthClassName); 153 | } 154 | // AutoWidth setting 155 | if (this.autoWidth) { 156 | 157 | var that = this; 158 | 159 | /** 160 | * Listener - Window resize detection for Table Reflow 161 | * 162 | * To support mode switch, this library doesn't use CSS @media to implement detection. 163 | */ 164 | $(window).on('resize.'+that.namespace, function() { 165 | 166 | // Detect mode 167 | if ($(window).width() <= that.autoWidth) { 168 | // Mobile mode 169 | that.mobileMode(true); 170 | } 171 | else { 172 | // Non-Mobile mode 173 | that.mobileMode(false); 174 | } 175 | }); 176 | 177 | // Trigger at the start 178 | $(window).trigger('resize.'+this.namespace); 179 | } 180 | } 181 | 182 | /** 183 | * Establishing th title to prepared td DOMs for mobile mode 184 | * 185 | * @return {self} 186 | */ 187 | ReflowTable.prototype.build = function() { 188 | 189 | var $table = this.$table; 190 | var titleAttr = this.titleAttr; 191 | 192 | // every th text to td data for mobile mode 193 | $(this.thead).find("th").each(function(key, thRow) { 194 | 195 | $table.find("tbody tr td:nth-child("+(key+1)+")") 196 | .attr(titleAttr, $(thRow).text()); 197 | }); 198 | 199 | return this; 200 | } 201 | 202 | /** 203 | * Switch table to mobile mode or not 204 | * 205 | * @param {boolean} enable Enable mobile mode or not, default is true 206 | * @return {self} 207 | */ 208 | ReflowTable.prototype.mobileMode = function(enable) { 209 | 210 | enable = (typeof enable !== 'undefined') ? enable : true; 211 | var isMobileMode = this.isMobileMode(); 212 | 213 | // Detect enabled mode 214 | if (enable) { 215 | // Mobile mode 216 | this.$table.addClass(this.mobileModeClassName); 217 | // Event trigger of first mobile on 218 | if (!isMobileMode) 219 | this.$table.trigger($.Event(this.eventMobileOn)); 220 | } 221 | else { 222 | // non-Mobile mode 223 | this.$table.removeClass(this.mobileModeClassName); 224 | // Event trigger of first mobile off 225 | if (isMobileMode) 226 | this.$table.trigger($.Event(this.eventMobileOff)); 227 | } 228 | 229 | return this; 230 | } 231 | 232 | /** 233 | * Switch table between mobile mode or original mode 234 | * 235 | * @return {self} 236 | */ 237 | ReflowTable.prototype.switchMode = function() { 238 | 239 | // Detect the reflow table is in mobile mode or not 240 | if (this.isMobileMode()) 241 | this.mobileMode(false); 242 | else 243 | this.mobileMode(true); 244 | 245 | return this; 246 | } 247 | 248 | /** 249 | * Check is in mobile mode 250 | * 251 | * @return {boolean} 252 | */ 253 | ReflowTable.prototype.isMobileMode = function() { 254 | 255 | return (this.$table.hasClass(this.mobileModeClassName)) ? true : false; 256 | } 257 | 258 | /** 259 | * Update or re-build each Reflow Table row th title for dynamic table content 260 | * 261 | * @return {self} 262 | */ 263 | ReflowTable.prototype.update = function() { 264 | 265 | // Re-build only without any options 266 | this.build(); 267 | return this; 268 | } 269 | 270 | /** 271 | * Unbind all events by same namespace 272 | * 273 | * @return {self} 274 | */ 275 | ReflowTable.prototype.unbind = function() { 276 | 277 | // Unbind resize listener 278 | $(window).off('resize.'+this.namespace); 279 | 280 | // Unbind all events of the table 281 | this.$table.off(); 282 | 283 | return this; 284 | } 285 | 286 | /** 287 | * Destroy Table Reflow by same namespace 288 | */ 289 | ReflowTable.prototype.destroy = function() { 290 | 291 | this.unbind(); 292 | // Switch mode back 293 | this.mobileMode(false); 294 | // Remove class for Table Reflow 295 | this.$table.removeClass(this.className); 296 | // Remove Width Ratio setting 297 | if (this.widthRatio) { 298 | this.$table.removeClass(this.widthRatioClassName); 299 | } 300 | // Remove Width Size setting 301 | if (this.widthSize) { 302 | this.$table.removeClass(this.widthWidthClassName); 303 | } 304 | } 305 | 306 | /** 307 | * Interface 308 | */ 309 | // Class for single element 310 | window.ReflowTable = ReflowTable; 311 | // jQuery interface 312 | $.fn.reflowTable = function (options) { 313 | 314 | // Single/Multiple mode 315 | if (this.length === 1) { 316 | 317 | return new ReflowTable(this, options) 318 | } 319 | else if (this.length > 1) { 320 | 321 | var result = []; 322 | // Multiple elements bundle 323 | this.each(function () { 324 | result.push(new ReflowTable(this, options)); 325 | }); 326 | 327 | return result; 328 | } 329 | 330 | return false; 331 | } 332 | 333 | })(jQuery, window); --------------------------------------------------------------------------------