1/** 2 * responsive-table.js 3 * 4 * Responsive Table is a tasty JavaScript library which helps format data tables on small 5 screens! It does so by reordering items in the DOM while keeping HTML markup 6 valid and assistive technology friendly! 7 * 8 * Repository URL: https://github.com/svinkle/responsive-table 9 * 10 * @author: Scott Vinkle <[email protected]> 11 * @version: 0.4.1 12 */ 13var ResponsiveTable = (function(window, document, undefined) { 14 'use strict'; 15 16 // Unique table identifier. 17 var tableId = 0; 18 19 // Lookup table of selectors, either id or element, no classes. 20 var selectors = { 21 tableList: 'table-list', 22 selected: 'selected-list-item', 23 tableRows: 'tbody tr', 24 tableCells: 'tbody td', 25 tableHeading: 'table-heading', 26 windowLink: 'window-link', 27 columnHeaders: 'thead th[scope="col"]', 28 rowHeaders: 'tbody th[scope="row"]', 29 caption: 'caption', 30 h2: 'h2', 31 links: 'a', 32 span: 'span', 33 styleSheets: 'link[rel="stylesheet"]', 34 tableRowFocusElement: 'tableRowFocusElement' 35 }; 36 37 // Lookup table of classes, to apply styles only. 38 var classes = { 39 table: 'responsive-table', 40 caption: 'responsive-table__caption', 41 tableList: 'responsive-table__list', 42 tableListItem: 'responsive-table__list-item', 43 tableListLink: 'responsive-table__list-link', 44 windowLink: 'responsive-table__window-link', 45 windowLinkActive: 'responsive-table__window-link--active', 46 windowLinkIcon: 'responsive-table__window-link-icon', 47 windowBody: 'responsive-table__window-body', 48 tableRowActive: 'responsive-table__row--active', 49 tableSmallScreen: 'responsive-table--small-screen', 50 headerContent: 'responsive-table__header-content', 51 cellContent: 'responsive-table__cell-content', 52 hidden: 'hidden', 53 visuallyHidden: 'visuallyhidden' 54 }; 55 56 // Lookup table of common, unique strings. 57 var strings = { 58 tableMissing: 'Table selector missing from object.', 59 tableNotFound: 'Table not found: ', 60 tableTypeMissing: 61 'No type defined. Must be "list", "stack", or "window" for table: ', 62 windowLinkStart: 'Open ', 63 windowLinkEnd: ' table in a new window', 64 windowIcon: 65 '<svg class="' + 66 classes.windowLinkIcon + 67 '" width="32" height="32" viewBox="0 0 32 32"><g transform="scale(0.03125 0.03125)"><path d="M192 64v768h768v-768h-768zM896 768h-640v-640h640v640zM128 896v-672l-64-64v800h800l-64-64h-672z"></path><path d="M352 256l160 160-192 192 96 96 192-192 160 160v-416z"></path></g></svg>', 68 checkDom: 'Check document structure requirements: ', 69 cellCount: 'Column/row cell count mismatch: ', 70 selected: ', selected', 71 headerContent: 'header-content-', 72 cellContent: 'cell-content-', 73 auto: 'auto', 74 column: 'column', 75 row: 'row', 76 show: 'show', 77 hide: 'hide', 78 large: 'large', 79 small: 'small', 80 list: 'list', 81 stack: 'stack', 82 window: 'window', 83 dataListLink: 'data-list-link', 84 pageSettings: { 85 lang: 'en', 86 charset: '<meta charset="UTF-8">', 87 httpEquiv: 88 '<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">', 89 viewport: 90 '<meta name="viewport" content="width=device-width, initial-scale=1.0">' 91 } 92 }; 93 94 // Class constructor. 95 var ResponsiveTable = function(table, type, breakPoint) { 96 var thisTable = null, 97 thisTableId = null, 98 tableList = null, 99 mediaQuery = null; 100 101 // Check to see if the `table` param is present. 102 // Then check to see if it exists in the DOM. 103 // If all good, assign the element to `thisTable`. 104 if (!table) { 105 console.error(strings.tableMissing); 106 return; 107 } else if (!document.querySelector(table)) { 108 console.error(strings.tableNotFound + table); 109 return; 110 } else { 111 thisTable = document.querySelector(table); 112 } 113 114 // Check to see if the `type` param is present. 115 // If all good, assign the value to `tableType`. 116 if (!type) { 117 console.error(strings.tableTypeMissing + table); 118 return; 119 } 120 121 /** 122 * Gets the current `table` element. 123 * 124 * @return thisTable {Object} the `table` element. 125 */ 126 this.getTable = function() { 127 return thisTable; 128 }; 129 130 /** 131 * Sets a unique identifier for the current `table`. 132 */ 133 this.setTableId = function() { 134 thisTableId = tableId; 135 }; 136 137 /** 138 * Gets the unique identifier value of the current table. 139 * 140 * @return thisTableId {number} index of the current `table`. 141 */ 142 this.getTableId = function() { 143 return thisTableId; 144 }; 145 146 /** 147 * Gets the current table media query object to test against. 148 * 149 * @return mediaQuery {Object} `matchMedia` window object. 150 */ 151 this.getMediaQuery = function() { 152 return mediaQuery; 153 }; 154 155 /**
156 * For `list` type tables, sets the current list `ul` element. 157 */ 158 this.setTableList = function(list) { 159 tableList = list; 160 }; 161 162 /** 163 * Gets the current list `ul` element. 164 * 165 * @return tableList {Object} the `ul` element. 166 */ 167 this.getTableList = function() { 168 return tableList; 169 }; 170 171 /** 172 * Gets the current table type. 173 * 174 * @return type {String} the value of the desired table type. 175 */ 176 this.getTableType = function() { 177 return type; 178 }; 179 180 /** 181 * Gets the current `table` selector. 182 * 183 * @return table {String} the `id` attribute from the current `table`. 184 */ 185 this.getTableSelector = function() { 186 return table; 187 }; 188 189 // Add classes to the table, etc, for styling puposes only. 190 thisTable.classList.add(classes.table); 191 thisTable.classList.add(classes.table + '--' + type); 192 thisTable.querySelector(selectors.caption).classList.add(classes.caption); 193 194 // Set the unique identifier for the current table, then increment for 195 // the next `table`. 196 this.setTableId(); 197 tableId++; 198 199 // Call `checkStructure()` method to make sure everything's in the DOM 200 // before continuing on. 201 if (this.checkStructure()) { 202 203 // Create the `matchMedia` object to test the desired `breakpoint` 204 // value against. 205 mediaQuery = window.matchMedia( 206 '(min-width: ' + 207 (!breakPoint 208 ? window.getComputedStyle(thisTable, null).width 209 : breakPoint) + 210 ')' 211 ); 212 213 // Add the event listener to the `matchMedia` object, listen 214 // for any window resizing or device orientation changes. 215 mediaQuery.addListener( 216 ResponsiveTable.prototype.testMediaQuery.bind(this) 217 ); 218 219 // Test the media query on page load to adjust the `table` if 220 // required. 221 this.testMediaQuery(); 222 } 223 }; 224 225 /** 226 * Checks the `table` elements and structure to make sure everything 227 * is in place. If something is missing, an error message is displayed 228 * in the `console` window. 229 * 230 * @method checkStructure 231 * @return {Boolean} simply return `true` or `false`. 232 */ 233 ResponsiveTable.prototype.checkStructure = function() { 234 var thisTable = this.getTable(), 235 thisTableType = this.getTableType(), 236 columnHeaders = thisTable.querySelectorAll(selectors.columnHeaders), 237 columnHeadersLength = columnHeaders.length, 238 tableRows = thisTable.querySelectorAll(selectors.tableRows),
239 tableRowsLength = tableRows.length, 240 structureError = false, 241 errorMessage = '', 242 i = 0; 243 244 // The `list` style requires `scope="col"` and `scope="row"` headers. 245 // The `stack` or `window` styles only require `scope="col"` headers. 246 // A `caption` is also required to generate the heading. 247 if (thisTableType === strings.list) { 248 if ( 249 !thisTable.querySelector(selectors.columnHeaders) || 250 !thisTable.querySelector(selectors.rowHeaders) || 251 !thisTable.querySelector(selectors.caption) 252 ) { 253 structureError = true; 254 errorMessage = strings.checkDom; 255 } else { 256 257 // Check to make sure each row cell count matches the total 258 // column count. Otherwise, `list` tables will output 259 // mismatched data. 260 for (; i < tableRowsLength; i++) { 261 if (tableRows[i].children.length !== columnHeadersLength) { 262 structureError = true; 263 errorMessage = 264 strings.cellCount + 265 '"' + 266 tableRows[i].children[0].textContent + 267 '": '; 268 } 269 } 270 } 271 } else if ( 272 thisTableType === strings.stack || 273 thisTableType === strings.window 274 ) { 275 if ( 276 !thisTable.querySelector(selectors.columnHeaders) || 277 !thisTable.querySelector(selectors.caption) 278 ) { 279 structureError = true; 280 errorMessage = strings.checkDom; 281 } 282 } 283 284 // If anything is missing, send an error message to the `console` window 285 // and stop anything else from happening. 286 if (structureError) { 287 console.error(errorMessage + this.getTableSelector()); 288 return false; 289 } else { 290 return true; 291 } 292 }; 293 294 /** 295 * Check and see if the desired breakpoint has been matched, and setup 296 * the `table` accordingly for the desired style. 297 * 298 * @method testMediaQuery 299 */ 300 ResponsiveTable.prototype.testMediaQuery = function() { 301 if (this.getMediaQuery().matches) { 302 this.setupScreen(strings.large); 303 } else { 304 this.setupScreen(strings.small); 305 } 306 }; 307 308 /** 309 * Call the appropriate methods to re-arrage the `table` based on the 310 * current screen "size" and selected `table` theme. 311 * 312 * @method setupScreen 313 * @param size {String} the "size" of the current screen. 314 */ 315 ResponsiveTable.prototype.setupScreen = function(size) { 316 var thisTable = this.getTable(), 317 thisTableType = this.getTableType(), 318 tableContainer = thisTable.parentNode, 319 caption = thisTable.querySelector(selectors.caption); 320 321 // The `list` style needs to show/hide each row, and display an 322 // "active" row. After, the link list is generated to click betweeen 323 // `table` rows. 324 if (thisTableType === strings.list) { 325 if (size === strings.small) { 326 this.toggleActiveRow(strings.hide); 327 this.generateList(); 328 } else { 329 this.toggleActiveRow(strings.show); 330 this.removeList(); 331 } 332 } 333 334 // The `list` and `stack` styles need to show/hide the header elements 335 // as well as move content around to meet their theme requirements. 336 // The `tableSmallScreen` CSS class is added for styling purposes. 337 if (thisTableType === strings.list || thisTableType === strings.stack) { 338 if (size === strings.small) { 339 thisTable.classList.add(classes.tableSmallScreen); 340 this.toggleHeaders(strings.hide); 341 this.moveContent(); 342 } else { 343 thisTable.classList.remove(classes.tableSmallScreen); 344 this.toggleHeaders(strings.show); 345 this.resetContent(); 346 } 347 } 348 349 // The `window` types need to show/hide the link which opens the `table` 350 // in its own window. 351 if (thisTableType === strings.window) { 352 353 // Need to add a heading before table so open link has context 354 if (!tableContainer.querySelector('h2')) { 355 var tableHeading = document.createElement('h2'); 356 tableHeading.id = selectors.tableHeading + '-' + tableId; 357 tableHeading.textContent = caption.textContent; 358 tableContainer.insertBefore(tableHeading, tableContainer.firstChild); 359 caption.classList.add(classes.visuallyHidden); 360 } 361 362 this.displayWindowLink(size === 'small' ? true : false); 363 } 364 }; 365 366 /** 367 * Called to show/hide `table` rows for `list` style tables. 368 * 369 * @method toggleActiveRow
370 * @param toggle {String} determines the display of all or one row 371 */ 372 ResponsiveTable.prototype.toggleActiveRow = function(toggle) { 373 var tableRows = this.getTable().querySelectorAll(selectors.tableRows), 374 tableRowsLength = tableRows.length, 375 i = 0; 376 377 // Hide or show all rows, depending on the `toggle` value. 378 for (; i < tableRowsLength; i++) { 379 if (toggle === strings.hide) { 380 tableRows[i].classList.add(classes.hidden); 381 } else { 382 tableRows[i].classList.remove(classes.hidden); 383 } 384 } 385 386 // On initial load, when hiding rows, only show the first row and mark 387 // it as `active`. Otherwise, show all rows. 388 if (toggle === strings.hide) { 389 tableRows[0].classList.remove(classes.hidden); 390 tableRows[0].classList.add(classes.tableRowActive); 391 } else { 392 tableRows[0].classList.remove(classes.tableRowActive); 393 } 394 }; 395 396 /** 397 * Generate the link list for `list` themed tables. Links are used to 398 * show the associated row's content. 399 * 400 * @method generateList 401 */ 402 ResponsiveTable.prototype.generateList = function() { 403 var thisTable = this.getTable(), 404 tableList = this.getTableList(), 405 rowHeaders = thisTable.querySelectorAll(selectors.rowHeaders), 406 rowHeadersLength = rowHeaders.length, 407 newSpanWrapper = null, 408 rowHeader = null, 409 list = null, 410 listItem = null, 411 listLink = null, 412 i = 0, 413 j = 0; 414 415 // If the link list already exists in the DOM, show it. Otherwise, 416 // create a new link list. 417 if (tableList) { 418 tableList.classList.remove(classes.hidden); 419 } else { 420 421 // Create the link list element and set attributes 422 list = document.createElement('ul'); 423 424 list.id = selectors.tableList; 425 list.classList.add(classes.tableList); 426 427 // Create links using the `scope="row"` header text, then add the 428 // link and list item to the `ul` list element. 429 for (; i < rowHeadersLength; i++) { 430 listItem = document.createElement('li'); 431 listLink = document.createElement('a'); 432 rowHeader = rowHeaders[i]; 433 j = 0; 434 435 listItem.classList.add(classes.tableListItem); 436 437 listLink.href = '#' + selectors.tableRowFocusElement + i; 438 listLink.textContent = rowHeader.textContent; 439 listLink.classList.add(classes.tableListLink); 440 listLink.setAttribute(strings.dataListLink, i); 441 listLink.addEventListener( 442 'click', 443 ResponsiveTable.prototype.listLinkClick.bind(this), 444 false 445 ); 446 447 listItem.appendChild(listLink); 448 list.appendChild(listItem); 449 450 // wrap header elements in a new span element that we can later give focus to. 451 // create new span element 452 newSpanWrapper = document.createElement('span'); 453 newSpanWrapper.id = selectors.tableRowFocusElement + i; 454 455 // Move all header nodes into the new span 456 for (; j < rowHeader.childNodes.length; j++) { 457 newSpanWrapper.appendChild(rowHeader.childNodes[0]); 458 } 459 460 // Add span back into header 461 rowHeader.insertBefore(newSpanWrapper, rowHeader.firstChild); 462 } 463 464 // Add the `ul` element to the DOM, directly before the `table`. 465 thisTable.parentNode.insertBefore(list, thisTable); 466 467 // Set this list element as the link list for the current `table`. 468 this.setTableList(list); 469 } 470 }; 471 472 /** 473 * Hide the link list element for `list` themed tables from the DOM. 474 * 475 * @method removeList 476 */ 477 ResponsiveTable.prototype.removeList = function() { 478 var tableList = this.getTableList(); 479 480 // If the link list doesn't exist in the DOM, don't continue. 481 if (!tableList) { 482 return; 483 } 484 485 // Hide the link list. 486 tableList.classList.add(classes.hidden); 487 }; 488 489 /** 490 * Show or hide `table` headers per the desired theme. 491 * 492 * @method toggleHeaders
493 * @param toggle {String} determines the display of `table` headers. 494 */ 495 ResponsiveTable.prototype.toggleHeaders = function(toggle) { 496 var columnHeaders = this.getTable().querySelectorAll( 497 selectors.columnHeaders 498 ), 499 columnHeadersLength = columnHeaders.length, 500 thisHeader = null, 501 headerRow = null, 502 i = 0; 503 504 // Show/hide each `scope="col"` header and its parent `tr` element. 505 for (; i < columnHeadersLength; i++) { 506 thisHeader = columnHeaders[i]; 507 headerRow = thisHeader.parentNode; 508 509 if (toggle === strings.hide) { 510 thisHeader.classList.add(classes.hidden); 511 headerRow.classList.add(classes.hidden); 512 } else { 513 thisHeader.classList.remove(classes.hidden); 514 headerRow.classList.remove(classes.hidden); 515 } 516 } 517 }; 518 519 /** 520 * Move content around the DOM for "small" screens in order to meet the 521 * theme requirements. 522 * 523 * @method moveContent 524 */ 525 ResponsiveTable.prototype.moveContent = function() { 526 var thisTable = this.getTable(), 527 tableId = this.getTableId(), 528 tableContainer = thisTable.parentNode, 529 tableCells = thisTable.querySelectorAll(selectors.tableCells), 530 tableCellsLength = tableCells.length, 531 colunmHeaders = thisTable.querySelectorAll(selectors.columnHeaders), 532 colunmHeadersLength = colunmHeaders.length - 1, 533 hasRowHeaders = thisTable.querySelectorAll(selectors.rowHeaders).length, 534 caption = thisTable.querySelector(selectors.caption), 535 tableHeading = null, 536 tableHeadingContent = null, 537 cellContent = null, 538 thisCell = null, 539 i = 0, 540 j = hasRowHeaders ? 1 : 0; 541 542 for (; i < tableCellsLength; i++) { 543 thisCell = tableCells[i]; 544 545 // Get the current `scope="col"` element text to be used within the 546 // cell content template, taking in to consideration that some 547 // headers may be empty cells. 548 if (colunmHeaders[j] !== undefined) { 549 tableHeadingContent = colunmHeaders[j].textContent; 550 } 551 552 // Make a backup copy of the current cell content. 553 cellContent = thisCell.innerHTML; 554 555 // Clear the cell content and insert the header text along side the 556 // current cell content, per theme requirements. 557 thisCell.innerHTML = ''; 558 thisCell.innerHTML = 559 '<span id="' + 560 strings.headerContent + 561 i + 562 '" class="' + 563 classes.headerContent + 564 '">' + 565 tableHeadingContent + 566 '</span>: <span id="' + 567 strings.cellContent + 568 i + 569 '" class="' + 570 classes.cellContent + 571 '">' + 572 cellContent + 573 '</span>'; 574 575 // Increment the counter to grab the current cell's `scope="col"` 576 // element content. Reset the counter when we've reached the 577 // end of the row to grab the first column content and start again. 578 j = j >= colunmHeadersLength ? (j = hasRowHeaders ? 1 : 0) : j + 1; 579 } 580 581 // If the `table` heading element exists, show it. Otherwise, create 582 // a new heading element and insert it before the `table`. Use the 583 // `caption` element text as the heading text. 584 // (The purpose for the heading is mostly used to get around a 585 // VoiceOver bug, where VO doesn't move in to a `table` when focused.) 586 if ( 587 tableContainer.querySelector('#' + selectors.tableHeading + '-' + tableId) 588 ) { 589 tableContainer 590 .querySelector('#' + selectors.tableHeading + '-' + tableId) 591 .classList.remove(classes.hidden); 592 } else { 593 tableHeading = document.createElement('h2'); 594 595 tableHeading.id = selectors.tableHeading + '-' + tableId; 596 tableHeading.textContent = caption.textContent; 597 598 tableContainer.insertBefore(tableHeading, tableContainer.firstChild); 599 } 600 601 // Hide the caption since the heading exists now. 602 caption.classList.add(classes.visuallyHidden); 603 }; 604 605 /** 606 * Reset the content back to its original, full-width `table` formatting. 607 * 608 * @method resetContent 609 */ 610 ResponsiveTable.prototype.resetContent = function() { 611 var thisTable = this.getTable(), 612 tableId = this.getTableId(), 613 caption = thisTable.querySelector(selectors.caption), 614 tableContainer = thisTable.parentNode, 615 tableCells = thisTable.querySelectorAll(selectors.tableCells), 616 tableCellsLength = tableCells.length, 617 thisCell = null, 618 thisCellContent = '', 619 i = 0; 620 621 for (; i < tableCellsLength; i++) { 622 thisCell = tableCells[i]; 623 624 // Make sure the content we're looking to place back actually exists. 625 if (thisCell.querySelector('#' + strings.headerContent + i) !== null) { 626 627 // Make a backup copy of the current cell content, minus the 628 // header content. 629 thisCellContent = thisCell.querySelector('#' + strings.cellContent + i) 630 .innerHTML; 631 632 // Remove the header and cell content elements. 633 thisCell.querySelector('#' + strings.headerContent + i).remove(); 634 thisCell.querySelector('#' + strings.cellContent + i).remove(); 635 636 // Place the cell content back in to the cell. 637 thisCell.innerHTML = thisCellContent; 638 } 639 } 640 641 // If it exists in the DOM, hide the `table` heading. 642 if ( 643 tableContainer.querySelector('#' + selectors.tableHeading + '-' + tableId) 644 ) { 645 tableContainer 646 .querySelector('#' + selectors.tableHeading + '-' + tableId) 647 .classList.add(classes.hidden); 648 } 649 650 // Show the caption. 651 caption.classList.remove(classes.visuallyHidden); 652 }; 653 654 /** 655 * Click event for each `list` themed list link item. 656 * 657 * @method listLinkClick 658 * @param event {Object} click event information object. 659 */ 660 ResponsiveTable.prototype.listLinkClick = function(event) { 661 event.preventDefault(); 662 663 var thisTable = this.getTable(),
664 thisTableList = this.getTableList(), 665 thisLink = event.target, 666 listLinks = thisTableList.querySelectorAll(selectors.links), 667 listLinksLength = listLinks.length, 668 tableRows = thisTable.querySelectorAll(selectors.tableRows), 669 tableRowsLength = tableRows.length, 670 thisTableRow = parseInt( 671 event.target.getAttribute(strings.dataListLink), 672 10 673 ), 674 selectedText = 675 '<span class="' + 676 classes.visuallyHidden + 677 '">' + 678 strings.selected + 679 '</span>', 680 i = 0, 681 j = 0; 682 683 // Remove the `active` class and hide each row. 684 // Show the `table` row which index matches the clicked link's 685 // `data-list-link` value. 686 for (; i < tableRowsLength; i++) { 687 tableRows[i].classList.remove(classes.tableRowActive); 688 tableRows[i].classList.add(classes.hidden); 689 690 if (i === thisTableRow) { 691 tableRows[i].classList.remove(classes.hidden); 692 tableRows[i].classList.add(classes.tableRowActive); 693 } 694 } 695 696 // Remove the "selected" hidden text from each link. 697 for (; j < listLinksLength; j++) { 698 if (listLinks[j].querySelector(selectors.span)) { 699 listLinks[j].querySelector(selectors.span).remove(); 700 } 701 } 702 703 // Apply the text to the clicked link element. 704 thisLink.innerHTML = thisLink.textContent + selectedText; 705 706 // Get link href #value to know where to set focus 707 var visibleRowHeaderTarget = thisTable.querySelector( 708 '#' + thisLink.hash.substr(1) 709 ); 710 711 // Set the `table` heading to be focusable, then set focus. 712 visibleRowHeaderTarget.setAttribute('tabindex', -1); 713 visibleRowHeaderTarget.focus(); 714 }; 715 716 /** 717 * Click event for the `window` themed "open window" link. 718 * 719 * @method windowLinkClick 720 * @param event {Object} click event information object. 721 */ 722 ResponsiveTable.prototype.windowLinkClick = function(event) { 723 event.preventDefault(); 724 725 var thisTable = this.getTable(), 726 tableWindow = window.open(), 727 tableWindowDocument = tableWindow.document, 728 tableContent = thisTable.outerHTML, 729 styleSheets = document.head.querySelectorAll(selectors.styleSheets), 730 styleSheetsLength = styleSheets.length, 731 tableWindowStyleSheets = null, 732 i = 0; 733 734 // Set the language of the new window document. 735 tableWindowDocument.documentElement.lang = strings.lang; 736 737 // Set the character set, httpEquiv value, and viewport meta tags of the 738 // new window document. 739 tableWindowDocument.head.innerHTML = 740 strings.pageSettings.charset + 741 strings.pageSettings.httpEquiv + 742 strings.pageSettings.viewport; 743 744 // Set the `title` element of the new window document to that of the 745 // current `caption` value. 746 tableWindowDocument.title = thisTable.querySelector( 747 selectors.caption 748 ).textContent; 749 750 // Dynamically create `link` elements in order to embed the current 751 // page's stylesheets, and append to the `head` element of the new 752 // window document. 753 for (; i < styleSheetsLength; i++) { 754 tableWindowStyleSheets = tableWindowDocument.createElement('link'); 755 756 tableWindowStyleSheets.rel = 'stylesheet'; 757 tableWindowStyleSheets.href = styleSheets[i].href; 758 759 tableWindowDocument.head.appendChild(tableWindowStyleSheets); 760 } 761 762 // Set the entire `body` element content of the new window document to 763 // that of the current `table` element content. 764 tableWindowDocument.body.innerHTML = tableContent; 765 tableWindowDocument.body.classList.add(classes.windowBody); 766 767 // Remove visuallyhidden class on table caption 768 var newWindowCaption = tableWindowDocument.querySelector(selectors.caption); 769 newWindowCaption.classList.remove(classes.visuallyHidden); 770 }; 771 772 /** 773 * Show/hide the `window` theme "open window" link. 774 * 775 * @method displayWindowLink 776 * @param display {Boolean} determines display mode of the "open window" link 777 */ 778 ResponsiveTable.prototype.displayWindowLink = function(display) { 779 var thisTable = this.getTable(), 780 tableHeader = thisTable.parentNode.querySelector(selectors.h2), 781 windowLink = document.querySelector('#' + selectors.windowLink); 782 783 // On display, create the link and set its parameters and event 784 // listener, the insert before the `table`. Otherwise, remove the link 785 // from the DOM. 786 if (display) { 787 windowLink = document.createElement('a'); 788 789 windowLink.id = selectors.windowLink; 790 windowLink.href = '#'; 791 windowLink.classList.add(classes.windowLink); 792 windowLink.innerHTML = 793 '<span class="' + 794 classes.visuallyHidden + 795 '">' +
796 strings.windowLinkStart + 797 tableHeader.textContent + 798 strings.windowLinkEnd + 799 '</span>' + 800 strings.windowIcon; 801 windowLink.addEventListener( 802 'click', 803 ResponsiveTable.prototype.windowLinkClick.bind(this), 804 false 805 ); 806 807 thisTable.parentNode.insertBefore(windowLink, tableHeader.nextSibling); 808 } else { 809 if (windowLink) { 810 windowLink.remove(); 811 } 812 } 813 }; 814 815 return ResponsiveTable; 816})(window, document);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.