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.

  • The sidebar background still isn’t going all the way to the bottom though – looking to the right of this comment box, it stops near the top of the related articles as per your final screenshot. Also, the height of Disqus comments can vary even after they’ve loaded, e.g. when you’ve posted a comment or used some of the Disqus navigation.

    • Aah, I see what you mean, now that I’ve clicked to reply (which has brought up the comment box)…

      Okay, since Disqus won’t be running to our rescue with a better API, I think the only solution for now is to listen for events within the #disqus_thread div and update our UI accordingly. I think it would work if I used an interval instead of a timeout, the condition being that the interval will be cleared as soon as it detects that the height of the sidebar is finally equal to that of the main column.

      The interval can be reinitiated whenever there’s a click or a keyup event withing #disqus_thread.

      Trying it out now…

      • Looks like you’ve fixed it. Looking good now :)

        • Thanks man. I’ve updated the article with the code.

  • It’s frustrating that they don’t have a way of allowing for callbacks outside of their plugin files.

    • I know, right! You’d think that would be a basic capability.

      • What I just ended up doing is using a manual integration in my WordPress theme instead of the plugin. Appreciate your write-up, though. Helped for documentation purposes.

  • Thanks for sharing!

signed. martians.™