Forum Moderators: martinibuster

Message Too Old, No Replies

Google Ad Manager, homemade fallback code

final code using GAM for fallback vs. Adsense for fallback

         

csdude55

9:12 pm on Aug 3, 2023 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



For YEARS I've read that one can use Google Ad Manager to create fallbacks and to make ads compete against one another, but no one has ever been able to tell me exactly how to do that :-/

I recently signed up with Ad Plus, and they do have a higher RPM! But with a very low fill rate, maybe 10%. I never could figure out how to set up GAM to fall back to my own account when Ad Plus has nothing, so I finally gave up and rolled my own.

I'm sharing the code here, hoping I can help others to not waste a month on it!

For my purposes, I surround each ad unit with <div id="block_[0-9]">...</div>; eg, the first unit is block_0, the second is block_1, and so on.

This first script worked to use my own GAM account as a fall back, but revenue was LOW so I don't know that it worked exactly right. When I expected 10,000 impressions I would get more like 1,000, and revenue was less than $0.25 /day.

window.googletag = window.googletag || {cmd: []};

var i, m, unit,
// 12345 = Ad Plus GAM ID
// 6789 = my GAM ID
gamID = ['12345', '6789'],

gFrame = [],
slotArr = [],
fallback = [],

unitArr = {};

unitArr.slot = {};
unitArr.slot.block = [];
unitArr.slot.backup = [];

// size options
unitArr.size = {};
unitArr.size.block = [];
unitArr.size.backup = [];

// optional, let's you specify a channel for reporting
unitArr.channel = {};
unitArr.channel.block = [];
unitArr.channel.backup = [];

// optional, if you want to control how each unit collapses
// you don't want it to collapse if you want a fall back to show up
unitArr.collapse = {};
// true = collapse if no ad is returned
unitArr.collapse.block = [];
unitArr.collapse.backup = [];

// true = collapse before an ad is fetched; ignored if above is not true
unitArr.collapseB.block = [];
unitArr.collapseB.backup = [];

// place the name of each container ID in the array
// I chose to do it this way so that I wouldn't have to repeat the word "block", and
// it let me define the order
for (m of [5, 2, ...]) {
switch (m) {
case 5:
unitArr.slot.block[m] = 'Ad.Plus-Leaderboard';
unitArr.slot.backup[m] = 'Leaderboard_Adsense';

unitArr.size.block[m] =
unitArr.size.backup[m] = ['fluid'];

unitArr.channel.block[m] =
unitArr.channel.backup[m] = 6635170156;

break;

case 2:
unitArr.slot.block[m] = 'Ad.Plus-300x250';
unitArr.slot.backup[m] = '300x250_ATF_Adsense';

unitArr.size.block[m] =
unitArr.size.backup[m] = [300, 250];

unitArr.channel.block[m] =
unitArr.channel.backup[m] = 7349948984;

unitArr.collapse.block[m] =
unitArr.collapse.backup[m] = true;

break;

// and so on
}
}

// Primary
googletag.cmd.push(function() {

// not sure if this really matters, but I kept reading that it will help revenue if the fall back is
// in an iFrame. I'm writing it in PHP, of course, you just want it to be the URL of the page
googletag.pubads().set('page_url', "{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}");

for (unit in unitArr.slot.block) {
if (unitArr.slot.block[unit] && document.getElementById('block_' + unit)) {

// if optional fields aren't given, default to "false"
unitArr.size.block[unit] ?= false;
unitArr.channel.block[unit] ?= false;
unitArr.collapse.block[unit] ?= false;

slotArr[unit] =
googletag.defineSlot('/' + gamID[0] + ',' + gamID[1] + '/' + unitArr.slot.block[unit],
unitArr.size.block[unit],
'block_' + unit)
.setCollapseEmptyDiv(unitArr.collapse.block[unit], unitArr.collapseB.block[unit])
.addService(googletag.pubads());

if (unitArr.channel.block[unit])
googletag.slotArr[unit].set('adsense_channel_ids', unitArr.channel.block[unit]);
}
}

googletag.pubads().enableSingleRequest();
googletag.enableServices();
});

// Fall back; checks every 0.5 seconds to see if document is compete before running
var interval = setInterval(() => {
if (document.readyState === 'complete') {
clearInterval(interval);

for (unit of [5, 2, ...]) {
if ((i = document.getElementById('block_' + unit)) &&
i.style.display !== 'none' &&

// hidden elements have a width of 0
i.clientWidth > 0) {

// look for elements created inside of the ad unit
for (m in i.childNodes)
if (gFrame[unit] = i.childNodes[m].id)
break;

if (
// measure the unit you created
i.clientHeight < 50 ||

// measure the 3rd party element inside of the unit you created
(
gFrame[unit] &&
(
document.getElementById(gFrame[unit]).clientWidth > 0 ||
document.getElementById(gFrame[unit]).innerHTML == ''
)
)
) {
// ad wasn't filled, let's clear it and try again with our own GAM
googletag.cmd.push(function() {
googletag.pubads().clear([slotArr[unit]]);

unitArr.size.backup[unit] ?= false;
unitArr.channel.backup[unit] ?= false;
unitArr.collapse.backup[unit] ?= false;

slotArr[unit] =
googletag.defineSlot('/' + gamID[1] + '/' + unitArr.slot.backup[unit],
unitArr.size.backup[unit],
'backup_' + unit)
.setCollapseEmptyDiv(unitArr.collapse.backup[unit])
.addService(googletag.pubads()
.set('page_url', newloc)
);

// this throws an error on using slotArr[unit] as the name and I'm not sure how to fix it
// if (unitArr.channel.block[unit])
// googletag.slotArr[unit].set('adsense_channel_ids', unitArr.channel.block[unit]);

googletag.display('block_' + unit);
});

fallback[unit] = i.clientWidth;
}
}
}

// Wait 2 seconds and see if an ad showed up from the fall back
// if not, plug in a default here
setTimeout(() => {
for (unit in fallback)
if ((i = document.getElementById('block_' + unit)) &&
i.style.display !== 'none' &&
i.clientWidth > 0 &&
i.clientHeight < 50)
i.innerHTML = 'default code';
}, 2000);
}
}, 500);



