meta pixel: more robust loading & graceful failure with adblockers
This commit is contained in:
@@ -16,7 +16,7 @@
|
|||||||
* @throws Error if the Meta Pixel API is not loaded.
|
* @throws Error if the Meta Pixel API is not loaded.
|
||||||
*/
|
*/
|
||||||
static loadGuard(): void {
|
static loadGuard(): void {
|
||||||
if (!this._baseLoaded || !window._fbq) {
|
if (!this._baseLoaded || !window.fbq) {
|
||||||
throw new Error('Meta Pixel API has not been loaded. Call PixelControl.load() first.');
|
throw new Error('Meta Pixel API has not been loaded. Call PixelControl.load() first.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,11 +32,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Loads the Meta Pixel base script. */
|
/** Loads the Meta Pixel base script. */
|
||||||
static load() {
|
static async load() {
|
||||||
if (this._baseLoaded && window._fbq) return;
|
if (this._baseLoaded && !!window.fbq) return;
|
||||||
if (!window._fbq) {
|
if (!window.fbq) {
|
||||||
PixelControl.revokeConsent(); // Initialize without consent
|
try {
|
||||||
loadMetaPixel(); // Load the Meta Pixel script
|
await loadMetaPixel(); // Load the Meta Pixel script
|
||||||
|
} catch (e) {
|
||||||
|
log.warn('Failed to load Meta Pixel script, all events will be queued.', e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this._baseLoaded = true;
|
this._baseLoaded = true;
|
||||||
log.debug('Meta Pixel base script loaded.');
|
log.debug('Meta Pixel base script loaded.');
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
|
import log from 'loglevel';
|
||||||
|
|
||||||
const SCRIPT_SRC = 'https://connect.facebook.net/en_US/fbevents.js';
|
const SCRIPT_SRC = 'https://connect.facebook.net/en_US/fbevents.js';
|
||||||
|
|
||||||
@@ -18,12 +19,24 @@ type QueuedFBQ = ((...args: unknown[]) => void) & {
|
|||||||
export const loadMetaPixel = (): Promise<void> => {
|
export const loadMetaPixel = (): Promise<void> => {
|
||||||
// Make sure we're using the browser
|
// Make sure we're using the browser
|
||||||
if (!browser || !window) {
|
if (!browser || !window) {
|
||||||
return Promise.reject(new Error('Window is undefined'));
|
return Promise.reject(new Error(`Not in browser, can't access window`));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If fbq is already defined, resolve immediately
|
// If fbq is already defined, resolve immediately
|
||||||
const existing = window.fbq as QueuedFBQ | undefined;
|
const existing = window.fbq as QueuedFBQ | undefined;
|
||||||
if (existing && existing.loaded) {
|
if (existing) {
|
||||||
|
const existingScript = getExistingScript();
|
||||||
|
if (existingScript) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
attachToScript(existingScript, resolve, reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
'Meta Pixel fbq already present, skipping injection',
|
||||||
|
existing.version,
|
||||||
|
existing.queue
|
||||||
|
);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,17 +54,14 @@ export const loadMetaPixel = (): Promise<void> => {
|
|||||||
q.loaded = true;
|
q.loaded = true;
|
||||||
q.version = '2.0';
|
q.version = '2.0';
|
||||||
window.fbq = q;
|
window.fbq = q;
|
||||||
|
window._fbq = q;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Avoid adding the same script twice
|
// Avoid adding the same script twice
|
||||||
const existingScript = document.querySelector(
|
const existingScript = getExistingScript();
|
||||||
`script[src="${SCRIPT_SRC}"]`
|
|
||||||
) as HTMLScriptElement | null;
|
|
||||||
if (existingScript) {
|
if (existingScript) {
|
||||||
existingScript.addEventListener('load', () => resolve());
|
attachToScript(existingScript, resolve, reject);
|
||||||
existingScript.addEventListener('error', () =>
|
log.debug('Meta Pixel script already present, waiting for load');
|
||||||
reject(new Error('Failed to load Meta Pixel script'))
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,8 +69,23 @@ export const loadMetaPixel = (): Promise<void> => {
|
|||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.src = SCRIPT_SRC;
|
script.src = SCRIPT_SRC;
|
||||||
script.async = true;
|
script.async = true;
|
||||||
script.addEventListener('load', () => resolve());
|
attachToScript(script, resolve, reject);
|
||||||
script.addEventListener('error', () => reject(new Error('Failed to load Meta Pixel script')));
|
|
||||||
document.head.appendChild(script);
|
document.head.appendChild(script);
|
||||||
|
log.debug('Meta Pixel script added to document');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getExistingScript = (): HTMLScriptElement | null => {
|
||||||
|
return document.querySelector(
|
||||||
|
`script[src*="connect.facebook.net"][src*="fbevents.js"]`
|
||||||
|
) as HTMLScriptElement | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const attachToScript = (
|
||||||
|
el: HTMLScriptElement,
|
||||||
|
resolve: () => void,
|
||||||
|
reject: (err: Error) => void
|
||||||
|
) => {
|
||||||
|
el.addEventListener('load', () => resolve());
|
||||||
|
el.addEventListener('error', () => reject(new Error('Failed to load Meta Pixel script')));
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user