How to Run a Function After Disqus for WordPress has Loaded

Soon after launching this site, we ran into a little problem. I noticed that the sidebar, whose height is adjusted by JavaScript to match the main column after the page loads, wasn’t going all the way to the bottom as it was supposed to.

disqus-problem
Scary moment!

A coder would probably be able to infer why this was so from the jQuery code below:

$( window ).on( 'load resize', function() {
	matchHeight( '.main-col', '.side-col', 0 );
});

function matchHeight( source, destination, offset ) {
	$( destination ).css( 'min-height', $( source ).innerHeight() + parseInt( offset ) );
}

The code above tells the browser to listen for two events: when the page completes loading, and when the window is resized. When those two things happen, the matchHeight function fetches the height of the source element and applies it as the minimum height of the destination element. Simple enough.

However, the problem we’ve seen in the screenshot comes about because Disqus comments are loaded asynchronously, having no effect on the overall page load. Therefore, the loaded event fires before the comments have been inserted into the page. That means that our function ends up retrieving a height that is less than what the source element will end up having once the comments have been inserted. That’s why the sidebar reaches the position shown.

What logically follows, therefore, is that we should find a way to adjust our sidebar’s height after Disqus has loaded.

Now, of course, the “lazy programmer” in me looked around online to see if anyone had encountered the same problem—and if they had solved it:

Even though the info on the Disqus website was scanty, at least it pointed me in the right direction with the note below:

NOTE: If you’re using the WordPress plugin, this variable (disqus_config) is already set. To avoid overriding it, you’ll need to edit the plugin and add your tracker to the existing disqus_config function.

So all we need to do is take a look at the plugin’s code, now that Disqus are being all stingy with their documentation. I found the code I needed to edit in the plugin file:

disqus-comment-system/comments.php

disqus-code
What we’re binding to.

After a few minutes of experimentation, I was able to partly fix the problem by adding a few lines of code to the onReady callback that executes when the Disqus commenting UI has loaded. I updated the code block from this:

config.callbacks.onReady.push( function() {
	// sync comments in the background so we don't block the page
	var script = document.createElement( 'script' );
	script.async = true;
	script.src = '?cf_action=sync_comments&post_id=ID; ?>';

	var firstScript = document.getElementsByTagName( "script" )[0];
	firstScript.parentNode.insertBefore( script, firstScript );
});

to this:

config.callbacks.onReady.push( function() {
	// sync comments in the background so we don't block the page
	var script = document.createElement( 'script' );
	script.async = true;
	script.src = '?cf_action=sync_comments&post_id=ID; ?>';

	var firstScript = document.getElementsByTagName( "script" )[0];
	firstScript.parentNode.insertBefore(script, firstScript);

	// ongeza code ya ku-adjust sidebar
	setTimeout( function() {
		var sideCol = document.getElementsByClassName( 'side-col' );
		var mainCol = document.getElementsByClassName( 'main-col' );
		console.log( 'sidecol:' + sideCol.item( 0 ) ); // confirm tumeiwahi
		sideCol.item( 0 ).style.minHeight = mainCol.item( 0 ).offsetHeight + "px";
	}, 100 );
});

Yea, vanilla JavaScript rocks.

This was the result:

disqus-problem-2
Weird behaviour. Madimoni.

Cool, some progress!

So it seemed like the sidebar was receiving the styling from the function, but somehow the value was falling short of the expected full height.

This baffled me for a while, especially since the heights being returned seemed to be random. Until I realised that after Disqus loads the comment form & comments, it animates into view comments from other posts. Therefore, the callback triggered before the animation began, meaning that after the 100 millisecond timeout, our function queried the height of a div that was mid-air, so to speak.

So I simply changed the code to have a longer timeout, allowing the animation to complete before attempting to match the heights:


	setTimeout( function() {
		var sideCol = document.getElementsByClassName( 'side-col' );
		var mainCol = document.getElementsByClassName( 'main-col' );
		console.log( 'sidecol:' + sideCol.item( 0 ) );
		sideCol.item( 0 ).style.minHeight = mainCol.item( 0 ).offsetHeight + "px";
	}, 1500 ); // ipatie time kiasi
});

And Eureka!!! As you can see on this site (desktop), it works perfectly.

Hope this helps someone out there.

Update

Many thanks to Chris who pointed out in the comments below that I actually wasn’t out of the woods yet! With the code above, the sidebar adjusts its height twice. The first time it does so is when the page loads. Then, after Disqus comments load, it adjusts a second time because of the callback function.

However, when someone clicks to reply or to add a comment, the comment box which appears alters the main column’s height and breaks the layout again! Argh!

So here’s my bulletproof solution:


var looper;
var disqusDiv = document.getElementById( 'disqus_thread' );
disqusDiv.onmouseenter = runLoop();
disqusDiv.onmouseleave = function() { clearInterval( looper ); };

function runLoop() {
	looper = setInterval( function() {
		var sideCol = document.getElementsByClassName( 'side-col' );
		var sideHeight = sideCol.item( 0 ).offsetHeight;
		var mainCol = document.getElementsByClassName( 'main-col' );
		var mainHeight = mainCol.item( 0 ).offsetHeight;
		if ( sideHeight != mainHeight ) {
			sideCol.item( 0 ).style.minHeight = mainHeight + "px";
		}
	}, 2000 );	
}

The trick is to use setInterval (which will run after every x milliseconds) instead of setTimeout which runs only once. It’s not the most elegant solution, but it’s the most effective I can think of. I wish we could target a click on an element within the Disqus container, but the events don’t bubble up to it, probably because everything is in an iframe. So all we can detect is onmouseenter and onmouseleave. At least we’re only running our sentinel height checker whenever the mouse is within the Disqus container.

If anyone can improve this, suggestions are welcome. I must say I’m pretty content with how it effectively solves a real problem on our own site.

Downer

The only disadvantage is that you’ll have to save this snippet of code somewhere (or just bookmark this post) so you can restore it to the plugin whenever it’s overwritten by an update within WordPress.

Cheers.

Signed. Martians.