{"id":28314513,"url":"https://github.com/openmost/ga4-events-javascript","last_synced_at":"2026-01-27T14:33:22.682Z","repository":{"id":284968220,"uuid":"956637531","full_name":"openmost/ga4-events-javascript","owner":"openmost","description":null,"archived":false,"fork":false,"pushed_at":"2025-03-28T16:48:57.000Z","size":15,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-24T19:11:17.566Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/openmost.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-03-28T15:40:47.000Z","updated_at":"2025-03-28T16:49:01.000Z","dependencies_parsed_at":"2025-03-28T17:37:23.420Z","dependency_job_id":"d52313e7-1f2d-446b-bdbb-a04c7f496664","html_url":"https://github.com/openmost/ga4-events-javascript","commit_stats":null,"previous_names":["openmost/ga4-events-javascript"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/openmost/ga4-events-javascript","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openmost%2Fga4-events-javascript","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openmost%2Fga4-events-javascript/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openmost%2Fga4-events-javascript/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openmost%2Fga4-events-javascript/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/openmost","download_url":"https://codeload.github.com/openmost/ga4-events-javascript/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openmost%2Fga4-events-javascript/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28815108,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-27T12:25:15.069Z","status":"ssl_error","status_checked_at":"2026-01-27T12:25:05.297Z","response_time":168,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-05-24T19:10:11.375Z","updated_at":"2026-01-27T14:33:22.677Z","avatar_url":"https://github.com/openmost.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Recreate GA4 native events\n\n```javascript\n(function () {\n    // Initialize dataLayer\n    window.dataLayer = window.dataLayer || [];\n\n    function sendEvent(eventName, params = {}) {\n        const defaultParams = {\n            language: navigator.language || navigator.userLanguage,\n            page_location: window.location.href,\n            page_referrer: document.referrer,\n            page_title: document.title,\n            screen_resolution: `${window.screen.width}x${window.screen.height}`,\n            user_agent: navigator.userAgent,\n            operating_system: navigator.platform,\n            timestamp: new Date().toISOString(),  \n        };\n\n        window.dataLayer.push({ event: eventName, ...defaultParams, ...params });\n    }\n\n    // Track first visit (check if it's the first visit)\n    function trackFirstVisit() {\n        if (!localStorage.getItem(\"first_visit\")) {\n            sendEvent(\"first_visit\", {\n                engagement_time_msec: 0\n            });\n            localStorage.setItem(\"first_visit\", \"true\");\n        }\n    }\n\n    // Track session (if inactive for 30 min)\n    function trackSession() {\n        let lastSessionTime = sessionStorage.getItem(\"session_start\");\n        let now = Date.now();\n        if (!lastSessionTime || now - lastSessionTime \u003e 30 * 60 * 1000) {\n            sendEvent(\"session_start\", {\n                engagement_time_msec: now - lastSessionTime,\n                session_id: now // Using timestamp as a session ID\n            });\n            sessionStorage.setItem(\"session_start\", now);\n        }\n    }\n\n    // Track user engagement (every 10 seconds)\n    function trackUserEngagement() {\n        setInterval(() =\u003e sendEvent(\"user_engagement\", {\n            engagement_time_msec: 10000,\n            page_location: window.location.href\n        }), 10000);\n    }\n\n    // Track page view\n    function trackPageView() {\n        sendEvent(\"page_view\");\n    }\n\n    // Track outbound link clicks\n    function trackOutboundClicks() {\n        document.addEventListener(\"click\", function (event) {\n            let link = event.target.closest(\"a\");\n            if (link \u0026\u0026 link.href) {\n                const isOutbound = new URL(link.href).hostname !== window.location.hostname;  // Dynamically check if the link is outbound\n                if (isOutbound) {\n                    sendEvent(\"click\", {\n                        link_classes: link.className,\n                        link_domain: new URL(link.href).hostname,\n                        link_id: link.id || null,\n                        link_url: link.href,\n                        outbound: true\n                    });\n                }\n            }\n        });\n    }\n\n    // Track internal search terms\n    function trackSearch() {\n        let params = new URLSearchParams(window.location.search);\n        let searchTerm = params.get(\"q\") || params.get(\"s\") || params.get(\"search\") || params.get(\"query\") || params.get(\"keyword\");\n        if (searchTerm) {\n            // Check if the search term is unique for the session using localStorage\n            let searchHistory = JSON.parse(localStorage.getItem(\"searchHistory\")) || [];\n            let isUniqueSearchTerm = !searchHistory.includes(searchTerm);\n\n            if (isUniqueSearchTerm) {\n                searchHistory.push(searchTerm);\n                localStorage.setItem(\"searchHistory\", JSON.stringify(searchHistory));\n            }\n\n            sendEvent(\"view_search_results\", {\n                search_term: searchTerm,\n                unique_search_term: isUniqueSearchTerm ? 1 : 0, // Only set to 1 if it's unique in this session\n                search_page: window.location.href\n            });\n        }\n    }\n\n    // Track scroll events at 90% of the page height\n    function trackScroll() {\n        let scrolled = false;\n        window.addEventListener(\"scroll\", function () {\n            if (!scrolled \u0026\u0026 window.innerHeight + window.scrollY \u003e= document.documentElement.scrollHeight * 0.9) {\n                sendEvent(\"scroll\", {\n                    scroll_percentage: 90,\n                    engagement_time_msec: Date.now() - window.performance.timing.navigationStart,\n                    page_location: window.location.href\n                });\n                scrolled = true;\n            }\n        });\n    }\n\n    // Track file downloads\n    function trackFileDownload() {\n        document.addEventListener(\"click\", function (event) {\n            let link = event.target.closest(\"a\");\n            if (link \u0026\u0026 link.href \u0026\u0026 link.download \u0026\u0026 /(?:pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma)$/i.test(link.href)) {\n                sendEvent(\"file_download\", {\n                    file_extension: link.href.split('.').pop(),\n                    file_name: link.href.split(\"/\").pop(),\n                    link_classes: link.className,\n                    link_id: link.id || null,\n                    link_text: link.textContent.trim(),\n                    link_url: link.href\n                });\n            }\n        });\n    }\n\n    // Track form starts and submissions\n    function trackForms() {\n        document.addEventListener(\"focusin\", function (event) {\n            if (event.target.closest(\"form\")) {\n                sendEvent(\"form_start\", {\n                    form_id: event.target.closest(\"form\").id,\n                    form_name: event.target.closest(\"form\").name,\n                    form_destination: event.target.closest(\"form\").action\n                });\n            }\n        });\n\n        document.addEventListener(\"submit\", function (event) {\n            sendEvent(\"form_submit\", {\n                form_id: event.target.id,\n                form_name: event.target.name,\n                form_destination: event.target.action,\n                form_submit_text: event.submitter ? event.submitter.value : null\n            });\n        });\n    }\n\n    // Track HTML5 video events\n    function trackHTML5Videos() {\n        document.querySelectorAll(\"video\").forEach((video) =\u003e {\n            video.addEventListener(\"play\", () =\u003e sendEvent(\"video_start\", {\n                video_provider: \"HTML5\",\n                video_title: video.title,\n                video_url: video.currentSrc,\n                video_current_time: video.currentTime,\n                video_duration: video.duration,\n                visible: isElementInViewport(video) // Dynamically check if video is visible in viewport\n            }));\n            video.addEventListener(\"ended\", () =\u003e sendEvent(\"video_complete\", {\n                video_provider: \"HTML5\",\n                video_title: video.title,\n                video_url: video.currentSrc,\n                video_duration: video.duration,\n                visible: isElementInViewport(video)\n            }));\n            video.addEventListener(\"timeupdate\", () =\u003e {\n                let percent = (video.currentTime / video.duration) * 100;\n                if ([10, 25, 50, 75].includes(Math.round(percent))) {\n                    sendEvent(\"video_progress\", {\n                        video_provider: \"HTML5\",\n                        video_title: video.title,\n                        video_url: video.currentSrc,\n                        video_current_time: video.currentTime,\n                        video_duration: video.duration,\n                        video_percent: Math.round(percent),\n                        visible: isElementInViewport(video)\n                    });\n                }\n            });\n        });\n    }\n\n    // Track YouTube videos (via embedded iframe)\n    function trackYouTubeVideos() {\n        if (!window.YT || !YT.Player) return;\n\n        document.querySelectorAll(\"iframe[src*='youtube.com']\").forEach((iframe) =\u003e {\n            let player = new YT.Player(iframe, {\n                events: {\n                    onStateChange: function (event) {\n                        if (event.data == YT.PlayerState.PLAYING) sendEvent(\"video_start\", {\n                            video_provider: \"YouTube\",\n                            video_url: iframe.src,\n                            video_title: iframe.title,\n                            visible: isElementInViewport(iframe)\n                        });\n                        if (event.data == YT.PlayerState.ENDED) sendEvent(\"video_complete\", {\n                            video_provider: \"YouTube\",\n                            video_url: iframe.src,\n                            video_title: iframe.title,\n                            visible: isElementInViewport(iframe)\n                        });\n                    },\n                },\n            });\n        });\n    }\n\n    // Load YouTube API if necessary\n    function loadYouTubeAPI() {\n        if (document.querySelector(\"iframe[src*='youtube.com']\")) {\n            let tag = document.createElement(\"script\");\n            tag.src = \"https://www.youtube.com/iframe_api\";\n            document.body.appendChild(tag);\n            window.onYouTubeIframeAPIReady = trackYouTubeVideos;\n        }\n    }\n\n    // Check if an element is in the viewport (for video visibility tracking)\n    function isElementInViewport(el) {\n        let rect = el.getBoundingClientRect();\n        return rect.top \u003e= 0 \u0026\u0026 rect.left \u003e= 0 \u0026\u0026 rect.bottom \u003c= (window.innerHeight || document.documentElement.clientHeight) \u0026\u0026 rect.right \u003c= (window.innerWidth || document.documentElement.clientWidth);\n    }\n\n    // ========== FUNCTION CALLS ==========\n    trackFirstVisit();      // first_visit\n    trackSession();         // session_start\n    trackUserEngagement();  // user_engagement\n    trackPageView();        // page_view\n    trackOutboundClicks();  // click\n    trackSearch();          // view_search_results\n    trackScroll();          // scroll\n    trackFileDownload();    // file_download\n    trackForms();           // form_start, form_submit\n    trackHTML5Videos();     // video_start, video_progress, video_complete\n    loadYouTubeAPI();       // video_start, video_complete (YouTube)\n})();\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenmost%2Fga4-events-javascript","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopenmost%2Fga4-events-javascript","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenmost%2Fga4-events-javascript/lists"}