Skills › Marketing Skills › conversion-tracking › file-download.js
file-download.js
94 lines · skills/conversion-tracking/assets/snippets/file-download.js at 88c79f6
1/*! File download conversion detection (MIT)
2 * Detects: click on a downloadable file link. Pushes dataLayer event: file_download
3 * From the conversion-tracking skill in Marketing Skills */
4(function () {
5 if (window.__ctDetectFileDownload) return;
6 window.__ctDetectFileDownload = true;
7 window.dataLayer = window.dataLayer || [];
9 /* For gated content that is NOT gated: price lists, brochures, spec
10 sheets, lead magnets served as a direct link. If the download sits
11 behind a form, track the form instead — that is where the lead is.
13 GA4 already collects file_download as an enhanced measurement event.
14 Use this when you need the same signal in Google Ads or Meta, which
15 do not. */
17 // EDIT if you serve other formats.
18 var EXTENSIONS = [
19 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx',
20 'csv', 'zip', 'rar', '7z', 'dmg', 'exe', 'pkg',
21 'mp3', 'mp4', 'mov', 'wav', 'txt', 'rtf'
22 ];
24 var DEDUPE_MS = 2000;
25 var lastUrl = null;
26 var lastAt = 0;
28 function extensionOf(url) {
29 try {
30 // Strip query and hash before reading the extension, so
31 // /brochure.pdf?utm_source=x still resolves to "pdf".
32 var path = String(url || '').split('#')[0].split('?')[0];
33 var name = path.substring(path.lastIndexOf('/') + 1);
34 var dot = name.lastIndexOf('.');
35 if (dot < 0) return null;
36 var ext = name.substring(dot + 1).toLowerCase();
37 for (var i = 0; i < EXTENSIONS.length; i++) {
38 if (EXTENSIONS[i] === ext) return ext;
39 }
40 } catch (e) { /* fall through */ }
41 return null;
42 }
44 function fileNameOf(url) {
45 try {
46 var path = String(url || '').split('#')[0].split('?')[0];
47 return decodeURIComponent(path.substring(path.lastIndexOf('/') + 1));
48 } catch (e) {
49 return '';
50 }
51 }
53 function handleClick(event) {
54 try {
55 if (event.button !== undefined && event.button !== 0) return;
56 if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
58 var target = event.target;
59 if (!target || !target.closest) return;
61 var link = target.closest('a[href]');
62 if (!link) return;
64 // Resolve relative hrefs against the current page. .href does this for
65 // us where available; getAttribute would leave "/files/x.pdf" relative.
66 var url = link.href || link.getAttribute('href');
67 if (!url) return;
69 // An anchor carrying the download attribute is a download regardless
70 // of extension, so honour that too.
71 var ext = extensionOf(url);
72 var forced = link.hasAttribute && link.hasAttribute('download');
73 if (!ext && !forced) return;
75 var now = new Date().getTime();
76 if (url === lastUrl && (now - lastAt) < DEDUPE_MS) return;
77 lastUrl = url;
78 lastAt = now;
80 window.dataLayer.push({
81 event: 'file_download',
82 file_name: fileNameOf(url),
83 file_extension: ext || '',
84 file_url: url,
85 link_text: (link.textContent || '').trim().slice(0, 100),
86 page_path: window.location.pathname
87 });
88 } catch (e) {
89 // Never throw back into the page's own click handling.
90 }
91 }
93 document.addEventListener('click', handleClick, false);
94})();