How to display or block personalized Adsense ads based on consent for IAB TCF v2.0 with Quantcast Choice?

I’ve chosen Quantcast Choice as the consent management platform for my website. However with their basic solution Google Adsense ads are still served to visitors before they give their consent for the related vendor (Google). Since 2 days there’s a notice in Adsense confirming this issue and after a grace period of 90 days no ads will be served anymore. The error message: "2.1a: the Tag or SDK isn’t receiving a TC string due to CMP status being stub, loading, or error."

I’m not familiar at all with scripts, but it seems I have to use some to make Quantcast Choice actually work.
Basically I need to know how to:

  • not serve any Google ads before a visitor consent to Google
  • serve personalized ads after consent
  • serve non-personalized ads after no consent

What I’ve learned so far:

Before a visitor’s consent, I can add this script to my Google Adsense code to not show any ads:

<script>(adsbygoogle=window.adsbygoogle||[]).pauseAdRequests=1;</script>

No problem so far. However then I should be able to know if a visitor has given consent for the vendor Google. Google’s vendor ID in the TCF v2.0 is 755. I found the following mixture of code and text on one of the Quantcast pages, but I don’t know how to use this. Should I put this on my webpages within the Quantcast Choice script or …? (source linked below)

{{QC - __cmpConsents.iabVendorConsentIds}} matches the regular expression (^|,)755(,|$).

So if ID 755 is found, I should call:

(adsbygoogle=window.adsbygoogle||[]).pauseAdRequests=0

and if ID 755 is not found, I should call:

(adsbygoogle=window.adsbygoogle||[]).requestNonPersonalizedAds=1
(adsbygoogle=window.adsbygoogle||[]).pauseAdRequests=0

But how do I do these "calls"?

So I think I understand the steps to follow, it’s just that I don’t know how to implement these steps and connect the dots. Any help would be much appreciated!

Sources I’ve used:

Example of a website that seems to do what I want to achieve: carscoops.com

