摘自: https://washamdev.com/multiple-facebook-pixels-on-one-site/
<
iframe src=\”https://washamdev.com/multiple-facebook-pixels-on-one-site/\” class=\”iframe-full-content\”>
(adsbygoogle = window.adsbygoogle || []).push({});
How to Use Multiple Facebook Pixels on One Site
This was a frustrating problem! Before late 2017, Facebook didn’t provide any real documentation about using more than one pixel on a single site. This is hard to believe, as I imagine many businesses work with multiple digital marketing vendors who provide Facebook ad services. From the looks of the many unanswered questions in Facebook’s help forum about this, it’s obvious many were frustrated along with me.
When I originally worked on this problem in late summer 2017, doing a Google search for “facebook multiple pixels on one site” found results from Facebook’s help forums and StackOverflow, but none had a full discussion of the possible options and what works or doesn’t.
I later discovered a blog post by Facebook called “Accurate Event Tracking with Multiple Pixels.” It goes into detail on how to track pixel events to specific pixel IDs using the fbq
actions trackSingle
and trackSingleCustom
. While those functions do track pixel events to only a single pixel ID, rather than all that have been init
ed on the site, the problem still remains that any other developer who doesn’t use those functions will track all their pixel events to your pixel, whether you want them or not.
Let’s get into this issue with a deeper discussion…
Discussion
I highly recommend you first install the Facebook Pixel Helper. It helps diagnose problems and allows you to see what’s going on under the hood.
I had two important objectives for this project:
- Include multiple Facebook pixels on one website and be able to track activity, engagement, and conversions on that site to them.
- Ability to fire my own events to my pixel ID without anyone else being able to track data to my pixel ID.
The first thing you might try is simply adding the two full Facebook Pixel code snippets to the site. Don’t do that! Only include one full snippet, then add additional fbq(\'init\', \'[your-pixel-id]\')
calls for each additional pixel ID you want to install. It would look something like this:
!function(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=\'2.0\';n.queue=[];t=b.createElement(e);t.async=!0; t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window, document,\'script\',\'//connect.facebook.net/en_US/fbevents.js\'); fbq(\'init\', \'[first-pixel-id]\'); fbq(\'init\', \'[second-pixel-id]\'); fbq(\'track\', \'PageView\'); //tracks a PageView to both initialized pixels![]()
![]()
If you init
your pixel IDs like this, any future calls using the basic track
action, like fbq(\'track\', \'PageView\')
, will send an event for each initialized pixel ID (see screenshot below).
But what we really want to do is only track our pixel events to our own pixel, not everyone else’s too. That’s where Facebook’s new trackSingle
and trackSingleCustom
actions come in. Instead of calling fbq(\'track\', \'PageView\')
, simply call fbq(\'trackSingle\', \'[your-pixel-id]\', \'PageView\')
instead.
If #1 is your only objective, you could stop here. You’ve found your solution!
But for me, the second objective was not met to my satisfaction. From what I’ve seen, most developers don’t use the trackSingle
or trackSingleCustom
actions yet, which means every event they track will also flow into my pixel. This isn’t the end of the world, but that data isn’t mine, and I don’t want it getting into my pixel. So if I init
my pixel ID on the site, it will receive any events they fire that use the basic track
action. In other words, with these new single track actions, I can ensure my pixel events only track to my pixel ID, but I can’t do anything about other developers who track to all initialized pixel IDs on the site. Argh!
Let’s discuss a specific scenario.
The company I work for provides a service for Facebook Dynamic Ads, so we have our own Product IDs in our Product Catalog that need to match the Product IDs on our clients’ websites. But our Product ID may not be the same as other companies’, so we need to be able to specify our own and send those along with only our pixel events.
For instance, let’s say you land on a client’s product page for a t-shirt, and that client works with two different digital marketing companies for Facebook Dynamic Ads. The first company identifies products by their own unique product ID (e.g. “tshirt_1022”). The second company identifies products by the UPC (e.g. “892185001003”). If both their pixels are initiated, the first company may include a call like this:
fbq(\'track\', \'ViewContent\', { content_type: \'product\', content_ids: [\'tshirt_1022\'] });
The obvious problem is that the Facebook pixel will send the track event to both Pixel IDs, but only the first will match a product ID, because my company’s product ID is “892185001003”, not “tshirt_1022”. You will see something like this in the Pixel Helper:
If both developers use the trackSingle action, then we’re golden. Objective #2 is met! But you have to trust that all other developers will use the new trackSingle action and not the track action.
Progress
All right, we’ve now identified the problem, so how do we go about solving it?
I originally spent hours searching and testing on the Facebook pixel’s fbq
object, but I couldn’t find any way to send a pixel event to only one pixel. Thankfully, since then, they have introduced the new track actions, which work perfectly, if every developer on the site uses them. But since I want to track only my events to my pixel ID and not have anyone else’s events getting into my pixel data, I had to continue working.
The first part of my eventual solution came from the recognition that Facebook’s pixel code includes two methods to initiate your pixel and track events on a site: JavaScript and non-JavaScript. Facebook’s example pixel code looks like this:
!function(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=\'2.0\';n.queue=[];t=b.createElement(e);t.async=!0; t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window, document,\'script\',\'//connect.facebook.net/en_US/fbevents.js\'); fbq(\'init\', \'FB_PIXEL_ID\'); fbq(\'track\', \'PageView\');
Note the
tag in the section. If the user doesn’t have JavaScript enabled, the browser will send the event data via an image request to Facebook. Using this method, you can pass a single pixel ID, and the event is tracked exclusively to my pixel. Hazah! So what we need to do is dynamically create an
tag like we see in that example code.
Solution
In my Googling, I came across an answer on StackOverflow that employed this idea of dynamically creating an
tag to send the event data, so I used it as a foundation. Credit goes to the user Flavio Wuensche for the basis of my function below!
I’ll include my code, then we can discuss what it does:
(function (window, document) { if (window.myfbq) return; window.myfbq = (function () { if (arguments.length > 0) { var pixelId, trackType, contentObj; if (typeof arguments[0] == \'string\') pixelId = arguments[0]; if (typeof arguments[1] == \'string\') trackType = arguments[1]; if (typeof arguments[2] == \'object\') contentObj = arguments[2]; var params = []; if (typeof pixelId === \'string\' && pixelId.replace(/\\s+/gi, \'\') != \'\' && typeof trackType === \'string\' && trackType.replace(/\\s+/gi, \'\')) { params.push(\'id=\' + encodeURIComponent(pixelId)); switch (trackType) { case \'PageView\': case \'ViewContent\': case \'Search\': case \'AddToCart\': case \'InitiateCheckout\': case \'AddPaymentInfo\': case \'Lead\': case \'CompleteRegistration\': case \'Purchase\': case \'AddToWishlist\': params.push(\'ev=\' + encodeURIComponent(trackType)); break; default: return; } params.push(\'dl=\' + encodeURIComponent(document.location.href)); if (document.referrer) params.push(\'rl=\' + encodeURIComponent(document.referrer)); params.push(\'if=false\'); params.push(\'ts=\' + new Date().getTime()); if (typeof contentObj == \'object\') { for (var u in contentObj) { if (typeof contentObj[u] == \'object\' && contentObj[u] instanceof Array) { if (contentObj[u].length > 0) { for (var y = 0; y < contentObj[u].length; y++) { contentObj[u][y] = (contentObj[u][y] + \'\').replace(/^\\s+|\\s+$/gi, \'\').replace(/\\s+/gi, \' \').replace(/,/gi, \'§\'); } params.push(\'cd[\' + u + \']=\' + encodeURIComponent(contentObj[u].join(\',\').replace(/^/gi, \'[\\\'\').replace(/$/gi, \'\\\']\').replace(/,/gi, \'\\\',\\\'\').replace(/§/gi, \'\\,\'))); } } else if (typeof contentObj[u] == \'string\') params.push(\'cd[\' + u + \']=\' + encodeURIComponent(contentObj[u])); } } params.push(\'v=\' + encodeURIComponent(\'2.8.12\')); var imgId = new Date().getTime(); var img = document.createElement(\'img\'); img.id = \'fb_\' + imgId, img.src = \'https://www.facebook.com/tr/?\' + params.join(\'&\'), img.width = 1, img.height = 1, img.style = \'display:none;\'; document.body.appendChild(img); window.setTimeout(function () { var t = document.getElementById(\'fb_\' + imgId); t.parentElement.removeChild(t); }, 1000); } } }); })(window, document);
This attaches a new function called myfbq
to the global window object, so you can use it from anywhere in your JavaScript code. Note that this function has no association with and makes no calls to Facebook’s fbq
object.
Near the top of the function, there are some checks to make sure there is a pixel ID and event type specified. If those exist, and the event type is valid, it begins building an array of parameters that need to be appended to the image request URL. Flavio included code to build the custom parameters, if you need to define those.
Lastly, the function creates an image element, defines its attributes, then appends it to the body. After one second, the image element will be removed from the page (that’s not necessary, but just a matter of housekeeping).
Usage
The function accepts 2-3 arguments, the first of which is your pixel ID:
myfbq(\"[your-pixel-id]\", \"PageView\"); myfbq(\"[your-pixel-id]\", \"ViewContent\"); myfbq(\"[your-pixel-id]\", \"ViewContent\", { content_type: \"product\", content_ids: [\'892185001003\'] });
Wrapping It Up
That’s it! When you browse to your site, open up the Facebook Pixel Helper and confirm that the pixel event is being sent to Facebook with the correct data.
One final note is that if you use this function, you won’t need to include your Facebook’s pixel code snippet on your site. If you call fbq(\'init\', \'[your-pixel-id]\')
on the site, remember that all events tracked with the basic track
action will also be sent for your pixel ID, and that’s what we want to avoid. Always use trackSingle
or trackSingleCustom
for your own events using the fbq
function. And if you want your events to only track to your pixel ID, just include the myfbq
function and make calls to it with your pixel ID.
Your Turn!
Have you run into this problem before? What solution did you come to? I’d love to hear your questions or experiences, so let’s discuss in the comments below!
window.onload = function(){prettyPrint();};
<div style=\"display:none\">
</div>
<script type=\"text/javascript\">
window.WPCOM_sharing_counts = {\"https:\\/\\/washamdev.com\\/multiple-facebook-pixels-on-one-site\\/\":2949};
</script><div id=\"sharing_email\" style=\"display: none;\">
<form action=\"/multiple-facebook-pixels-on-one-site/\" method=\"post\">
<label for=\"target_email\">Send to Email Address</label>
<input type=\"email\" name=\"target_email\" id=\"target_email\" value=\"\"><label for=\"source_name\">Your Name</label>
<input type=\"text\" name=\"source_name\" id=\"source_name\" value=\"\"><label for=\"source_email\">Your Email Address</label>
<input type=\"email\" name=\"source_email\" id=\"source_email\" value=\"\"><input type=\"text\" id=\"jetpack-source_f_name\" name=\"source_f_name\" class=\"input\" value=\"\" size=\"25\" autocomplete=\"off\" title=\"This field is for validation and should not be changed\"><img style=\"float: right; display: none\" class=\"loading\" src=\"https://washamdev.com/wp-content/plugins/jetpack/modules/sharedaddy/images/loading.gif\" alt=\"loading\" width=\"16\" height=\"16\"><input type=\"submit\" value=\"Send Email\" class=\"sharing_send\"><a rel=\"nofollow\" href=\"#cancel\" class=\"sharing_cancel\">Cancel</a>
<div class=\"errors errors-1\" style=\"display: none;\">
Post was not sent - check your email addresses! </div>
<div class=\"errors errors-2\" style=\"display: none;\">
Email check failed, please try again </div>
<div class=\"errors errors-3\" style=\"display: none;\">
Sorry, your blog cannot share posts by email. </div>
</form>
</div>
/* */
/* */
/* */
/* */
/* */
var windowOpen;
jQuery( document.body ).on( \’click\’, \’a.share-facebook\’, function() {
// If there\’s another sharing window open, close it.
if ( \’undefined\’ !== typeof windowOpen ) {
windowOpen.close();
}
windowOpen = window.open( jQuery( this ).attr( \’href\’ ), \’wpcomfacebook\’, \’menubar=1,resizable=1,width=600,height=400\’ );
return false;
});
var windowOpen;
jQuery( document.body ).on( \’click\’, \’a.share-twitter\’, function() {
// If there\’s another sharing window open, close it.
if ( \’undefined\’ !== typeof windowOpen ) {
windowOpen.close();
}
windowOpen = window.open( jQuery( this ).attr( \’href\’ ), \’wpcomtwitter\’, \’menubar=1,resizable=1,width=600,height=350\’ );
return false;
});
var windowOpen;
jQuery( document.body ).on( \’click\’, \’a.share-google-plus-1\’, function() {
// If there\’s another sharing window open, close it.
if ( \’undefined\’ !== typeof windowOpen ) {
windowOpen.close();
}
windowOpen = window.open( jQuery( this ).attr( \’href\’ ), \’wpcomgoogle-plus-1\’, \’menubar=1,resizable=1,width=480,height=550\’ );
return false;
});
var windowOpen;
jQuery( document.body ).on( \’click\’, \’a.share-tumblr\’, function() {
// If there\’s another sharing window open, close it.
if ( \’undefined\’ !== typeof windowOpen ) {
windowOpen.close();
}
windowOpen = window.open( jQuery( this ).attr( \’href\’ ), \’wpcomtumblr\’, \’menubar=1,resizable=1,width=450,height=450\’ );
return false;
});
;(function($, window) {
\’use strict\’
var WD = {};
window.WD = WD;
if( $(\’.more-link\’).length ) {
$(\’.more-link\’).text(\’Read More\’);
$(\’.more-link\’).wrap(\’
}
function throttle(n,t,i){t||(t=250);var r,u;return function(){var e=i||this,f=+new Date,o=arguments;r&&f=0&&e 0) {
var trackedScrollTo1000 = false;
var trackedScrollToFooter = false;
$(function () {
$(window).on(\”scroll\”, function() {
if (typeof ga !== \”undefined\” && $(document).height() > 1200) {
if (!trackedScrollTo1000 && $(this).scrollTop() > 1000) {
trackedScrollTo1000 = true;
ga(\’send\’, \’event\’, \’post\’, \’scrolled past 1000px\’);
}
if (!trackedScrollToFooter && isElementScrolledIntoView(document.getElementsByClassName(\”entry-footer\”)[0])) {
trackedScrollToFooter = true;
ga(\’send\’, \’event\’, \’post\’, \’scrolled to footer\’);
}
}
});
});
}
//add thumbnail class to featured image of \”Fetch as Google\” post
if ($(\”#post-3182\”).length > 0) {
$(\”.entry-thumb\”).addClass(\”thumbnail\”);
}
//cookie message
WD.setCookie = function (name, value, days) {
var d = new Date;
d.setTime(d.getTime() + 24*60*60*1000*days);
document.cookie = name + \”=\” + value + \”;path=/;expires=\” + d.toGMTString();
}
var v = document.cookie.match(\'(^|;) ?acceptedCookiePolicy=([^;]*)(;|$)\’);
if (v === null) {
$(\”body\”).append(\’