Generating live website thumbnail previews using an Iframe

I was looking for a good way to generate website thumbnail previews for the end-user which would load near instantly. With a quick Google search I came across services that can take a screenshot but they were neither free or fast. So I set out to create my own home brewed solution which looked something like this:

I’m going to use KnockoutJS which is a MVVM JavaScript framework for the purpose of this demo just for the convenience factor. You can do this in plain old JavaScript as well if you wanted to.

TL;DR: Scroll to the bottom for a link to the demo

HTML:

<div class="live-example">
  <input data-bind='value: myURL' />
  <button data-bind='click: initLinkPreview'>Show Preview</button>
  <!-- ko if: showPreview -->
  <iframe class="link-preview" data-bind="attr: {target: '_top', src: myURL}" sandbox='' scrolling="no" />
  <!-- /ko -->
</div>

Key part to note in the HTML is that we are loading the iFrame in a sandbox mode to make sure it loads the link with a white-list of restrictions to mitigate any risk of malicious activity.

CSS:

.live-example {
  height: 200px !important;
  overflow: hidden;
}
.link-preview {
  width: 1028px;
  height: 800px;
  border: none;
  pointer-events: none;
  -webkit-transform: scale(0.2);
  -moz-transform: scale(0.2);
  -ms-transform: scale(0.2);
  transform: scale(0.2);
  -webkit-transform-origin: 0 0;
  -moz-transform-origin: 0 0;
  -ms-transform-origin: 0 0;
  transform-origin: 0 0;
}

The main crux of this technique comes down to the transform CSS property which will allow us to render the URL in a full sized iFrame and then scale it down to a smaller size to make it look like a thumbnail. Also as you can see, the container class .live-example has a height restriction of 200px to hide the space that the browser will initially occupy to render a full-sized iFrame.

RoR:

def get_headers
  meta = open(params[:url]) { |f| f.meta }
  render :json => {status: "success", headers: meta}
end

I also created a back-end endpoint (in this case in RoR) to return the website headers to the front-end. This is to allow the front-end to detect if the site which we are loading allows itself to be rendered in an iFrame. Lot of mainstream sites such as Google and Yahoo explicitly set X-Frame-Options header to avoid Clickjacking attacks, etc. By doing this we can control the UX and make sure we either show a default thumbnail or hide the area in which we were supposed to render the iFrame in case the X-Frame-Options header was set to DENY or SAMEORIGIN.

JavaScript:

var MyViewModel = function MyViewModel() {
  var self = this;
  self.myURL = ko.observable();
  self.showPreview = ko.observable();

  self.initLinkPreview = function () {
    $.ajax({
      type: "POST",
      url: "/get_headers/url=?" + self.myURL(),
      success: function (data) {
        var headers = data.headers;
        if (headers['x-frame-options'] && (headers['x-frame-options'].toUpperCase() == 'DENY' || headers['x-frame-options'].toUpperCase() == 'SAMEORIGIN')) {
          self.showPreview(false);
        } else {
          self.showPreview(true);
        }
      }
    });

  };
};

ko.applyBindings(new MyViewModel());

So lastly the JS ties everything together. It grabs the URL from the front-end, checks with back-end to see if it’s kosher to render it in an iFrame and then either shows or hides the iFrame with that URL.

Demo: http://jsfiddle.net/2jv7n2q7/37/

If you have any questions or comments, please post them below. If you liked this post, you can share it with your followers or follow me on Twitter!