Chrome下如何为window.selection添加多range实现可撤销复制?
Hey there! The problem you're hitting is a browser implementation difference: Chrome's window.getSelection() only allows one Range at a time, whereas Firefox supports multiple. But we can work around this to get the same multi-cell copy behavior, while keeping the native Ctrl-Z undo feature you need.
Core Idea
Instead of trying to add multiple Ranges to the selection (which Chrome blocks), we'll create a hidden temporary element, populate it with all the cell content you want to copy (formatted to preserve table structure), select that single element's content, and then run the copy command. This tricks Chrome into handling the copy while maintaining the undo functionality of document.execCommand("copy").
Modified Full Code
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Copy event to clipboard</title> <script src="https://code.jquery.com/jquery-3.3.1.js"></script> <script> $(document).ready(function() { $("body").on("copy", copy); }); function copy(event) { // Stop default copy behavior—we'll handle it ourselves event.preventDefault(); const endCol = getValue("endCol"); const endRow = getValue("endRow"); const startCol = getValue("startCol"); const startRow = getValue("startRow"); // Create a hidden temporary element to hold our combined content const tempContainer = document.createElement('div'); tempContainer.style.position = 'absolute'; tempContainer.style.left = '-9999px'; tempContainer.style.top = '-9999px'; tempContainer.style.whiteSpace = 'pre'; // Preserve tabs/newlines // Build the content: tab-separated columns, newline-separated rows let copyContent = ''; for (let i = startRow; i <= endRow; i++) { const rowCells = []; for (let j = startCol; j <= endCol; j++) { rowCells.push(getCell(i, j).textContent); } copyContent += rowCells.join('\t') + '\n'; } tempContainer.textContent = copyContent; document.body.appendChild(tempContainer); // Select the entire content of our temporary element const selection = window.getSelection(); selection.removeAllRanges(); const range = document.createRange(); range.selectNodeContents(tempContainer); selection.addRange(range); // Clean up the temp element after copy completes setTimeout(() => document.body.removeChild(tempContainer), 0); } function copyContent() { document.execCommand("copy"); } function getCell(rowIndex, cellIndex) { const table = document.getElementById("dataTable"); return table.rows[rowIndex].cells[cellIndex]; } function getValue(id) { const element = document.getElementById(id); return element.options[element.selectedIndex].value; } </script> </head> <body> <table border="1" id="dataTable"> <tr> <td id="c11" contenteditable="true">1_1</td> <td id="c12" contenteditable="true">1_2</td> <td id="c13" contenteditable="true">1_3</td> </tr> <tr> <td id="c21" contenteditable="true">2_1</td> <td id="c22" contenteditable="true">2_2</td> <td id="c23" contenteditable="true">2_3</td> </tr> <tr> <td id="c31" contenteditable="true">3_1</td> <td id="c32" contenteditable="true">3_2</td> <td id="c33" contenteditable="true">3_3</td> </tr> </table><br> start column: <select id="startCol"> <option value=0>1</option> <option value=1>2</option> <option value=2>3</option> </select> end column: <select id="endCol"> <option value=0>1</option> <option value=1>2</option> <option value=2>3</option> </select> <br> start row: <select id="startRow"> <option value=0>1</option> <option value=1>2</option> <option value=2>3</option> </select> end row: <select id="endRow"> <option value=0>1</option> <option value=1>2</option> <option value=2>3</option> </select> <br> <button onclick="copyContent()">Copy</button> <textarea id=copiedContent> </textarea> </body> </html>
Key Changes Explained
- Prevent Default Copy: We add
event.preventDefault()to thecopyhandler to stop Chrome from copying whatever it thinks is selected. - Hidden Temp Element: This element holds our combined cell content, styled to be invisible and preserve whitespace (so tabs/newlines work correctly when pasted into tools like Excel).
- Single Range Selection: We select the entire content of the temp element with one Range—this plays nicely with Chrome's selection limits.
- Automatic Cleanup: The temp element is removed right after copy to keep the DOM clean and avoid any unintended side effects.
Why Undo Still Works
We're still using document.execCommand("copy"), which integrates with the browser's native undo stack. That means users can hit Ctrl-Z (or Cmd-Z on Mac) to undo the copy, just like with native text selection.
Alternative: Clipboard API
If you don't need to support older browsers, you could use navigator.clipboard.writeText(copyContent) directly. However, this method doesn't support native undo because it bypasses the browser's editing context. Since undo is a requirement for you, the temp element approach is the way to go.
内容的提问来源于stack exchange,提问作者HK KNVB

