table-wrapper.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. /**
  2. * 为所有 table.table-bordered 添加可滚动父元素(仅在移动设备上)
  3. * 在页面加载时自动执行
  4. */
  5. (function() {
  6. 'use strict';
  7. /**
  8. * 检测是否为移动设备
  9. */
  10. function isMobileDevice() {
  11. // 方法1:检查 User Agent
  12. const userAgent = navigator.userAgent || navigator.vendor || window.opera;
  13. const mobileRegex = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini|mobile/i;
  14. // 方法2:检查触摸支持
  15. const hasTouchScreen = (('ontouchstart' in window) ||
  16. (navigator.maxTouchPoints > 0) ||
  17. (navigator.msMaxTouchPoints > 0));
  18. // 方法3:检查屏幕宽度
  19. const isSmallScreen = window.innerWidth <= 991;
  20. // 综合判断:User Agent 匹配 或 (有触摸支持 且 屏幕较小)
  21. return mobileRegex.test(userAgent) || (hasTouchScreen && isSmallScreen);
  22. }
  23. function wrapTablesWithScroll() {
  24. const isMobile = isMobileDevice();
  25. // 查找所有 class 包含 "table table-bordered" 的表格
  26. const tables = document.querySelectorAll('table.table.table-bordered');
  27. let wrappedCount = 0;
  28. tables.forEach(function(table) {
  29. // 检查是否已经被包裹
  30. const parent = table.parentElement;
  31. // 如果父元素已经有 overflow-x 样式或 table-responsive 类,跳过
  32. if (parent && (
  33. parent.style.overflowX === 'auto' ||
  34. parent.classList.contains('table-responsive') ||
  35. window.getComputedStyle(parent).overflowX === 'auto'
  36. )) {
  37. return;
  38. }
  39. // 判断是否需要包裹
  40. let needWrap = false;
  41. if (isMobile) {
  42. // 移动设备:始终包裹
  43. needWrap = true;
  44. } else {
  45. // 桌面设备:检查表格实际宽度
  46. const tableWidth = table.scrollWidth || table.offsetWidth;
  47. const parentWidth = parent ? (parent.clientWidth || parent.offsetWidth) : window.innerWidth;
  48. // 调试信息
  49. console.log(`表格检测 - scrollWidth: ${table.scrollWidth}, offsetWidth: ${table.offsetWidth}, parentWidth: ${parentWidth}`);
  50. // 如果表格宽度超过 1000px 或超过父容器宽度,则需要包裹
  51. if (tableWidth > 1000 || tableWidth > parentWidth) {
  52. needWrap = true;
  53. console.log(`需要包裹:tableWidth=${tableWidth}, 阈值=1000`);
  54. }
  55. }
  56. if (!needWrap) {
  57. return;
  58. }
  59. // 创建包裹元素
  60. const wrapper = document.createElement('div');
  61. wrapper.style.overflowX = 'auto';
  62. wrapper.style.webkitOverflowScrolling = 'touch'; // iOS 平滑滚动
  63. // 在表格前插入包裹元素
  64. table.parentNode.insertBefore(wrapper, table);
  65. // 将表格移入包裹元素
  66. wrapper.appendChild(table);
  67. wrappedCount++;
  68. });
  69. if (wrappedCount > 0) {
  70. const deviceType = isMobile ? '移动设备' : '桌面设备(宽表格)';
  71. console.log(`✓ ${deviceType}:已为 ${wrappedCount} 个表格添加横向滚动功能`);
  72. } else if (!isMobile) {
  73. console.log('✓ 桌面设备:所有表格宽度正常,无需添加滚动');
  74. }
  75. return wrappedCount;
  76. }
  77. // 执行函数(多次尝试,确保表格完全渲染)
  78. function executeWrap() {
  79. // 立即执行一次
  80. wrapTablesWithScroll();
  81. // 延迟执行,等待表格完全渲染
  82. setTimeout(wrapTablesWithScroll, 100);
  83. setTimeout(wrapTablesWithScroll, 500);
  84. setTimeout(wrapTablesWithScroll, 1000);
  85. }
  86. // 页面加载完成后执行
  87. if (document.readyState === 'loading') {
  88. document.addEventListener('DOMContentLoaded', executeWrap);
  89. } else {
  90. // DOM 已经加载完成,直接执行
  91. executeWrap();
  92. }
  93. // 监听窗口大小变化
  94. let resizeTimer;
  95. window.addEventListener('resize', function() {
  96. clearTimeout(resizeTimer);
  97. resizeTimer = setTimeout(wrapTablesWithScroll, 250);
  98. });
  99. // 监听动态添加的内容(使用 MutationObserver)
  100. const observer = new MutationObserver(function(mutations) {
  101. let shouldWrap = false;
  102. mutations.forEach(function(mutation) {
  103. mutation.addedNodes.forEach(function(node) {
  104. if (node.nodeType === 1) { // 元素节点
  105. // 检查新添加的节点是否是 table 或包含 table
  106. if (node.matches && node.matches('table.table.table-bordered')) {
  107. shouldWrap = true;
  108. } else if (node.querySelectorAll) {
  109. const tables = node.querySelectorAll('table.table.table-bordered');
  110. if (tables.length > 0) {
  111. shouldWrap = true;
  112. }
  113. }
  114. }
  115. });
  116. });
  117. if (shouldWrap) {
  118. setTimeout(wrapTablesWithScroll, 100);
  119. }
  120. });
  121. // 开始观察
  122. observer.observe(document.body, {
  123. childList: true,
  124. subtree: true
  125. });
  126. })();