Building a Smart Client-Side JavaScript “Discuss on Hacker News” Button

by Marian Steinbach on Jul 7, 2015

Building a Smart Client-Side JavaScript “Discuss on Hacker News” Button

At Giant Swarm, we build infrastructure for developers, and of course we want our content to be visible amongst developers, too. To foster the distribution and discussion of our blog posts on Hacker News, we built ourselves a little button to appear here in our blog. No rocket science, just a little something we thought we’d share with you. Here is a preview:

Preview of our buttons

To date, Hacker News (HN) doesn’t provide an embeddable button for using in a website and just pre-populating a link to https://news.ycombinator.com/submit doesn’t account for posts that have already been submitted to HN. With those you would rather like to direct the user to upvote and discuss the already existent HN entry. There are a few third party solutions, of which one seems to be particularly popular: hackernews-button by Ilya Grigorik. Here is how it looks in action:

Screenshot of embeddable Hacker News Buttons

Both buttons require a little script snippet to be embedded into a website, which then loads some more script. These scripts communicate with a server application, which communicates to Hacker News to do one thing: Check whether a URL in question is already available on Hacker News and if yes, get the URL of the discussion page and the number of votes.

That information is pulled in via JavaScript, which, in the end, creates a button the user can click. If the URL – usually the URL of the page the user is looking at – is not yet available on Hacker News, this button links to a form where the URL can be added. If it has been added before, the link points to the discussion page.


Tiny buttons, coming at a price


With hn-button.com, 16.7 KB worth of uncompressed, unminified JavaScript are loaded from a Heroku server. After the script has done it’s job, the DOM is inflated by another fixed-size iFrame containing the actual button.

Ilya’s JavaScript is more lean. Only 681 Bytes are transferred, in this case from a Google App Engine application. Still, an iFrame is generated to display a tiny button.

Both these iFrames show content rendered by some server-side application that is generously hosted by someone for us. But what if that someone decides to stop hosting this? We could of course host our own, since both are Open Source offers. However, does it have to be that complicated?

At Giant Swarm, we are not shy when it comes to hosting another tiny service. But why, if there is no need?


Look, Mum, no Server Logic


The Hacker News API used by these button solutions is pretty straight forward. And besides that, thanks to CORS, it’s open for requests from client-side JavaScript everywhere. Check this sample URL and the response headers provided by the server:

https://hn.algolia.com/api/v1/search?tags=story&restrictSearchableAttributes=url&query=http%3A%2F%2Fdocker.io%2F

The header we are pretty happy about is this one:

Access-Control-Allow-Origin: *

This allows for a pretty straight-forward implementation that doesn’t need any server-side logic at all. Here is how it should work:

  • By default, our button links to the “Add URL to Hacker News” form.
  • Via JavaScript we check the Hacker News Search API for entries matching the current URL.
  • If we find entries for the current URL, we change the button text and the link to point to the discussion page.

We already have jQuery in place, so everything we need for convenient cross-browser Ajax communication and DOM manipulation is there.

A quick aside: It’s not the intent here to point fingers at the authors of the server-side solutions mentioned above. In fact, we are grateful for their inspiration on how to use the Hacker News API for this purpose. Very likely, this API hasn’t always been open for cross-origin requests. So a server-side solutions was the only possible workaround.

Here is our JavaScript code, which we provide as a little jQuery plugin:

$.fn.hnButton = function() {
  // this is the button to be upgraded
  var linkbutton = this;
  var title = document.title;
  var thisUrl = window.location.href;
  if (linkbutton.data('url')) {
    thisUrl = linkbutton.data('url');
  }
  if (linkbutton.data('title')) {
    title = linkbutton.data('title');
  }
  var hnUrlPrefix = "https://news.ycombinator.com/item?id=";

  // default button action
  linkbutton.click(function(evt){
    evt.preventDefault();
    window.open('http://news.ycombinator.com/submitlink?u='+encodeURIComponent(thisUrl)+'&t='+encodeURIComponent(title));
  });

  if (linkbutton.length) {
    var api = 'https://hn.algolia.com/api/v1/search';
    var params = {
      tags: 'story',
      query: thisUrl,
      restrictSearchableAttributes: 'url',
      advancedSyntax: true
    };
    $.getJSON(api, params, function(data){
      if (data.nbHits > 0) {
        $.each(data.hits, function(index, item){
          if (item.url === thisUrl) {
            // this is our item!
            var hnPostLink = hnUrlPrefix + item.objectID;
            linkbutton.unbind('click');
            linkbutton.attr('href', hnPostLink);
            linkbutton.text('Discuss/vote on Hacker News (' + item.points + ')');
            return;
          }
        });
      }
    });
  }
  return linkbutton;
};

The link could look like this and it could be placed anywhere inside the page HTML:

<a href="#" class="hn-linkbutton" target="blank" data-url="http://docker.io/" data-title="This is the title">Submit to Hacker News</a>

The data-url and data-title attributes are optional. If not present, the page’s current URL and title are used.

Finally, to apply our enhancement to the link, we use this little code snippet:

$(document).ready(function(){
    $('.hn-linkbutton').hnButton();
});

See it in action and play with the code in JSFiddle.

The beauty of this solution is that the button is a normal page element that can be styled just like anything else using CSS. It resizes and scales with the rest of the page. We can even apply our own little responsive helpers to make the button work well on mobile and touch devices. Check out the source of this page to see how we apply a different text to the button depending on the device size. Hint: we use Bootstrap’s hidden-xs and visible-xs.

Of course, the best thing you can do now is check our implementation by hitting the actual button below and go discuss it directly on HN. :)

Feel free to use the code, improve on it, and let us know about your thoughts in the comments or on Hacker News.