29

I need to trigger some JavaScript after DFP has finished rendering all the ads on the page - or at least when it has triggered collapseEmptyDivs (which hides ad units that contain no line items).

Is anyone aware of a way to get DFP to trigger a callback after either of these events?

Sp4cecat
  • 961
  • 1
  • 8
  • 18
  • Which direction did you end up going with this? Assuming the problem was that you were trying to prevent the flash of empty ad space caused by collapseEmptyDivs? – Brian Wigginton Jul 29 '13 at 18:40
  • I found a solution to the problem I stated previously: http://stackoverflow.com/a/17931853/105061 – Brian Wigginton Jul 29 '13 at 18:50

6 Answers6

65

The GPT API now has a callback that is triggered after each slot is filled.

For example:

googletag.pubads().addEventListener('slotRenderEnded', function(event) {
 console.log('Creative with id: ' + event.creativeId +
  ' is rendered to slot of size: ' + event.size[0] + 'x' + event.size[1]);
});

See https://developers.google.com/doubleclick-gpt/reference#googletag.events.SlotRenderEndedEvent

z12345
  • 2,076
  • 4
  • 20
  • 27
  • 3
    One thing to note, slotRenderEnded fires once the creative returned from DFP/AdX has been written into the page. If that creative has additional resources in it, they likely will not be rendered yet. – mmilleruva May 23 '18 at 13:47
  • @mmilleruva or anyone is there a way to find out when all the creative has loaded? I'm guessing no but just looking for confirmation – Scott Leonard Aug 17 '20 at 16:13
20

I hacked googletag's debug_log.log function and pushed it through jQuery to fire events on a lot of DFP's actions. The hack does require jQuery.

https://github.com/mcountis/dfp-events

  • gpt-google_js_loaded
  • gpt-gpt_fetch
  • gpt-gpt_fetched
  • gpt-page_load_complete
  • gpt-queue_start
  • gpt-service_add_slot
  • gpt-service_add_targeting
  • gpt-service_collapse_containers_enable
  • gpt-service_create
  • gpt-service_single_request_mode_enable
  • gpt-slot_create
  • gpt-slot_add_targeting
  • gpt-slot_fill
  • gpt-slot_fetch
  • gpt-slot_receiving
  • gpt-slot_render_delay
  • gpt-slot_rendering
  • gpt-slot_rendered
Countis
  • 209
  • 2
  • 4
  • The above implementation where you are checking if the ad does not exists by checking the children of the first iframe that is rendered might not work everytime. For e.g a 3rd party ad like sharethrough does not renders ad in iframe but as a simple div container. The above implementation would fail in that case. Check here to see a sharethrough and see how it is rendered.http://imm.io/1kPqP – agaase Nov 25 '13 at 07:42
  • 1
    Nice code. I wish there were some comments though explaining what exactly you are doing to get these to fire. The code seems simple enough, but I don't fully understand where these ID numbers come in. I assume that's just how they are compiled in DFP's minified output? If so, how do you stay on top of changes? – Brad Feb 26 '14 at 17:20
9

Load script in the part of your page:

// set global variable if not already set
var googletag = googletag || {};
googletag.cmd = googletag.cmd || [];

// load asynchronously the GPT JavaScript library used by DFP,
// using SSL/HTTPS if necessary
(function() {
  var gads   = document.createElement('script');
  gads.async = true;
  gads.type  = 'text/javascript';

  var useSSL = 'https:' === document.location.protocol;
  gads.src = (useSSL ? 'https:' : 'http:') + '//www.googletagservices.com/tag/js/gpt.js';
  var node =document.getElementsByTagName('script')[0];
  node.parentNode.insertBefore(gads, node);
})();

Initialise google publisher tag with the following script, preferable as well in the section of your page:

// can be moved as well in the body
// if using async mode, wrap all the javascript into googletag.cmd.push!
googletag.cmd.push(function() {
  // set page-level attributes for ad slots that serve AdSense
  googletag.pubads().set("adsense_background_color", "FFFFFF");
  googletag.pubads().setTargeting("topic","basketball");

  // enables Single Request Architecture (SRA)
  googletag.pubads().enableSingleRequest();

  // Disable initial load, we will use refresh() to fetch ads.
  // Calling this function means that display() calls just
  // register the slot as ready, but do not fetch ads for it.
  googletag.pubads().disableInitialLoad();

  // Collapses empty div elements on a page when there is no ad content to display.
  googletag.pubads().collapseEmptyDivs();

  // Enables all GPT services that have been defined for ad slots on the page.
  googletag.enableServices();
});

