Dynamic Disqus Instantiation

27 August 2012

I needed to use Disqus from a GWT app, so I needed to solve the problem of loading threads on demand as virtual pages in the app were changed.

A small amount of reverse engineering and experimentation led me to construct a utility class (below).

I also posted this answer on Stack Overflow.


The main insights are:

  1. There is an undocumented global parameter called disqus_container_id which allows you to place the comments wherever you like. If that doesn’t work in some future version, my fallback was going to be to temporarily set the id of the target element to disqus_thread, add the comments and then change it back to the original id.
  2. Since this was being developed for GWT using JSNI, I needed to set the global parameters in the original window context, accessible through $wnd. I changed the default Disqus embed code accordingly. I didn’t realise before that all global variables are in the Window object, but I learned something new.
  3. You can re-use the same container, Disqus seems to clear the contents when you activate it.
  4. This leaves lots of copies of the script tag in the DOM. Maybe it would be a good idea to clean these up too once they’ve been used. Alternatively, I might do some more experiments using the DISQUS.reset method described in other answers.

Extracting just the crucial information for someone using JS on its own, this should allow you to stick a Disqus thread anywhere you like:

function loadComments(container_id, shortname, identifier, developer) {
    // CONFIGURATION VARIABLES
    window.disqus_container_id = container_id;
    window.disqus_developer = developer ? 1 : 0;
    window.disqus_shortname = shortname; // required
    if (identifier) window.disqus_identifier = identifier;

    // DON'T EDIT BELOW THIS LINE
    (function() {
       var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
       dsq.src = 'http://' + shortname + '.disqus.com/embed.js';
       (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
    })();
}

And here is the full GWT utility class. I’ve only implemented the parameters I needed so far.

import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Random;
import com.google.gwt.user.client.ui.Widget;

public class Disqus {

    public static boolean developer = false;
    public static String shortname;

    public static void showComments(Widget where, String identifier) {
        showComments(where.getElement(), identifier);
    }

    public static void showComments(Element where, String identifier) {
        if (shortname == null)
            throw new IllegalArgumentException(
                      "You must specify the disqus shortname before displaying comments");

        // Store the original id of the target element
        String id = where.getId();
        if (id == null) {
            id = "disqus-" + Integer.toHexString(Random.nextInt());
            where.setId(id);
        }

        // Update the id temporarily
        where.setId("disqus_thread");

        // Load the comments
        loadComments(id, shortname, identifier, developer);
    }

    private static native void loadComments(String container_id, String shortname, String identifier, boolean developer) /*-{
        // CONFIGURATION VARIABLES
        $wnd.disqus_container_id = container_id;
        $wnd.disqus_developer = developer ? 1 : 0;
        $wnd.disqus_shortname = shortname; // required
        if (identifier) $wnd.disqus_identifier = identifier;

        // TODO
        // disqus_url

        // disqus_title

        // disqus_category_id

        // DON'T EDIT BELOW THIS LINE (sorry, I've edited it anyway)
        (function() {
            var dsq = $doc.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
            dsq.src = 'http://' + shortname + '.disqus.com/embed.js';
            ($doc.getElementsByTagName('head')[0] || $doc.getElementsByTagName('body')[0]).appendChild(dsq);
        })();
    }-*/;
}