screenshot

Here at data·yze our sole source of income is from ad revenue. As such, we take ad blockers as a threat to our existence. We've devised a way to detect the use of add blocker, and get around them. In the first half of the tutorial, we're focused on detecting the use of an adblocker.

In this tutorial we're using JQuery as it makes the code shorter, and easier to read. Our approach can work without JQuery with some minor modifications. To further simplify things, we'll use Google Adsense as our example ad network.

Our approach to Adblocking Detection

Most adblocking detection scripts use a bait-and-catch type approach. A "bait" file, such as a local ads.js, is created to look like an advertising script file. In reality, all the bait file does is set some variables. If the ad blocker takes the bait and blocks the script from downloading or executing, the variables will remain unset and the site can detect the ad blocker. We don't like this approach for several reasons: (1) in our experience, not all ad blockers take the bait. In fact, some of the bigger ones seem particularly adapt and ignoring the bait. (2) Most adnetworks already require an ad script to be downloaded and executed, anyway. And (3) adding an extra file to download creates additional latency that can slow down the rendering of your website which can affect how your site ranks with major search engines, not to mention worsen the user experience.

Our approach is to create a specialized wrapper around the ad. When we detect the wrapper we know an ad was intended to be included within the wrapper. Thus in addition to detecting if the script downloaded, we can also detect whether the page has been modified by the DOM.

A Google Adsense is inserted into the DOM by inserting an INS element with class name "adsbygoogle", e.g. <ins class="adsbygoogle" ... >. With a custom wrapper the code inserted into our page might look like <div id="foo"><ins class="adsbygoogle" ... ></div>. (See advanced tips on randomizing the wrapper id.)

Detecting if the Script Failed Load

In our experience, 98.8% of adblockers work by preventing the adscript from downloading. This is easily checked by using jQuery to get the script, and setting a variable on success.

var adsenseloaded = false; var adsenseLoadTime = 'AdsenseLoadTime'; $.getScript( '//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js', function( data, textStatus, jqxhr ) { adsenseloaded = true; });

We're data people, here at data·yze. As such, we like to track everything. The following lines of code use Google Analytics to track adsense script load times. We first check if the adsense script has been loaded by the Window.Ready event, then the Window.Load event. If not, we check every 250 milliseconds up to a timeout. In our experience if the script isn't loaded by Window.Ready, there's only a 0.2% chance it will load by the timeout. Depending on your audience, however, a different timeout might be more appropriate. (See advanced tips below on using a subset of your user base to test load times.)

var adsenseTimeOut = 1500; $(window).ready(function() { if (adsenseloaded){ adsenseloadedbyready = true; _gaq.push(['_trackEvent', adsenseLoadTime, 'Loaded at Window.ready'); } }); $(window).on("load", function() { if (!adsenseloaded){ checkIfLoadNextInterval(250); } else { if (!adsenseloadedbyready){ _gaq.push(['_trackEvent', adsenseLoadTime, 'Loaded at Window.load']); } checkAdsense(); } }); function checkIfLoadNextInterval(interval){ setTimeout(function() { if (adsenseloaded){ _gaq.push(['_trackEvent', adsenseLoadTime, 'Loaded after Window.load + ' + interval + 'ms']); adblockerDetected(); } else if (interval < adsenseTimeOut){ checkIfLoadNextInterval(interval + 250); } else { _gaq.push(['_trackEvent', adsenseLoadTime, 'Failed to Load by Window.load + ' + interval + 'ms']); checkAdsense(interval); } }, 250); }); function adblockerDetected(){}

The function adblockerDetected() is a place holder. In this half of the tutorial we're just focusing on detecting the presence of an adblocker. In the next part we'll give you some ideas for adblockerDetected().

Checking other types of Adblockers

While most adblockers work by blocking the adscript from downloading, there are other ways to hide an ad. One can use CSS to render it invisible, or rip it from the DOM. Thus, once the script has downloaded we call checkAdsense.