Register slots individually (can be generated with a foreach loop) and render them. The event listener can be registered as well per slot. Here the important part: make sure that you refresh them together to avoid ending up with the same ads on both slots (if the ad is assign to both slots) => googletag.pubads().refresh([slot1, slot2]]);

// this code can be moved externally to improve performance
googletag.cmd.push(function() {
  // define slot1
  slot1 = googletag.defineSlot(
    "/1234/travel/asia/food",
    [728, 90],
    "banner1"
  )
  .addService(googletag.pubads())
  .setTargeting(
    "interests",
    ["sports", "music", "movies"]
  );
  // prerender the slot but don't display it because of disableInitialLoad()
  googletag.display("banner1");

  // define slot2    
  slot2 = googletag.defineSlot(
    "/1234/travel/asia/food",
    [[468, 60], [728, 90], [300, 250]],
    "banner2"
  )
  .addService(googletag.pubads())
  .setTargeting("gender", "male")
  .setTargeting("age", "20-30");

  // prerender the slot but don't display it because of disableInitialLoad()
  googletag.display("banner2");  


  // add event to sign the slot as redered or not
  googletag.pubads().addEventListener('slotRenderEnded', function(event) {
    if (event.slot === slot1 || event.slot === slot2) {
      // do something related to the slot
    }
  });

  // refresh all container ads and show them
  // very important to call refresh with an array to avoid 
  // multiple callback to the registered event 
  googletag.pubads().refresh([slot1, slot2]);
});
<div id="banner1" style="width:300px; height:250px;"></div>

<div id="banner2" style="width:300px; height:250px;"></div>

After the ad has been rendered, the callback gets triggered.

For more information have a look on this file: https://github.com/davidecantoni/googletag

Spidi
  • 138
  • 2
  • 5
1

I'm pretty sure that DFP doesn't provide for a callback after the ad has rendered. I have used the following code to do this. It calls the callback function after one of the following has happened:

-The ad has loaded and the iframe has rendered

-No ad was loaded, and the ad unit was hidden by collapseEmptyDivs()

-A certain amount of time has passed (in this case, 2 seconds) with neither one happening. Like if there was some sort of network error connecting to DFP.

adId would be the id of your ad container

assumes you are using jQuery

function callback() {
   //your callback function - do something here
}

function getIframeHtml(iframe) {
   if(iframe.contentWindow && iframe.contentWindow.document && iframe.contentWindow.document.body && iframe.contentWindow.document.body.innerHTML) {
       return iframe.contentWindow.document.body.innerHTML;
   }
   return null;
}

var dfpNumChecks = 20;
var dfpCheckCount = 0;
var dfpCheckTimer = 100;

function dfpLoadCheck(adId) {
   var nodes = document.getElementById(adId).childNodes;

   if(nodes.length && nodes[0].nodeName.toLowerCase() == 'iframe' && getIframeHtml(nodes[0])) {
       //the iframe for the ad has rendered
       callback();
       return;
   } else if($('#' + adId).css('display') == 'none' || (dfpCheckCount >= dfpNumChecks)) {
       //the ad unit has been hidden by collapseEmptyDivs()
       callback();
       return;
   } else {
       dfpCheckCount++;
       setTimeout(function() { dfpLoadCheck(adId) }, dfpCheckTimer); 
   }
} 
alexp
  • 3,229
  • 2
  • 25
  • 34
1

Check out the jQuery DFP extension I'm working on... it is still a bit of a work in progress but provides the callback you are after.

An example of how to use it is in this file.

You will see two callbacks are available... after each ad has loaded and after all ads have loaded. A class is also set on the ad unit container element, this can either be display-none (when no ad is found), display-block (when an ad is found) or display-original (for when no ad is found but the container div contained content to begin with, I use this for over riding certain parts of sites with ad content when required). These classes are of course useful to work with once inside the callback.

Matt Cooper
  • 7,992
  • 1
  • 28
  • 26
1

If you need to identify the render end of a specific slot ( useful if you are using same creative for multiple slots ) you can do the following

googleAd =  googletag.defineSlot('/xxxxx/web_top_AAAxAAA', [xxx, xxx], 'div-id').addService(googletag.pubads());    


googletag.pubads().addEventListener('slotRenderEnded', function(event) {
                    if( event.slot.W == googleAd.W ){
                        // your code here
                    }
                });
Aman Verma
  • 320
  • 1
  • 11