3 thoughts on “How to display or block personalized Adsense ads based on consent for IAB TCF v2.0 with Quantcast Choice?”

  1. IMO, if you want to follow official instructions for Quantcast Choice and Google Adsense, this is the way:

    Add all of this in your <head> tag:

    google adsense

    <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <script>
    //pause all ad requests until further notice
    (adsbygoogle=window.adsbygoogle||[]).pauseAdRequests=1;
    (adsbygoogle=window.adsbygoogle||[]).push({
        google_ad_client: "ca-pub-XXXXXXXXXXXXXXXX",
        enable_page_level_ads: true
    });
    </script>
    

    quantcast choice CMT for TCF 2.0

    <script>
    (function() {
    var host = window.location.hostname;
    var element = document.createElement('script');
    var firstScript = document.getElementsByTagName('script')[0];
    var url = 'https://quantcast.mgr.consensu.org'
    .concat('/choice/', YOUR_QUANTCAST_ID_HERE, '/', host, '/choice.js')
    var uspTries = 0;
    var uspTriesLimit = 3;
    element.async = true;
    element.type = 'text/javascript';
    element.src = url;
    firstScript.parentNode.insertBefore(element, firstScript);
    function makeStub() {
        var TCF_LOCATOR_NAME = '__tcfapiLocator';
        var queue = [];
        var win = window;
        var cmpFrame;
        function addFrame() {
            var doc = win.document;
            var otherCMP = !!(win.frames[TCF_LOCATOR_NAME]);
            if(!otherCMP) {
                if(doc.body) {
                    var iframe = doc.createElement('iframe');
                    iframe.style.cssText = 'display:none';
                    iframe.name = TCF_LOCATOR_NAME;
                    doc.body.appendChild(iframe);
                } else {
                    setTimeout(addFrame, 5);
                }
            }
            return !otherCMP;
        }
        function tcfAPIHandler() {
            var gdprApplies;
            var args = arguments;
            if(!args.length) {
                return queue;
            } else if(args[0] === 'setGdprApplies') {
                if(
                args.length > 3 &&
                args[2] === 2 &&
                typeof args[3] === 'boolean'
                ) {
                    gdprApplies = args[3];
                    if(typeof args[2] === 'function') {
                        args[2]('set', true);
                    }
                }
            } else if(args[0] === 'ping') {
                var retr = {
                    gdprApplies: gdprApplies,
                    cmpLoaded: false,
                    cmpStatus: 'stub'
                };
                if(typeof args[2] === 'function') {
                    args[2](retr);
                }
            } else {
                queue.push(args);
            }
        }
        function postMessageEventHandler(event) {
            var msgIsString = typeof event.data === 'string';
            var json = {};
            try {
                if(msgIsString) {
                    json = JSON.parse(event.data);
                } else {
                    json = event.data;
                }
            } catch (ignore) {}
            var payload = json.__tcfapiCall;
            if(payload) {
                window.__tcfapi(
                payload.command,
                payload.version,
                function(retValue, success) {
                    var returnMsg = {
                        __tcfapiReturn: {
                        returnValue: retValue,
                        success: success,
                        callId: payload.callId
                        }
                    };
                    if(msgIsString) {
                        returnMsg = JSON.stringify(returnMsg);
                    }
                    event.source.postMessage(returnMsg, '*');
                },
                payload.parameter
                );
            }
        }
        while (win) {
            try {
                if(win.frames[TCF_LOCATOR_NAME]) {
                    cmpFrame = win;
                    break;
                }
            } catch (ignore) {}
            if(win === window.top) {
                break;
            }
            win = win.parent;
        }
        if(!cmpFrame) {
            addFrame();
            win.__tcfapi = tcfAPIHandler;
            win.addEventListener('message', postMessageEventHandler, false);
        }
    };
    makeStub();
    var uspStubFunction = function() {
        var arg = arguments;
        if(typeof window.__uspapi !== uspStubFunction) {
            setTimeout(function() {
            if(typeof window.__uspapi !== 'undefined') {
                window.__uspapi.apply(window.__uspapi, arg);
            }
            }, 500);
        }
    };
    var checkIfUspIsReady = function() {
        uspTries++;
        if(window.__uspapi === uspStubFunction && uspTries < uspTriesLimit) {
            console.warn('USP is not accessible');
        } else {
            clearInterval(uspInterval);
        }
    };
    if(typeof window.__uspapi === 'undefined') {
        window.__uspapi = uspStubFunction;
        var uspInterval = setInterval(checkIfUspIsReady, 6000);
    }
    })();
    </script>
    

    TCF 2.0 API listener

    <script>
    window.__tcfapi('addEventListener', 2, function(tcData, listenerSuccess) {
        if(listenerSuccess) {
            //check the eventstatus
            if(tcData.eventStatus === 'useractioncomplete' || tcData.eventStatus === 'tcloaded') {
                if(!tcData.gdprApplies) {
                    //GDPR does not apply to this user, load ads immediately
                    (adsbygoogle=window.adsbygoogle||[]).pauseAdRequests=0;
                    return;
                }
    
                //deal with parsing tcData for IAB Vendor consents
                //deal with personalized/non-personalized Google ads
                if((tcData.vendor.consents[755] || tcData.vendor.legitimateInterests[755]) && (tcData.purpose.consents[1] && tcData.purpose.consents[3] && tcData.purpose.consents[4] && tcData.purpose.legitimateInterests[2] && tcData.purpose.legitimateInterests[7] && tcData.purpose.legitimateInterests[9] && tcData.purpose.legitimateInterests[10])) {
                    //consent signals sufficient for personalized ads
                    //set personalized ads and unpause loading process
                    (adsbygoogle=window.adsbygoogle||[]).requestNonPersonalizedAds=0;
                } else if((tcData.vendor.consents[755] || tcData.vendor.legitimateInterests[755]) && (tcData.purpose.consents[1] && tcData.purpose.legitimateInterests[2] && tcData.purpose.legitimateInterests[7] && tcData.purpose.legitimateInterests[9] && tcData.purpose.legitimateInterests[10])) {
                    //consent signals NOT sufficient for personalized ads
                    //set non-personalized ads and unpause loading process
                    (adsbygoogle=window.adsbygoogle||[]).requestNonPersonalizedAds=1;
                }
                (adsbygoogle=window.adsbygoogle||[]).pauseAdRequests=0;
            }
        }
    });
    </script>
    
    Reply
  2. Thank’s for this snippet.

    There is a strange problem.
    On Chrome, when I put an Objection on Legitimate Interest Purpose(s) "Select basic ads", i still have ads when i reload the page.
    On firefox, ads are not showing.

    Do you have the same problem ? I can’t find how to solve it

    Reply
  3. I would recommend a much simple solution. With TCF 2.0 you don’t have to manually configure Adsense parameters (personalized ads etc.) based on a given consent. You just have to guarantee, you start loading ads after consent is successfully loaded. Adsense library read the consent strings automatically and display ads in accordance with a given consent.

    Example code for integration Quantcast Choices with Adsense:

    <!-- Quantcast Choice. Consent Manager Tag v2.0 (for TCF 2.0) -->
    ...
    <!-- End Quantcast Choice. Consent Manager Tag v2.0 (for TCF 2.0) -->
    
    <script>
    __tcfapi('addEventListener', 2, function(tcData, success) {
        if (success) {
            if (tcData.eventStatus == 'useractioncomplete' || tcData.eventStatus == 'tcloaded') {
                var hasStoreOnDeviceConsent = tcData.purpose.consents[1] || false;
    
                if (hasStoreOnDeviceConsent) {
                    var adsbygoogle_script = document.createElement('script');
                    adsbygoogle_script.src = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
                    document.head.appendChild(adsbygoogle_script);
                }
            }
        }
    });
    </script>
    
    Reply

Leave a Comment