Track Copy & Paste
Tracking text copies tells you which content is compelling enough that users want to take it with them. On documentation sites, it shows which code snippets get used. On news sites, it shows which quotes get shared. On pricing pages, it can indicate research behavior.
Implementation
Section titled “Implementation”Fires when a user copies text from the page. Identifies the section and a preview of the copied text.
(function() { document.addEventListener('copy', function(event) { var selection = window.getSelection(); if (!selection || selection.toString().trim().length === 0) return;
var copiedText = selection.toString().trim();
// Identify which section the copy happened in var sectionEl = null; var anchorNode = selection.anchorNode; var el = anchorNode.nodeType === Node.TEXT_NODE ? anchorNode.parentElement : anchorNode;
// Walk up to find a section, article, or heading while (el && el !== document.body) { if (el.dataset.trackSection || el.tagName === 'SECTION' || el.tagName === 'ARTICLE') { sectionEl = el; break; } el = el.parentElement; }
window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: 'text_copy', copy_text_preview: copiedText.substring(0, 100), copy_text_length: copiedText.length, copy_section: sectionEl ? (sectionEl.dataset.trackSection || sectionEl.id || sectionEl.tagName.toLowerCase()) : 'unknown', page_path: window.location.pathname, page_title: document.title }); });})();-
Add the code as a Custom HTML tag in GTM
- Trigger: DOM Ready (All Pages, or limit with Page Path condition)
-
Create a Custom Event Trigger
- Trigger type: Custom Event
- Event name:
text_copy
-
Create Data Layer Variables
DLV - copy_text_preview→copy_text_previewDLV - copy_text_length→copy_text_lengthDLV - copy_section→copy_section
-
Create a GA4 Event Tag
- Tag type: Google Analytics: GA4 Event
- Event name:
text_copy - Parameters:
copy_text_preview→{{DLV - copy_text_preview}}copy_text_length→{{DLV - copy_text_length}}copy_section→{{DLV - copy_section}}page_path→{{Page Path}}
- Trigger: the Custom Event trigger
-
Add section attributes to your HTML for better section identification:
<section data-track-section="introduction"><h2>Introduction</h2><p>Content here...</p></section><section data-track-section="pricing-table">...</section> -
Test in Preview Mode
Select some text on the page and press Ctrl+C (or Cmd+C). A
text_copyevent should appear in the Summary pane.
GA4 - text_copy
- Type
- Google Analytics: GA4 Event
- Trigger
- Custom Event - text_copy
- Variables
-
DLV - copy_text_previewDLV - copy_text_lengthDLV - copy_section
Privacy considerations
Section titled “Privacy considerations”Do not send the full copied text to GA4. Always truncate or hash it:
- Truncate to 100 characters: good for previewing what was copied without capturing full PII
- Send length only: if the text might contain PII, send only
copy_text_length— no content - Filter by page type: only track copies on clearly non-PII pages like documentation or pricing
// Extra-safe version: only track copies on documentation pagesif (!window.location.pathname.startsWith('/docs/')) return;
window.dataLayer.push({ event: 'text_copy', copy_text_length: copiedText.length, copy_section: sectionId, is_code_block: copiedText.match(/\{|\}|=>|function/) ? true : false});Identifying code block copies
Section titled “Identifying code block copies”On documentation sites, knowing which code blocks users copy is particularly valuable:
document.addEventListener('copy', function(event) { var selection = window.getSelection(); var anchor = selection.anchorNode; var el = anchor.nodeType === Node.TEXT_NODE ? anchor.parentElement : anchor;
var inCodeBlock = false; var codeId = null; while (el && el !== document.body) { if (el.tagName === 'CODE' || el.tagName === 'PRE') { inCodeBlock = true; codeId = el.id || el.dataset.codeId || null; break; } el = el.parentElement; }
window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: 'text_copy', copy_type: inCodeBlock ? 'code' : 'text', code_block_id: codeId || undefined, copy_text_length: selection.toString().length });});Test it
Section titled “Test it”- Open GTM Preview, visit a page with content
- Select a sentence and press Ctrl+C (or Cmd+C on Mac)
- Verify
text_copyevent fires in the Summary pane - Check
copy_sectionmatches the expected section - Verify
copy_text_previewshows a truncated version, not the full text
Common gotchas
Section titled “Common gotchas”copy event fires on programmatic copies too. If your page uses document.execCommand('copy') or the Clipboard API to copy text programmatically (e.g., a “Copy to clipboard” button), this handler will also fire. Check the event’s isTrusted property to distinguish user-initiated copies: if (!event.isTrusted) return;
Selection can be empty. Right-clicking without a selection, or keyboard shortcuts for other operations, can fire the copy event with no text selected. The selection.toString().trim().length === 0 guard handles this.