I have run into a bit a frustrating problem with duplicating the "PageView" event for my pixel in my Nuxt.js (Vue) project with my Facebook Conversion API.
I initially stripped out this packages code, so that I could modify to include a eventID for every "PageView" event: https://github.com/WilliamDASILVA/nuxt-facebook-pixel-module#readme
My "PageView" event workflow is like this:
- On Vue router "afterEach" hook, call the tracking function.
- When I call my tracking function, make a async call to my API, which will fire off the server Conversion API with the "PageView" event. The API call returns the exact same eventID, so we can duplicate on the FE.
- Call Facebooks Pixel track method with the given eventID.
Initial load works fine:
However, subsequent calls after a route change does not work. Both the server level event and the browser level event are being tracked, but not duplicated.
First thing I noticed was there was no eventID being set for the browser call. I spent hours ensuring that the correct function was being called to the facebook pixel in the browser with the eventID, it is.
I found https://gist.github.com/sunderls/dfd5293a8b8f24a4ef37189a1d8c1b46 online, and began to try to figure out what was happening. It looks like an event handler is automatically added to the pixel when the replacestate/pushstate of the browsers history is called. This would explain why no eventID is being passed along, it's firing automatically.
Looking through Facebooks documentation I found that I could set:
fbq.disablePushState = true
Which would then stop the pixel from adding those event listeners. However, when I add this line of code above, I do not receive any subsequent "PageView" events after the initial page load, only server events are coming through.
Here is my plugin source code:
import { Minimatch } from "minimatch"
import axios from "axios"
import cookie from "js-cookie"
/**
* @class Fb
*/
class Fb {
constructor (fbq, options, eventUrl) {
this.options = options
this.fbq = fbq
this.eventUrl = eventUrl
this.isEnabled = !options.disabled
}
setPixelId (pixelId) {
this.options.pixelId = pixelId
this.init()
}
/**
* @method enable
*/
enable () {
this.isEnabled = true
this.init()
this.track()
}
/**
* @method disable
*/
disable () {
this.isEnabled = false
}
/**
* @method init
*/
init () {
this.query('init', this.options.pixelId)
}
/**
* @method track
*/
async track (event = null, parameters = null, eventId = null) {
if (!event) {
event = this.options.track
}
if (this.eventUrl && !eventId) {
if (cookie.get("auth._token.cookie")) {
axios.defaults.headers.common["Authorization"] = cookie.get("auth._token.cookie");
}
try {
let url = this.eventUrl
const queryString = []
if (event === "PageView") {
queryString.push("url=" + window.location.href.split('?')[0])
queryString.push("view=true")
}
if (cookie.get("_fbp")) {
queryString.push(`_fbp=${cookie.get("_fbp")}`)
}
if (cookie.get("_fbc")) {
queryString.push(`_fbc=${cookie.get("_fbc")}`)
}
if (queryString.length !== 0) {
url += "?" + queryString.join("&")
}
const {data: {data}} = await axios.get(url)
eventId = data.event_id
} catch (err) {
console.error(err)
}
}
this.query('track', event, parameters, eventId)
}
/**
* @method query
* @param {string} cmd
* @param {object} option
* @param {object} parameters
*/
query (cmd, option, parameters = null, eventId = null) {
if (this.options.debug) log('Command:', cmd, 'Option:', option, 'Additional parameters:', parameters, 'Event Id:', eventId)
if (!this.isEnabled) return
if (!parameters && !eventId) {
this.fbq(cmd, option)
} else if (parameters && !eventId) {
this.fbq(cmd, option, parameters)
} else {
this.fbq(cmd, option, {}, {eventID: eventId})
}
}
}
function getMatchingPixel (options, path) {
return options.pixels.find(pixel => {
const routeIndex = pixel.routes.findIndex(route => {
const minimatch = new Minimatch(route)
return minimatch.match(path)
})
return routeIndex !== -1
})
}
function log (...messages) {
console.info.apply(this, ['[nuxt-facebook-pixel-module]', ...messages])
}
export default (ctx, inject) => {
let _fbq
const parsedOptions = <%= JSON.stringify(options) %>
const isDev = parsedOptions.dev && !parsedOptions.debug
if (isDev) log('You are running in development mode. Set "debug: true" in your nuxt.config.js if you would like to trigger tracking events in local.')
const { path } = ctx.route
const matchingPixel = getMatchingPixel(parsedOptions, path)
const pixelOptions = Object.assign({}, matchingPixel || parsedOptions)
/* eslint-disable */
if (typeof window !== 'undefined') {
((f, b, e, v, n, t, s) => {
if (f.fbq) return; n = f.fbq = function () {
n.callMethod ?
n.callMethod.apply(n, arguments) : n.queue.push(arguments)
};
if (!f._fbq) f._fbq = n; n.push = n; n.loaded = !0; n.version = pixelOptions.version;
n.queue = [];
t = b.createElement(e);
t.async = true;
t.defer = true;
t.src = v;
s = b.getElementsByTagName('body')[0];
s.parentNode.appendChild(t, s);
_fbq = fbq;
if (!isDev && !pixelOptions.disabled) {
if (pixelOptions.manualMode) {
fbq('set', 'autoConfig', false, pixelOptions.pixelId)
}
// fbq.disablePushState = true
fbq('init', pixelOptions.pixelId)
// fbq('track', pixelOptions.track)
}
})(window, document, 'script', 'https://connect.facebook.net/en_US/fbevents.js');
}
/* eslint-enable */
const instance = new Fb(_fbq, pixelOptions, parsedOptions.eventUrl)
if (ctx.app && ctx.app.router) {
const router = ctx.app.router
router.afterEach(({ path }) => {
/**
* Change the current pixelId according to the route.
*/
const matchingPixel = getMatchingPixel(parsedOptions, path)
const pixelOptions = Object.assign({}, matchingPixel || parsedOptions)
if (pixelOptions.pixelId !== instance.options.pixelId) {
instance.setPixelId(pixelOptions.pixelId)
}
/**
* Automatically track PageView
*/
if (parsedOptions.autoPageView) {
instance.track('PageView')
}
})
}
/* eslint-enable */
ctx.$fb = instance
inject('fb', instance)
}
All settings are correctly set for the plugin to work. I am not using multiple Facebook Pixels.
I would greatly appreciate help in figuring this out.

