Skip to content

Offloading Google Analytics (gtag) to a Web Worker #1455

@westonruter

Description

@westonruter

Feature Description

The Web Worker Offloading plugin is now merged into trunk (#1247). However, for it to be utilized a plugin author has to manually make the web-worker-offloading script a dependency of the script intended to be offloaded. As mentioned in #1247 (comment), we should look for opportunities to offload existing scripts automatically as much as possible. From Partytown's Common Services page, they list out 3rd party services known to be compatible. Included on that list is Google Tag Manager, which I've found to be responsible for INP issues on highly-trafficked sites. Partytown has documentation for how to integrate with Google Tag Manager.

The challenge for automatically implementing the Partytown integration with Google Tag Manager is that the plugins (and themes) which add gtag do so in inconsistent ways. The scripts could be registered properly via wp_enqueue_script() but the handles won't be consistent. Or a plugin may manually print the script at wp_head.

In the case where a plugin properly enqueues the script, we can look at WPdirectory for the most common script handles used. Otherwise, for manually-printed scripts the alternative would be for Web Worker Offloading to integrate with Optimization Detective to register a tag visitor that looks for a SCRIPT tag with a src pointing to https://www.googletagmanager.com/gtag/js, and when present, inject the Partytown script (if not already present) and update the SCRIPT tag attributes as necessary.

In looking at WPdirectory for plugins with at least 100K installs, the script handles used are:

Plugin Handle Install Count Source
WooCommerce (Google Analytics for WooCommerce) google-tag-manager 5,000,000 (200,000) Trac (Trac)
Site Kit by Google google_gtagjs 4,000,000 Trac
Rank Math SEO google_gtagjs 2,000,000 Trac

These are not using GTM:

image

These add up to 11.2 million installs.

The following plugins print the script without using WP_Scripts:

Plugin Install Count
Jetpack (legacy module) 4,000,000
Complianz – GDPR/CCPA Cookie Consent 900,000
Google Listings & Ads 900,000
LightStart 700,000
GA Google Analytics 600,000
SEOPress 300,000
Blocksy Companion 200,000
WooCommerce Checkout & Funnel Builder by CartFlows 200,000
Orbit Fox by ThemeIsle 200,000
CTX Feed 100,000
SEO Plugin by Squirrly SEO 200,000
VK All in One Expansion Unit 100,000

Excluding the Jetpack legacy module, these total up to 4.4 million installs.

So by starting out just targeting scripts registered via WP_Scripts, we can handle ~72% of the installs.

So the only script handles used are google-tag-manager and google_gtagjs. However, we could discover other script handles by looping over wp_scripts()->registered to find any other dependencies with a src beginning with https://www.googletagmanager.com/gtag/js at runtime. We can then add web-worker-offloading as a dependency for this registered script.

However, adding the script dependency is half of what we need to do. We also need to make sure that the inline script also gets the text/partytown type:

  <script type="text/partytown">
    window.dataLayer = window.dataLayer || [];
    window.gtag = function gtag(){dataLayer.push(arguments);}
    gtag('js', new Date());

    gtag('config', 'YOUR-ID-HERE');
  </script>

WooCommerce adds this as an inline after script. As does Site Kit. It seems that "Google Analytics for WooCommerce" adds another script with its own inline script, so it may not be as straightforward to offload to a worker.

WC_Google_Gtag_JS::enquque_tracker() [sic] method in woocommerce-google-analytics-integration/includes/class-wc-google-gtag-js.php
	public function enquque_tracker(): void {
		wp_enqueue_script(
			'google-tag-manager',
			'https://www.googletagmanager.com/gtag/js?id=' . self::get( 'ga_id' ),
			array(),
			null,
			array(
				'strategy' => 'async',
			)
		);
		// tracker.js needs to be executed ASAP, the remaining bits for main.js could be deffered,
		// but to reduce the traffic, we ship it all together.
		wp_enqueue_script(
			$this->script_handle,
			Plugin::get_instance()->get_js_asset_url( 'main.js' ),
			array(
				...Plugin::get_instance()->get_js_asset_dependencies( 'main' ),
				'google-tag-manager',
			),
			Plugin::get_instance()->get_js_asset_version( 'main' ),
			true
		);
		// Provide tracker's configuration.
		wp_add_inline_script(
			$this->script_handle,
			sprintf(
				'var wcgai = {config: %s};',
				wp_json_encode( $this->get_analytics_config() )
			),
			'before'
		);
	}

But in the case of WooCommerce and Site Kit doing things in a more straightforward case: in order to add the type="text/partytown" to the inline script, we can do so via filtering wp_inline_script_attributes. For example:

add_filter( 'wp_inline_script_attributes', static function ( $attributes, $data ) {
	if (
		array_key_exists( 'id', $attributes ) &&
		in_array( $attributes['id'], array( 'google-tag-manager-js-after', 'google_gtagjs-js-after' ), true ) &&
		str_contains( $data, 'dataLayer' )
	) {
		$attributes['type'] = 'text/partytown';
	}
	return $attributes;
} );

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

Status

Done 😃

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions