Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait for Facebook Pixel Load

I want to detect in my JavaScript code that the load of a Facebook pixel has been completed. Is this possible?

For reference here is the Facebook Pixel Tracking code:

<script>
!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');
// Insert Your Facebook Pixel ID below. 
fbq('init', 'FB_PIXEL_ID');
fbq('track', 'PageView');
</script>
<!-- Insert Your Facebook Pixel ID below. --> 
<noscript><img height="1" width="1" style="display:none"
src="https://www.facebook.com/tr?id=FB_PIXEL_ID&amp;ev=PageView&amp;noscript=1"
/></noscript>

Breaking it down, it seems that fbq('init', ...) causes a script tag to be added with the async attribute set and src set to //connect.facebook.net/en_US/fbevents.js.

Subsequently the call to fbq('track', ...) somehow causes an HTTP GET to an image via one redirect.

How to detect that all the steps are complete, especially that the final image load is complete?

like image 608
user2297550 Avatar asked Jan 20 '17 02:01

user2297550


2 Answers

So Kelly Seldens solution looks great but didnt work for me due to facebook changing their library. My solution is derived from Kelly Seldens and is as follows (applicable to facebook 2.9.15). I offer it up for other people looking for solutions to firing off actions based on facebook pixel being loaded.

In my case, I have Mautic loading the fb pixel on its own. I needed a way to detect when this occured so that I could fire off a conversion event (fb - track lead).

The changes I made to Kelly's code reflects 2 things:

First I added in the condition checking if fbq is defined. Before doing this I was getting console errors that fbq was undefined each time the timeout executed.

Second, I removed the fbq.on statement simple because it doesnt exist anymore in the 2.9.15 version of the pixel being loaded by Mautic.

The final code I ended up with is:

(function wait() {
    //first check if the fbq object is defined, then check the version (probably not needed but for good measure

    if ((typeof fbq !== 'undefined') && (fbq.version > '2.9')) {
        //fire off the call back / tracking event
        myCallBack(); //fbq('track', 'Lead');       
    } 
else 
  setTimeout(wait, 10);
})();
like image 124
MediaGiantDesign Avatar answered Oct 22 '22 10:10

MediaGiantDesign


Since OP @user2297550 said his ultimate goal is to redirect a user after an event fires, I'll explain how that can be done (without timeouts or intervals). Some previous answers try to detect when the Facebook pixel <script> is done loading, but that's not the same as determining when an actual event is done firing. Presumably, OP want's to know when the PageView event is complete. This solution is not amazing for every use case, but it's pretty simple if we don't have much else happening on the page.

In order to ping their servers and track events, Facebook's code creates a new Image() and sets its src attribute to something like https://www.facebook.com/tr/?id=XXXXXX&ev=PageView&{more parameters}. I discovered this by examining the tracking library and finding this sendGET function:

this.sendGET = function(b, c, d) {
    b.replaceEntry("rqm", "GET");
    var f = b.toQueryString();
    f = i(c, d) + "?" + f;
    if (f.length < 2048) {
        var g = new Image();
        if (d != null) {
            var h = a.getShouldProxy();
            g.onerror = function() {
                a.setShouldProxy(!0), h || e.sendGET(b, c, d)
            }
        }
        g.src = f;
        return !0
    }
    return !1
};

We can hook into that image load by polyfilling a default Image.onload callback with our redirect code. The result is something like this, which could be placed immediately above the normal Facebook pixel code in your header:

OriginalImage = Image;
Image = function(){
  let oi = new OriginalImage();
  oi.onload = function() {
    // if the image that loaded was indeed a Facebook pixel
    // for a "PageView" event, redirect
    if( this.src.indexOf( 'facebook.com/tr/?id=XXXXXX&ev=PageView' ) != -1 )
      window.location = 'https://example.com/redirect-here';
  };
  return oi;
};

So a full "paint the user and redirect" page could look about like this:

<html>
<head>
<!-- Facebook Pixel Code -->
<script>
  OriginalImage = Image;
  Image = function(){
    let oi = new OriginalImage();
    oi.onload = function() {
      // if the image that loaded was indeed a Facebook pixel
      // for a "PageView" event, redirect
      if( this.src.indexOf( 'facebook.com/tr/?id=XXXXXX&ev=PageView' ) != -1 )
        window.location = 'https://example.com/redirect-here';
    };
    return oi;
  };

  !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',
  'https://connect.facebook.net/en_US/fbevents.js');
  fbq('init', 'XXXXXX');
  fbq('track', 'PageView');
</script>
<noscript>
  <img height="1" width="1" style="display:none" 
       src="https://www.facebook.com/tr?id=XXXXXX&ev=PageView&noscript=1"/>
</noscript>
<!-- End Facebook Pixel Code -->
</head>
<body></body>
</html>

Of course, you could skip the Javascript and literally just load the image from that <noscript> block and add an 'onload' attribute, like so:

<html>
<head>
<!-- Facebook Pixel Code -->
<img height="1" width="1" style="display:none" 
       src="https://www.facebook.com/tr?id=XXXXXX&ev=PageView&noscript=1"
       onload="window.location = 'https://example.com/redirect-here';"/>
<!-- End Facebook Pixel Code -->
</head>
<body></body>
</html>

But I would guess that plain image tracking degrades Facebook's ability to identify the user.

This strategy may work for more generic use cases where you want to detect when an arbitrary event is done being sent to Facebook. But it may be unwise to polyfill the onload callback for every single image on a normal page. Also know that Facebook could change their code at any time, breaking this.

like image 34
Dan Avatar answered Oct 22 '22 10:10

Dan