Just to help everyone understand, the 2.1a Google IAB TCF v2 Error is likely caused by Google Advertise Product Tags being added to the website before a user consent has been set/established. This is a short video shows the expected behavior, ads not loading until after a user has given consent. If you see ads loading in the background before a user has selected their consent then you are getting 2.1a errors because you are adding Google tags without waiting for consent.
The solution to this will vary depending upon how you are adding Google Advertising Product tags to your site but hopefully the below information and Adsense example help.
Google Tag Manager
If you are using Google Tag Manager to add Choice and Google tags to your site you can use the guide here https://help.quantcast.com/hc/en-us/articles/360051794434 and https://help.quantcast.com/hc/en-us/articles/360051794434-TCF-v2-GTM-Implementation-Guide-IAB-Vendor-Tag-Blocking as references.
Adsense Specific Example
Taking the example from https://support.google.com/adsense/answer/9042142 I believe this is how you would need to rework the example to wait for proper consent signals from Quantcast Choice TCF v2.0.
I have not had a chance to fully test this so please let me know if you have any issues with the code and I will update the example.
SEE BELOW THIS CODE BLOCK FOR AN UPDATED VERIONS
<html>
<head>
<title>Your site title</title>
</head>
<body>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<script>
// Initially pause adsbygoogle (wait for consent to unpause)
(adsbygoogle=window.adsbygoogle||[]).pauseAdRequests=1;
</script>
<script>
( function() {
// Run this in an interval (every 0.1s) just in case we are still waiting for consent
var cnt = 0;
var consentSetInterval = setInterval(function(){
cnt += 1;
// Bail if we have not gotten a consent response after 60 seconds.
if( cnt === 600 )
clearInterval(consentSetInterval);
if( typeof window.__tcfapi !== 'undefined' ) { // Check if window.__tcfapi has been set
clearInterval( consentSetInterval );
window.__tcfapi( 'addEventListener', 2, function( tcData,listenerSuccess ) {
if ( listenerSuccess ) {
if( tcData.eventStatus === 'tcloaded' || tcData.eventStatus === 'useractioncomplete' ) {
if ( ! tcData.gdprApplies ) {
// GDPR DOES NOT APPLY, UnpauseAdRequests
// Set request non-personalized ads to false as GDPR does not apply.
(adsbygoogle=window.adsbygoogle||[]).requestNonPersonalizedAds=0;
// Unpause ads, as GDPR does not apply.
(adsbygoogle = window.adsbygoogle || []).pauseAdRequests=0;
}
else {
// GDPR DOES APPLY
// Purpose 1 refers to the storage and/or access of information on a device.
var hasDeviceStorageAndAccessConsent = tcData.purpose.consents[1] || false;
// Google Requires Consent for Purpose 1
if (hasDeviceStorageAndAccessConsent) {
// GLOBAL VENDOR LIST - https://iabeurope.eu/vendor-list-tcf-v2-0/
// CHECK FOR GOOGLE ADVERTISING PRODUCTS CONSENT. (IAB Vendor ID 755)
var hasGoogleAdvertisingProductsConsent = tcData.vendor.consents[755] || false;
// Check if the user gave Google Advertising Products consent (iab vendor 755)
if(hasGoogleAdvertisingProductsConsent) {
var hasPersonalizedProfileConsent = tcData.purpose.consents[3] || false;
var hasPersonalizedAdsConsent = tcData.purpose.consents[4] || false;
// Check if have add personalization consent Purpose 3 and 4
if( hasPersonalizedAdsConsent && hasPersonalizedProfileConsent ) {
// Set request non-personalized ads to false.
(adsbygoogle=window.adsbygoogle||[]).requestNonPersonalizedAds=0;
}
else {
// Set request non-personalized ads to true.
(adsbygoogle=window.adsbygoogle||[]).requestNonPersonalizedAds=1;
}
// Unpause ads , the user has granted consent for purpose 1 and given google consent.
(adsbygoogle = window.adsbygoogle || []).pauseAdRequests=0;
}
}
}
}
}
} );
}
cnt++;
}, 100);
})();
</script>
<!-- One test unit for GDPR -->
<ins class="adsbygoogle"
style="display:inline-block;width:970px;height:250px"
data-ad-client="ca-pubxxx"
data-ad-slot="slot_id">
</ins>
<!-- Another test unit for GDPR -->
<ins class="adsbygoogle"
style="display:inline-block;width:250px;height:250px"
data-ad-client="ca-pubxxx"
data-ad-slot="slot_id">
</ins>
<script>
// This will trigger the ad request if ads were unpaused in the CMP consent check above.
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
</body>
</html>
Edit: New Updated Version Added (aug 18)
This might be a better version than the above. This is again untested, so please test and give feedback if you are having issues. The main differences with this updated version is:
We are no longer checking tcData.purpose.consents[3] and tcData.purpose.consents[4] instead we are relying on google to decide between showing personalized ads vs not.
We do not add https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js to the page until we have consent, preventing any unwanted cookies from being added until we are sure we have consent to do so. This also allowed us to remove the (adsbygoogle=window.adsbygoogle||[]).pauseAdRequests=1; items as well
<html>
<head>
<title>Your site title</title>
</head>
<body>
<script>
( function() {
var insertAdsByGoogleJs = function() {
var element = document.createElement('script');
var firstScript = document.getElementsByTagName('script')[0];
var url = "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js";
element.async = true;
element.type = 'text/javascript';
element.src = url;
firstScript.parentNode.insertBefore(element, firstScript);
};
// Run this in an interval (every 0.1s) just in case we are still waiting for consent
var cnt = 0;
var consentSetInterval = setInterval(function(){
cnt += 1;
// Bail if we have not gotten a consent response after 60 seconds.
if( cnt === 600 )
clearInterval(consentSetInterval);
if( typeof window.__tcfapi !== 'undefined' ) { // Check if window.__tcfapi has been set
clearInterval( consentSetInterval );
window.__tcfapi( 'addEventListener', 2, function( tcData,listenerSuccess ) {
if ( listenerSuccess ) {
if( tcData.eventStatus === 'tcloaded' || tcData.eventStatus === 'useractioncomplete' ) {
if ( ! tcData.gdprApplies ) {
// GDPR DOES NOT APPLY
// Insert adsbygoogle.js onto the page.
insertAdsByGoogleJs();
}
else {
// GDPR DOES APPLY
// Purpose 1 refers to the storage and/or access of information on a device.
var hasDeviceStorageAndAccessConsent = tcData.purpose.consents[1] || false;
// Google Requires Consent for Purpose 1
if (hasDeviceStorageAndAccessConsent) {
// GLOBAL VENDOR LIST - https://iabeurope.eu/vendor-list-tcf-v2-0/
// CHECK FOR GOOGLE ADVERTISING PRODUCTS CONSENT. (IAB Vendor ID 755)
var hasGoogleAdvertisingProductsConsent = tcData.vendor.consents[755] || false;
// Check if the user gave Google Advertising Products consent (iab vendor 755)
if(hasGoogleAdvertisingProductsConsent) {
// Insert adsbygoogle.js onto the page.
insertAdsByGoogleJs();
}
}
}
}
}
} );
}
cnt++;
}, 100);
})();
</script>
<!-- One test unit for GDPR -->
<ins class="adsbygoogle"
style="display:inline-block;width:970px;height:250px"
data-ad-client="ca-pubxxx"
data-ad-slot="slot_id">
</ins>
<!-- Another test unit for GDPR -->
<ins class="adsbygoogle"
style="display:inline-block;width:250px;height:250px"
data-ad-client="ca-pubxxx"
data-ad-slot="slot_id">
</ins>
<script>
// This will trigger the ad request if ads were unpaused in the CMP consent check above.
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
</body>
</html>