Function to copy rendered table randomly stops working, a page refresh fixes the issue

Viewed 153

I can't seem to figure out why this function stops working. Refreshing the page fixes the issue. What it does is take a table by ID, strip the classes and does some optimization on the styles. The users are copying and pasting a generated table into their emails.

Can you spot why it's occasionally failing? I don't see any errors in the console when it happens, and I can seem to figure out why it works fine most of the time.

A page refresh fixes the issue.

(this function is called onPress by a button in React. It has worked for over a year just fine but has seemed to have stopped working in the last week sporadically and I can't find any package updates that may have effected it)

function copyByElementId(element: HTMLElement | null) {
    if (!element) return false;

    // Clone the node so we don't interrupt the view
    let el = element.cloneNode(true) as HTMLTableElement;
    el.className = ""; // Remove classes from parent Div

    if (comparedScenarios.length > 1) {
      // Delete last row if the arrows are present
      el.deleteRow(el.children[0].children.length - 1);
    }

    // Add default fonts to the table
    el.style.fontSize = "inherit";
    el.style.fontFamily = "San Francisco, Helvetica, Arial, sans-serif";

    // This works for clients other than Outlook grrr...
    el.style.borderCollapse = "collapse";
    el.style.borderSpacing = "16px 4px";
    el.style.border = "0";

    let rows = el.children[0].children; // Rows in the table

    // Add padding to the left side of each cell other than the 1st column
    for (let i = 0; i < rows.length; i++) {
      let cells = rows[i].children;
      rows[i].className = ""; // Remove classes from rows
      for (let i = 0; i < cells.length; i++) {
        cells[i].className = ""; // Remove classes from cells
        // Only the 2nd column and after
        let cell = cells[i] as HTMLTableCellElement; // TD element
        cell.style.border = "0";
        if (i > 0) {
          cell.style.paddingTop = "0";
          cell.style.paddingRight = "0";
          cell.style.paddingBottom = "0";
          cell.style.paddingLeft = "20px";
        }
      }
    }

    // Create our hidden div element
    let hiddenDiv = document.createElement("div");
    hiddenDiv.style.position = "absolute";
    hiddenDiv.style.left = "-9999px";
    hiddenDiv.style.backgroundColor = "";

    // Render the table of the hidden div
    hiddenDiv.appendChild(el);

    // Check and see if the user had a text selection range
    let currentRange;
    let documentSelection = document.getSelection();
    if (documentSelection?.rangeCount && documentSelection?.rangeCount > 0) {
      // The user has a text selection range, store it
      currentRange = document.getSelection()?.getRangeAt(0);
      // Remove the current selection
      window.getSelection()?.removeAllRanges();
    }

    // Append the div to the body
    document.body.appendChild(hiddenDiv);
    // Create a selection range
    let CopyRange = document.createRange();
    // Set the copy range to be the hidden div
    CopyRange.selectNode(hiddenDiv);
    // Add the copy range
    window.getSelection()?.addRange(CopyRange);

    //since not all browsers support this, use a try block
    try {
      //copy the text
      document.execCommand("copy");
    } catch (err) {
      window.alert("Your browser doesn't support copy and paste! Error:" + err);
    }
    //remove the selection range (Chrome throws a warning if we don't.)
    window.getSelection()?.removeAllRanges();
    //remove the hidden div
    document.body.removeChild(hiddenDiv);

    //return the old selection range
    if (currentRange) {
      window.getSelection()?.addRange(currentRange);
    }
    runButtonStates();
  }
1 Answers

The issue resulted from another React component that was blocking the viewport when it was first mounted (a modal overlay). It didn't seem intuitive at first that this could be causing an issue with the native copy function in the browser.

After upgrading the component and reworking the overlay, it issue resolved itself.

Related