PageSourceSearch

https://www.dedicated.care/themes/custom/chentheme/js/gtm-defer.js?tm5xd5

js dedicated.care collected 2026-09-30 05:53:40 UTC 3,870 bytes, 95 lines download raw bytes

1/* eslint-disable no-undef */
2/**
3 * @file
4 * Defer GTM until first interaction. chentheme_page_attachments_alter()
5 * strips the google_tag script libraries and passes their resolved URLs via
6 * drupalSettings.chenGtmDefer.scripts; this re-loads them on first interaction
7 * (idle fallback for non-interacting visitors).
8 */
9(() => {
10  const EVENTS = ['scroll', 'mousemove', 'mousedown', 'touchstart', 'keydown'];
11  const LISTENER_OPTS = { once: true, passive: true, capture: true };
12  // Fallback for visitors who never interact. Measured at 5000: behaviours
13  // attach after DCL (~2.4s on a throttled mobile load), so GTM was starting at
14  // ~7.9s and googletagmanager.com was still transferring at ~10s. That keeps
15  // the page network-busy well past the point Lighthouse looks for quiet,
16  // inflating TTI, while buying real users nothing — LCP had long since
17  // painted. 2000 keeps GTM clear of the LCP window but lands it in the same
18  // couple of seconds eager loading would have. [CMDIST-471]
19  const IDLE_FALLBACK_MS = 2000;
20
21  Drupal.behaviors.chenGtmDefer = {
22    attach: () => {
23      if (window.chenGtmDeferBound) {
24        return;
25      }
26      window.chenGtmDeferBound = true;
27
28      const settings = drupalSettings.chenGtmDefer || {};
29      const scripts = settings.scripts || [];
30      if (!scripts.length) {
31        return;
32      }
33
34      let loaded = false;
35      let timer;
36
37      const loadGtm = () => {
38        if (loaded) {
39          return;
40        }
41        loaded = true;
42        clearTimeout(timer);
43        EVENTS.forEach((evt) =>
44          window.removeEventListener(evt, loadGtm, LISTENER_OPTS),
45        );
46
47        // No attachBehaviors() re-run after injection: none of the google_tag
48        // scripts define Drupal behaviours, and the pass would fire at script
49        // file onload — before GTM's container tags have executed — so it
50        // can't serve late GTM-delivered globals either (chen_ketch polls for
51        // its own readiness instead). A full-document pass also re-runs every
52        // behaviour on the page, double-binding any not guarded by once(),
53        // which several legacy theme components are not.
54        //
55        // `src` values are full URLs resolved server-side from the google_tag
56        // library definition, so no module path is assumed here.
57        scripts.forEach((src) => {
58          const script = document.createElement('script');
59          script.src = src;
60          script.async = true;
61          script.onerror = () => {
62            // Surface the failure instead of silently dropping analytics.
63            window.dataLayer = window.dataLayer || [];
64            window.dataLayer.push({
65              event: 'gtm_defer_load_error',
66              src: script.src,
67            });
68            // eslint-disable-next-line no-console
69            window.console.error(
70              `[gtm-defer] failed to load ${script.src} — GTM/analytics will not fire`,
71            );
72          };
73          document.body.appendChild(script);
74        });
75      };
76
77      EVENTS.forEach((evt) =>
78        window.addEventListener(evt, loadGtm, LISTENER_OPTS),
79      );
80      // Fallback for visitors who never interact; keeps GTM well clear of the
81      // LCP measurement window while still collecting analytics.
82      timer = setTimeout(loadGtm, IDLE_FALLBACK_MS);
83      // A visitor who leaves (tab switch / close) before interacting or before
84      // the idle timer still gets GTM loaded on tab-hide, so short bounces
85      // aren't silently dropped. loadGtm is idempotent, so no removal needed.
86      // NOTE: this covers tab-hide only; capturing a true instant close would
87      // need navigator.sendBeacon.
88      document.addEventListener('visibilitychange', () => {
89        if (document.visibilityState === 'hidden') {
90          loadGtm();
91        }
92      });
93    },
94  };
95})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.