adsense_ids = ['foo']; var AdsenseEvent = 'Adsense'; function checkAdsense(timeelapsed){ if (timeelapsed == undefined){timeelapsed = 0;} for (var id in adsense_ids){ checkAdsenseID(id, timeelapsed); } } function checkAdsenseID(id, timeelapsed){ if (!adsenseloaded){ adblockerDetected(); _gaq.push(['_trackEvent', AdsenseEvent, "Adsense Failed Load"]); } else { var ad = $('#' + id + " ins.adsbygoogle"); if (ad.length > 0) { if (ad.html().replace(/\s/g, "").length == 0) { if (timeelapsed >= adsenseTimeOut){ _gaq.push(['_trackEvent', AdsenseEvent, "Ad Content Empty"]); adblockerDetected(); } else{ checkIfExecutedNextInterval(id, timeelapsed); } } else if (ad.css('display') === 'none'){ _gaq.push(['_trackEvent', AdsenseEvent, "Ad Display Set to None"]); adblockerDetected(); } else if (ad.height() == 0 || ad.width() == 0 || ad.css('height') == 0 || ad.css('width') == 0){ _gaq.push(['_trackEvent', AdsenseEvent, "Ad Height or Width set to 0"]); adblockerDetected(); } else if (ad.css('opacity') == 0){ _gaq.push(['_trackEvent', AdsenseEvent, "Opacity set to 0"]); adblockerDetected(); } else if (ad.attr('data-ad-client') != 'ca-pub-XXXXXXX') { _gaq.push(['_trackEvent', AdsenseEvent, 'Ad Injection: ' + $(ad).attr('data-ad-client')]); adblockerDetected(); } else { _gaq.push(['_trackEvent', AdsenseEvent, "Ad Shown"); } } else { _gaq.push(['_trackEvent', AdsenseEvent, "ins.adsbygoogle removed"]); adblockerDetected(); } } } function checkIfExecutedNextInterval(id, interval){ setTimeout(function() { var ad = $('#' + id + " ins.adsbygoogle"); if (ad.html().replace(/\s/g, "").length > 0){ checkAdsenseID(id, interval); } else if (interval < adsenseTimeOut){ checkIfExecutedNextInterval(id, interval + 250); } else { _gaq.push(['_trackEvent', AdsenseEvent, "Ad Content Empty"]); adblockerDetected(); } }, 250); }

The first step is to get the wrapper div id, and check to see if there is still an ins element with a classname of "adsbygoogle". This is done with checkAdsense(). In our experience, modifying the DOM to remove this ins element is the 3rd most common approach by adblockers. If the ad exists, we check to see if the there is content within the ad with line 18. If not, it's possible the ad script has not had a chance to complete yet. Thus we use another timeout, checkIfExecutedNextInterval() to give the script a chance to execute.

Lines 25-34 check for other forms of hiding ads though CSS tricks. Line 34 checks for an ad injection. Be sure to change the publisher id, ca-pub-XXXXXXX to your own!

Note, line 21 may be controversial. It's possible that the user was not using an adblocker, but Google was unable to identify an appropriate ad to display, and that's why the ad content is empty after the time out. If adblockerDetected() creates a pop up that blocks access to your site, it may not be appropriate to call it at line 21. If adblockerDetected() shows another ad in place of the missing adsense, then there may be no harm calling it at line 21.

Advanced Tips

Randomizing your wrapper ids

Why do you want to randomize your wrapper ids? Some adblockers allow user customization that can target sections of HTML code. By randomizing the ID, no one can create an adblock scripts that removes the wrapper (and thus all it's ad contents) from the DOM ahead of time. The user will have to remove each an every ad individually, after it has been seen.

To randomize the wrapper id, start by changing line adsense_ids = ['foo']; to adsense_ids = [];

Use the PHP function randomAdsenseDivID() to generate a random id.

function randomAdsenseDivID(){ $characters = '0123456789abcdefghijklmnopqrstuvwxyz'; $randstring = $characters[rand(10, strlen($characters))]; $len = (5 + rand(0, 5)); for ($i = 0; $i < $len; $i++) { $randstring .= $characters[rand(0, strlen($characters))]; } return $randstring; }

Using randomAdsenseDivID(), try the following line of PHP code which creates the id, and pushes it to the adsense_id javascript array.

$adsenseid = randomAdsenseDivID(); echo "<div id=\"$adsenseid\"><ins class=\"adsbygoogle\" ... ></div>"; echo "<script>adsense_ids.push('$adsenseid');</script>";

Using a subset of your user base to test load times

It is very important to set your time out appropriately. If the timeout is too short, you'll over estimate the presence of adblocking. Too long, and you'll miss out on opportunities to catch adblockers.

Since your user demographics can always change, we recommend siphoning off a subset of your traffic and making them exempt from the time out. Modify checkIfLoadNextInterval(interval) as follows:

function checkIfLoadNextInterval(interval, exempt){ setTimeout(function(){ if (adsenseloaded){ if (exempt){ _gaq.push(['_trackEvent', adsenseLoadTime, 'Loaded after Window.load + ' + interval + 'ms']); } adblockerDetected(); } else if (interval < adsenseTimeOut || exempt){ checkIfLoadNextInterval(interval + 250, exempt); } else{ if (exempt){ _gaq.push(['_trackEvent', adsenseLoadTime, 'Failed to Load by Window.load + ' + interval + 'ms']); } checkAdsense(interval); } }, 250); });

Note, we only track the event in Google Analytics if the variable exempt is set to true. Then, inside your $(window).on("load", function() { modify your call to checkIfLoadNextInterval(250); to checkIfLoadNextInterval(250, Math.random() < x); where x is the percentage of traffic you want to siphon off for testing.

Code Liscence

Although code shared on data·yze is source-avaliable, it is still proprietary and data·yze maintains it's intellectual property rights. In particular, data·yze restricts redistribution of the code. Code displayed above may be copied, modified, displayed or adapted for use on other websites (commercial or otherwise) only under certain conditions and may not be repackaged or redistributed. See Terms for details.