Copy all text with one click: navigator.clipboard.writeText(document.body.innerText);
Copy webpage body content to clipboard
The code navigator.clipboard.writeText(document.body.innerText)
quickly copies the current webpage's body text to the clipboard. It combines the Clipboard API from the Web API with DOM operations to implement a common yet practical feature.
Clipboard API Basics
The Clipboard API provides asynchronous read/write capabilities for clipboard content. Compared to the deprecated document.execCommand('copy')
method, it is more modern and secure. Main methods include:
writeText()
: Writes plain text to the clipboardreadText()
: Reads plain text from the clipboardwrite()
: Writes arbitrary data (e.g., images)read()
: Reads arbitrary data
// Basic usage example
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
console.log('Content copied to clipboard');
} catch (err) {
console.error('Copy failed:', err);
}
}
document.body.innerText Explained
document.body.innerText
retrieves the visible text content of the <body>
element and all its children, automatically ignoring HTML tags while preserving text formatting. The similar textContent
property retains all whitespace characters, while innerHTML
returns a string containing HTML tags.
// Comparing different properties
const body = document.body;
console.log(body.innerText); // Plain text
console.log(body.textContent); // Includes text from hidden elements
console.log(body.innerHTML); // Includes HTML tags
Practical Application Scenarios
- Browser Bookmark Tool: Create a bookmarklet (javascript:) to copy current page content with a click
javascript:navigator.clipboard.writeText(document.body.innerText);alert('Page content copied');
- Browser Extension: Add a "Copy Page Text" option to the right-click menu
// Chrome extension background.js
chrome.contextMenus.create({
id: "copyPageText",
title: "Copy Page Text",
contexts: ["page"]
});
chrome.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId === "copyPageText") {
chrome.scripting.executeScript({
target: {tabId: tab.id},
function: () => {
navigator.clipboard.writeText(document.body.innerText)
.then(() => alert('Copy successful'))
.catch(err => alert('Copy failed: ' + err));
}
});
}
});
- Web Tool: Add a "Copy Full Text" button to article pages
<button id="copyFullText">Copy Full Text</button>
<script>
document.getElementById('copyFullText').addEventListener('click', async () => {
try {
await navigator.clipboard.writeText(document.body.innerText);
alert('Full text copied');
} catch (err) {
alert(`Copy failed: ${err}`);
}
});
</script>
Permissions and Security Restrictions
The Clipboard API is subject to strict browser security restrictions:
- HTTPS Requirement: May be unavailable in non-secure contexts (HTTP)
- User Gesture: Some browsers require triggering within user interactions (e.g., clicks)
- Permissions API: May require requesting
clipboard-write
permission
// Check clipboard permissions
navigator.permissions.query({name: 'clipboard-write'}).then(result => {
if (result.state === 'granted' || result.state === 'prompt') {
console.log('Can write to clipboard');
} else {
console.log('No clipboard write permission');
}
});
Error Handling and Fallback Solutions
Due to browser compatibility issues, proper fallback handling is needed:
async function copyPageText() {
const text = document.body.innerText;
try {
// Modern browser approach
if (navigator.clipboard) {
await navigator.clipboard.writeText(text);
return;
}
// Legacy browser fallback
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed'; // Prevent scrolling
document.body.appendChild(textarea);
textarea.select();
try {
const successful = document.execCommand('copy');
if (!successful) throw new Error('Copy failed');
} finally {
document.body.removeChild(textarea);
}
} catch (err) {
console.error('Copy failed:', err);
// Can provide manual copy option
prompt('Please manually copy the following', text);
}
}
Performance Optimization Suggestions
Optimizations needed for large documents:
- Text Preprocessing: Remove excess whitespace and duplicate content
function getCleanText() {
return document.body.innerText
.replace(/\s+/g, ' ') // Merge multiple spaces
.trim(); // Remove leading/trailing spaces
}
- Chunked Copying: Process very large documents in chunks
async function copyLargeContent() {
const chunks = splitText(document.body.innerText, 10000); // 10k characters per chunk
for (const chunk of chunks) {
await navigator.clipboard.writeText(chunk);
// Add delay to avoid browser limits
await new Promise(resolve => setTimeout(resolve, 100));
}
}
function splitText(text, size) {
const chunks = [];
for (let i = 0; i < text.length; i += size) {
chunks.push(text.substring(i, i + size));
}
return chunks;
}
- Selective Copying: Copy only specific areas instead of entire body
// Copy only article tag content
const articleText = document.querySelector('article').innerText;
navigator.clipboard.writeText(articleText);
User Experience Enhancements
- Visual Feedback: Show success prompt instead of using alert
<div id="copyFeedback" style="display:none; position:fixed; bottom:20px; right:20px; background:#4CAF50; color:white; padding:10px; border-radius:4px;">
Text copied!
</div>
<script>
async function copyWithFeedback() {
try {
await navigator.clipboard.writeText(document.body.innerText);
const feedback = document.getElementById('copyFeedback');
feedback.style.display = 'block';
setTimeout(() => feedback.style.display = 'none', 2000);
} catch (err) {
console.error('Copy failed:', err);
}
}
</script>
- Copy Progress Display: Show progress for large documents
<progress id="copyProgress" value="0" max="100" style="width:100%"></progress>
<script>
async function copyWithProgress() {
const text = document.body.innerText;
const chunkSize = 10000;
const totalChunks = Math.ceil(text.length / chunkSize);
const progress = document.getElementById('copyProgress');
progress.max = totalChunks;
for (let i = 0; i < totalChunks; i++) {
const chunk = text.substring(i * chunkSize, (i + 1) * chunkSize);
await navigator.clipboard.writeText(chunk);
progress.value = i + 1;
await new Promise(resolve => setTimeout(resolve, 50));
}
}
</script>
Advanced Application Examples
- Copy as Markdown: Convert HTML content to Markdown before copying
function htmlToMarkdown(html) {
// Simple conversion example
return html
.replace(/<h[1-6]>(.*?)<\/h[1-6]>/g, '\n# $1\n')
.replace(/<p>(.*?)<\/p>/g, '\n$1\n')
.replace(/<a href="(.*?)">(.*?)<\/a>/g, '[$2]($1)')
.replace(/<strong>(.*?)<\/strong>/g, '**$1**')
.replace(/<em>(.*?)<\/em>/g, '*$1*')
.replace(/<[^>]+>/g, '');
}
async function copyAsMarkdown() {
const markdown = htmlToMarkdown(document.body.innerHTML);
await navigator.clipboard.writeText(markdown);
}
- Copy with Source Information: Add source URL to copied text
async function copyWithSource() {
const text = `${document.body.innerText}\n\nSource: ${window.location.href}`;
await navigator.clipboard.writeText(text);
}
- Multilingual Support: Show different prompts based on user language
async function copyWithI18n() {
try {
await navigator.clipboard.writeText(document.body.innerText);
const lang = navigator.language || 'en';
const messages = {
'zh-CN': '文本已复制',
'en': 'Text copied',
'ja': 'テキストをコピーしました'
};
alert(messages[lang] || messages['en']);
} catch (err) {
console.error('Copy failed:', err);
}
}
Debugging and Testing
Testing clipboard functionality requires attention to:
- Console Testing: Run directly in console
// Test in browser console
document.body.innerText.length // First check text length
navigator.clipboard.writeText('Test text').then(() => console.log('Success'))
- Unit Testing: Use testing frameworks like Jest
// Mock navigator.clipboard
beforeEach(() => {
global.navigator.clipboard = {
writeText: jest.fn().mockResolvedValue(undefined)
};
});
test('Copy body text', async () => {
document.body.innerHTML = '<div>Test content</div>';
await copyPageText();
expect(navigator.clipboard.writeText).toHaveBeenCalledWith('Test content');
});
- Error Scenario Testing: Simulate permission denials etc.
// Test error handling
test('Handle clipboard rejection', async () => {
navigator.clipboard.writeText = jest.fn().mockRejectedValue(new Error('Permission denied'));
console.error = jest.fn();
await copyPageText();
expect(console.error).toHaveBeenCalled();
});
Browser Compatibility Considerations
Different browsers implement the Clipboard API differently:
- Chrome: Requires HTTPS or localhost
- Firefox: Supported since version 63
- Safari: Partial support, may require user gesture
- Mobile Browsers: May have additional restrictions
// Compatibility check function
function canUseClipboard() {
return !!navigator.clipboard &&
(location.protocol === 'https:' || location.hostname === 'localhost' || location.protocol === 'file:');
}
if (!canUseClipboard()) {
console.warn('Clipboard API may be unavailable in current environment');
// Show fallback UI
}
Real Project Integration
In real projects, can be encapsulated as reusable modules:
// clipboard.js
export default {
/**
* Copy text to clipboard
* @param {string} text - Text to copy
* @param {object} options - Options
* @param {boolean} options.fallback - Whether to enable fallback
* @returns {Promise<void>}
*/
async copy(text, { fallback = true } = {}) {
try {
if (navigator.clipboard) {
return await navigator.clipboard.writeText(text);
}
if (!fallback) {
throw new Error('Clipboard API not available');
}
// Fallback solution
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
document.body.appendChild(textarea);
textarea.select();
try {
if (!document.execCommand('copy')) {
throw new Error('execCommand failed');
}
} finally {
document.body.removeChild(textarea);
}
} catch (err) {
console.error('Copy failed:', err);
throw err;
}
},
/**
* Copy page body content
* @param {HTMLElement} [element=document.body] - Element to copy
* @returns {Promise<void>}
*/
async copyPageContent(element = document.body) {
return this.copy(element.innerText);
}
};
// Usage example
import clipboard from './clipboard.js';
document.getElementById('copyBtn').addEventListener('click', async () => {
try {
await clipboard.copyPageContent();
showToast('Copy successful');
} catch {
showToast('Copy failed, please manually select text to copy');
}
});
Related Technology Extensions
- Selection API: APIs related to text selection
// Get user-selected text
function getSelectedText() {
return window.getSelection().toString();
}
// Copy selected text
document.getElementById('copySelected').addEventListener('click', () => {
const selectedText = getSelectedText();
if (selectedText) {
navigator.clipboard.writeText(selectedText);
}
});
- Drag and Drop API: Implement drag-and-drop copying
<div id="dragSource" draggable="true">Drag me to copy text</div>
<script>
document.getElementById('dragSource').addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', document.body.innerText);
e.dataTransfer.effectAllowed = 'copy';
});
</script>
- SharedWorker: Share clipboard state across multiple tabs
// worker.js
const ports = new Set();
onconnect = (e) => {
const port = e.ports[0];
ports.add(port);
port.onmessage = (e) => {
if (e.data.type === 'clipboard-update') {
// Broadcast to all tabs
for (const p of ports) {
p.postMessage({ type: 'clipboard-notify', text: e.data.text });
}
}
};
};
// Main page
const worker = new SharedWorker('worker.js');
// Notify other tabs when copying
async function copyAndNotify(text) {
await navigator.clipboard.writeText(text);
worker.port.postMessage({ type: 'clipboard-update', text });
}
// Receive clipboard updates from other tabs
worker.port.onmessage = (e) => {
if (e.data.type === 'clipboard-notify') {
console.log('Other tab updated clipboard:', e.data.text);
}
};
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn