I recently updated my old Support Message Board Web site that previously was purely server rendered to a site that mixes server rendered content with dynamically loaded AJAX content. The app is a message board application, that uses a panel browsing layout. Any directly accessed URLs use server rendering to get content into the page, but any subsequent requests to update messages pulls down just a the actual message content as HTML and then injects that into the document. The result is a much smoother browsing experience.
In the site below the full page renders with server rendered output initially. Any clicks on messages then refreshes the right panel with content downloaded via AJAX and refreshing just the messaging area:
The result is a much faster and smoother navigation experience than refreshing the entire page.
AdSense and AJAX
Ok old hat, that's nothing new or exciting really.
But the issue is that Google Adsense ads display just fine when the page is initially server rendered, but are not rendering at all when the right hand panel is updated with AJAX loaded messages. The AJAX loaded content includes the ad markup including script code, but because the markup is loaded dynamically via AJAX it's not properly activated and so no ads actually render with the AJAX content by default.
AdSense by default doesn't officially support AJAX content and some quick searching around seems to confirm that fact. But it looks like there are workarounds one of which I'll discuss here.
The Adsense default script that Google provides for displaying ads looks like this:
<!-- MessageBoard Thread Responsive Ad -->
<script src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js" async ></script>
<ins class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-XXXXXXX"
data-ad-slot="63623XXXXX"
data-ad-format="auto"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
And this works fine for server rendered ads. You can embed this script into the page and an ad displays. However, when loading this script content via AJAX the script code isn't explicitly fired. While the ad placeholder gets embedded into the page, the script tag doesn't get fired.
Breaking up the Script
But luckily there's a relatively easy way to make this work by breaking up the google script code into its component pieces:
- Put the script link into the header (so it only loads once – a good idea even for server rendered pages)
- Put the <ins> tag placeholder whereever it needs to display (also in AJAX content)
- Call the trigger code on AJAX reloads explicitly
So we'll start by putting the script tag into the header or the bottom of the page. Where doesn't really matter but it needs to be handled as part of the initially rendered page.
<script src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js" async ></script>
Then embed the <ins> tag in the location(s) of the document where it shows. In my case that's in the rendered Message partial where ads are rendered after particular messages (1,3,6):
@{
int msgCount = 0;
foreach(var msg in Messages)
{
msgCount++;
<article>
<div class="message-body">
...
</div>
@if (msgCount == 1 || msgCount == 3 || msgCount == 6)
{
<!-- script in layout and on bottom and ajax refresh in loadTopicAjax()
<!-- MessageBoard Thread Responsive Ad -->
<ins class="adsbygoogle"
style="display: block"
data-ad-client="ca-pub-4571123155768157"
data-ad-slot="6111111111111"
data-ad-format="auto"></ins>
}
</article>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
}
Now, the script at the bottom fires on server rendered content and everything works as expected – I get a max 3 ads rendered into my content.
The Google script appears to have some awareness about where it lives in the page, as it renderes 3 ads in the right place in the page. If you move the script to the bottom of the page as I initially tried however, only the first script is rendered. So the script figures out which tag to render based on the location in the page. When run out of context (ie. on the bottom of the page) it appears the script fires only the first embedded ad it can find.
AJAX Loaded Message Content
When messages are reloaded via AJAX when a user clicks a message after the initial page load, the application makes an XHR call to retrieve a partial HTML fragment that contains just the message content without all the 'frame' chrome and extracts just the the content. The downloaded AJAX content is essentially identical to the message body we rendered before minus the message list. The HTML in this payload includes the threaded message along with the embedded Google Adsense ads.
The problem is that when the AJAX HTML is embedded into the page, the script code associated with the Ad markup is not automatically fired.
The work around to make this work is explicitly fire the script code that updates the ad on the page by adding the activation script code to the AJAX callback that merges the content into the page.
Here's the (truncated) client code that reloads messages into the page via the AJAX request:
function loadTopicAjax(href) {
$.get(href, function(html) {
var $html = $(html);
var title = html.extract("<title>", "</title>");
window.document.title = title;
var $content = $html.find(".main-content");
if ($content.length > 0) {
html = $content.html();
$(".main-content").html(html);
// update the navigation history/url in addressbar
if (window.history.pushState && !hrefPassed)
window.history.pushState({ title: '', URL: href }, "", href);
// fire google ads
(adsbygoogle = window.adsbygoogle || []).push({});
} else
return;
});
return false; // don't allow click
};
The key item in regards of the AdSense is to trigger the ad display code as part of the AJAX result, so after the message has been loaded and updated we can trigger the Google ad code again and the ad will then be displayed properly.
The key item is:
(adsbygoogle = window.adsbygoogle || []).push({});
which activates the ad.
Good news, bad news
Notice that I said the ad – singular.
While the above code works to trigger an ad via AJAX, it unfortunately only triggers the first ad on the page, not any of the subsequent ones. The issue here is that script code has some internal awareness of where it's running in the page and finding the adjacent <ins> tag to render the ad. If called generically as I'm doing here (either on the bottom of the page in server render, or as above in the AJAX callback code) only the first ad actually renders.
So I'm only able to render a single ad on the AJAX calls, but all three render one server side rendering.
I haven't found a way around this – if anybody knows of a way to make this happen, please leave a comment. I suspect it might be possible with options as part of the push command but I couldn't find any documentation on this.
Careful!
All of this is actually unsupported. Officially Google doesn't support ads in AJAX based content and what I describe here is somewhat unorthodox and based on a hack. It works, but it can easily break if Google decides to change how the script code works.
In my Message Board application I'm not doing anything unorthodox with the ad-units as I'm simply trying to get new ad units to display when AJAX navigation has taken place – ie. new content is loaded. This is really no different than loading a new page and displaying new ads. But Google's policies and terms explicitly forbid tinkering with the ad code and not displaying more than 3 ad units per page. Technically, the code I'm using is violating that – since I have a single page effectively replaces the same page's content with new content. But logically, this is doing what traditionally a full page reload would do, so I'll take my chances in fair use under those terms.
But, it's easy to see how this approach could be used to hijack ads and trigger a lot of ad refreshes on a single page which is where you can easily run into problems with Google's policy. So use this with caution, and be aware that you're skirting the outer realms of Google's Terms of service and you can potentially get cut off for violating their terms. Use at your own risk. Be weary and err on the side of caution. If in doubt, contact Google and ask.
Other Posts you might also like