This option uses Adsense as the fall back instead of GAM, and that worked out a LOT better for me! That $0.25 turned in to $20 :-)

window.googletag = window.googletag || {cmd: []};

var i, m, unit,
// 12345 = Ad Plus GAM ID
// 6789 = my GAM ID
gamID = ['12345', '6789'],

gFrame = [],
slotArr = [],
fallback = [],

unitArr = {};

unitArr.slot = {};
unitArr.slot.block = [];
unitArr.slot.backup = [];

// size options
unitArr.size = {};
unitArr.size.block = [];
unitArr.size.backup = [];

// optional, let's you specify a channel for reporting
unitArr.channel = {};
unitArr.channel.block = [];
unitArr.channel.backup = [];

// optional, if you want to control how each unit collapses
// you don't want it to collapse if you want a fall back to show up
unitArr.collapse = {};
// true = collapse if no ad is returned
unitArr.collapse.block = [];
unitArr.collapse.backup = [];

// true = collapse before an ad is fetched; ignored if above is not true
unitArr.collapseB.block = [];
unitArr.collapseB.backup = [];

// place the name of each container ID in the array
// I chose to do it this way so that I wouldn't have to repeat the word "block", and
// it let me define the order
for (m of [5, 2, ...]) {
switch (m) {
case 5:
unitArr.slot.block[m] = 'Ad.Plus-Leaderboard';
unitArr.size.block[m] =
unitArr.channel.block[m] = 6635170156;

break;

case 2:
unitArr.slot.block[m] = 'Ad.Plus-300x250';
unitArr.size.block[m] = [300, 250];
unitArr.channel.block[m] = 7349948984;
unitArr.collapse.block[m] = true;

break;

// and so on
}
}

// Primary
googletag.cmd.push(function() {

// not sure if this really matters, but I kept reading that it will help revenue if the fall back is
// in an iFrame. I'm writing it in PHP, of course, you just want it to be the URL of the page
googletag.pubads().set('page_url', "{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}");

for (unit in unitArr.slot.block) {
if (unitArr.slot.block[unit] && document.getElementById('block_' + unit)) {

// if optional fields aren't given, default to "false"
unitArr.size.block[unit] ?= false;
unitArr.channel.block[unit] ?= false;
unitArr.collapse.block[unit] ?= false;

slotArr[unit] =
googletag.defineSlot('/' + gamID[0] + ',' + gamID[1] + '/' + unitArr.slot.block[unit],
unitArr.size.block[unit],
'block_' + unit)
.setCollapseEmptyDiv(unitArr.collapse.block[unit], unitArr.collapseB.block[unit])
.addService(googletag.pubads());

if (unitArr.channel.block[unit])
googletag.slotArr[unit].set('adsense_channel_ids', unitArr.channel.block[unit]);
}
}

googletag.pubads().enableSingleRequest();
googletag.enableServices();
});

// Fall back; checks every 0.5 seconds to see if document is compete before running
var interval = setInterval(() => {
if (document.readyState === 'complete') {
clearInterval(interval);

for (unit of [5, 2, ...]) {
if ((i = document.getElementById('block_' + unit)) &&
i.style.display !== 'none' &&

// hidden elements have a width of 0
i.clientWidth > 0) {

// look for elements created inside of the ad unit
for (m in i.childNodes)
if (gFrame[unit] = i.childNodes[m].id)
break;

if (
// measure the unit you created
i.clientHeight < 50 ||

// measure the 3rd party element inside of the unit you created
(
gFrame[unit] &&
(
document.getElementById(gFrame[unit]).clientWidth > 0 ||
document.getElementById(gFrame[unit]).innerHTML == ''
)
)
) {
// ad wasn't filled, let's try again with Adsense
i.innerHTML = '<ins class="adsbygoogle" ' +
(() => {
switch (unit) {
case 5:
return 'style="width: 300px; max-height: 250px" ' +
'data-ad-slot="123" ' +
'data-ad-format="auto" ' +
'data-full-width-responsive="true" ';

case 2:
return 'data-ad-slot="456" ' +
'data-ad-format="autorelaxed" ' +
'data-matched-content-rows-num="1" ' +
'data-matched-content-columns-num="4" ' +
'data-matched-content-ui-type="image_stacked" ';

// and so on
}
})() + 'data-ad-client="ca-pub-123456789"></ins>';

(adsbygoogle = window.adsbygoogle || []).push({});

fallback[unit] = i.clientWidth;
}
}
}

// Wait 2 seconds and see if an ad showed up from the fall back
// if not, plug in a default here
setTimeout(() => {
for (unit in fallback)
if ((i = document.getElementById('block_' + unit)) &&
i.style.display !== 'none' &&
i.clientWidth > 0 &&
i.clientHeight < 50)
i.innerHTML = 'default code';
}, 2000);
}
}, 500);

LucasPerez

12:02 pm on Aug 4, 2023 (gmt 0)

Top Contributors Of The Month



@csdude55 how interesting! thank you so much!