Copy table

Adds a button to copy tables to the clipboard in HTML format for pasting into Word with full borders.

  1. // ==UserScript==
  2. // @name Copy table
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description Adds a button to copy tables to the clipboard in HTML format for pasting into Word with full borders.
  6. // @author Bui Quoc Dung
  7. // @match *://*/*
  8. // @grant GM_addStyle
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. // Add some CSS for the copy button
  15. GM_addStyle(`
  16. .copy-table-btn {
  17. margin-top: 5px;
  18. padding: 5px 10px;
  19. font-size: 13px;
  20. background: #333333;
  21. color: white;
  22. border: none;
  23. border-radius: 3px;
  24. cursor: pointer;
  25. }
  26. `);
  27.  
  28. // Function to add the "Copy to Word" button below a table element
  29. function addCopyButtonToTable(table) {
  30. // Avoid adding duplicate buttons
  31. if (table.nextElementSibling && table.nextElementSibling.classList.contains('copy-table-btn')) {
  32. return;
  33. }
  34.  
  35. // Ensure all table cells have borders
  36. table.style.borderCollapse = 'collapse';
  37. table.querySelectorAll('th, td').forEach(cell => {
  38. cell.style.border = '1px solid black';
  39. cell.style.padding = '4px';
  40. });
  41.  
  42. // Create the button element
  43. const btn = document.createElement('button');
  44. btn.textContent = 'Copy';
  45. btn.className = 'copy-table-btn';
  46. btn.addEventListener('click', function(e) {
  47. e.stopPropagation();
  48. e.preventDefault();
  49. copyTableToClipboard(table);
  50. });
  51.  
  52. // Insert button after the table
  53. table.parentElement.insertBefore(btn, table.nextSibling);
  54. }
  55.  
  56. // Function that copies the table's HTML to the clipboard
  57. function copyTableToClipboard(table) {
  58. const html = table.outerHTML;
  59. // Try using the Clipboard API with HTML support
  60. if (navigator.clipboard && navigator.clipboard.write) {
  61. const type = "text/html";
  62. const blob = new Blob([html], { type: type });
  63. const data = [new ClipboardItem({ [type]: blob })];
  64. navigator.clipboard.write(data).catch(err => {
  65. console.error('Error copying table: ', err);
  66. fallbackCopy(html);
  67. });
  68. } else {
  69. fallbackCopy(html);
  70. }
  71. }
  72.  
  73. // Fallback copy function using a temporary element and execCommand
  74. function fallbackCopy(html) {
  75. const tempDiv = document.createElement('div');
  76. tempDiv.style.position = 'absolute';
  77. tempDiv.style.left = '-9999px';
  78. tempDiv.innerHTML = html;
  79. document.body.appendChild(tempDiv);
  80. const range = document.createRange();
  81. range.selectNodeContents(tempDiv);
  82. const sel = window.getSelection();
  83. sel.removeAllRanges();
  84. sel.addRange(range);
  85. try {
  86. document.execCommand('copy');
  87. } catch (err) {
  88. console.error('Fallback: Unable to copy', err);
  89. }
  90. sel.removeAllRanges();
  91. document.body.removeChild(tempDiv);
  92. }
  93.  
  94. // Observe the document for new tables (for dynamically loaded content)
  95. const observer = new MutationObserver(mutations => {
  96. mutations.forEach(mutation => {
  97. mutation.addedNodes.forEach(node => {
  98. if (node.nodeType === 1) { // ELEMENT_NODE
  99. if (node.tagName === 'TABLE') {
  100. addCopyButtonToTable(node);
  101. } else {
  102. const tables = node.querySelectorAll('table');
  103. tables.forEach(addCopyButtonToTable);
  104. }
  105. }
  106. });
  107. });
  108. });
  109. observer.observe(document.body, { childList: true, subtree: true });
  110.  
  111. // Initially add buttons to any tables already on the page
  112. document.querySelectorAll('table').forEach(addCopyButtonToTable);
  113. })();