Dr Nic

DIY widgets – How to embed your site on another site

UPDATE: Demo and tutorial fixed for IE7.

UPDATE: Opera-supporting code.

I used to wonder how Google AdSense worked. How did those ads from over at Google get on another site over here? This article ignores how Google did it and shows you how you can do it. That is, shows you how to embed JavaScript widgets on other people’s websites.

Demonstration

As a demonstration, the following yellow box and text is dynamically generated from a 3rd party web server without using Ajax:

The embedded code

Only one line of code is required to activate the widget to perform the preceding trick:

<script src="http://drnicwilliams.com/wp-content/uploads/2006/11/xss_magic.js"></script>

You can copy that line into any HTML page (even a static file on your computer) and it will generate the same HTML view. Sexy magic.

Cross Site Scripting

The ability for Google and you to embed dynamic content on someone else’s site is called Cross Site Scripting (XSS). If you read the Wikipedia page, the first sentence announces warnings and dire consequences:

Cross-site scripting (XSS) is a type of computer security vulnerability typically found in web applications which allow malicious web users to inject HTML or client-side script into the web pages viewed by other users.

XSS rocks and Wikipedia is wrong

So, Wikipedia and Google differ in their thinking. I’m with Google. XSS rocks. Wikipedia is for 15 year olds who are too lazy to research their own essays.

Its one thing to provide users of your sites with a REST API or include microformats into your HTML, and let them get their dirty hands on your data.

But its another level of mutual lovey-dovey-ness entirely between for users to be able to embed something from your site into their site. Not just images, but dynamic, sex-on-a-stick content. Like Google AdSense, or Sphere (see the “Sphereit” icon at the bottom of any TechCrunch article), or all the various wacky Digg tools (where you can embed “Digg this” etc into your page and look supercool).

How to do this

If you’ve been diligent enough to learn Javascript, you too can do this at home without parental supervision.

Things you’ll need

The following things are lying around on the internet for you to use:

  1. A copy of someone else’s website, say like an article from Ajaxian [1]. This will be called the mock page.
  2. Some data you want to embed into the site. Relax, it will look pretty when the users sees it. Your data must be in JSON format. But I’ll explain this later on.

The run-thru of what will happen

The user will load up the webpage (e.g. Ajaxian mock page) that has a small <script src="http://yoursite.com/magic_xss.js"></script> snippet in it [2]. When the page is loaded, the magic_xss.js file is loaded too. The user doesn’t know nor care.

When the magic_xss.js file is loaded it will do a couple of things:

  1. Install any stylesheets it needs
  2. Insert an empty, invisible HTML element into the page (e.g. <div id="my_magic_xss" />).
  3. Read in any variables (e.g. Google Adsense requires the website owner to specify a number of variables, such as google_ad_format)
  4. Fetch any additional Javascript files or data. This is where even more dynamic magic can be performed. When requesting the additional data, you could pass back the current document’s URL or the current users’s IP address, and the webserver could return data that is relevant to that URL or IP address/geographic location. Clever, eh.
  5. Insert new HTML into the #my_magic_xss element based on the data that is returned from your own server. Your server – not the host website’s server.

XSS is not Ajax

Technically speaking, Ajax uses the XmlHttpRequest object within the browser to fetch content. Unfortunately, in all browsers, it can only fetch content from the same server as the webpage.

That’s why XSS is interesting – we’re fetching data from a 3rd party server. Your server. So, we can’t use XmlHttpRequest.

The tutorial

Hereafter is the tutorial to explain all this. No specific webserver is required for the tutorial – the data returned will be a static file, though you will implement this with your own webapp to be dynamic.

The data

Later on in the tutorial we’ll need some data to represent the dynamic content from your server. For the sake of the demo, you can use my data: http://drnicwilliams.com/wp-content/uploads/2006/11/people_list.js

Remember, in real life this would be a dynamic server request returning dynamic JSON data.

Here’s the JSON we’re going to be using:

MyMagicXss.serverResponse(['Dr Nic', 'Banjo', 'Angus']);

Down the track, this bit of Javascript will be invoked on the browser. It will look for the function MyMagicXss.serverResponse and invoke it with the Array ['Dr Nic', 'Banjo', 'Angus']. The server function would be the same each time. The data would be different.

The embedded javascript

The point of showing you the dynamic data first is to show us that we need 3 things:

  1. A way to fetch and evaluate remote javascript without using XmlHttpRequest
  2. A function MyMagicXss.serverResponse
  3. A place to display these results on the host webpage

These three things are done by the original page that is loaded into the host webpage when the user visits. For our tutorial, this is the xss_magic.js file: http://drnicwilliams.com/wp-content/uploads/2006/11/xss_magic.js

This file looks like:

MyXssMagic = new function() {
  var BASE_URL = 'http://drnicwilliams.com/wp-content/uploads/2006/11/';
  var STYLESHEET = BASE_URL + "xss_magic.css"
  var CONTENT_URL = BASE_URL + 'people_list.js';
  var ROOT = 'my_xss_magic';

  function requestStylesheet(stylesheet_url) {
    stylesheet = document.createElement("link");
    stylesheet.rel = "stylesheet";
    stylesheet.type = "text/css";
    stylesheet.href = stylesheet_url;
    stylesheet.media = "all";
    document.lastChild.firstChild.appendChild(stylesheet);
  }

  function requestContent( local ) {
    var script = document.createElement('script');
    // How you'd pass the current URL into the request
    // script.src = CONTENT_URL + '&url=' + escape(local || location.href);
    script.src = CONTENT_URL;
    // IE7 doesn't like this: document.body.appendChild(script);
    // Instead use:
    document.getElementsByTagName('head')[0].appendChild(script);
  }

  this.serverResponse = function( data ) {
    if (!data) return;
    var div = document.getElementById(ROOT);
    var txt = '';
    for (var i = 0; i < data.length; i++) {
      if (txt.length > 0) { txt += ", "; }
      txt += data[i];
    }
    div.innerHTML = "<strong>Names:</strong> " + txt;  // assign new HTML into #ROOT
    div.style.display = 'block'; // make element visible
  }

  requestStylesheet(STYLESHEET);
  document.write("<div id='" + ROOT + "' style='display: none'></div>");
  requestContent();
}

For the most part you should be able to reuse that and modify it as necessary.

No Javascript frameworks allowed

First point – we should not rely upon any Javascript frameworks (e.g. prototype or jquery) as they might conflict with any Javascript already active in the host page. So, our code is encapsulated in an object MyXssMagic which we hope is unique on any host page.

Step one – install stylesheets

  requestStylesheet(STYLESHEET);

So that your embedded HTML looks pretty you might want to preinstall your own stylesheets.

Step two – create the holder element

  document.write("<div id='" + ROOT + "' style='display: none'></div>");

This allows the code to subsequently know where to insert HTML. Here we’re going to install the HTML at the same location where the host inserted our <script src="..."> code.

Step three – request remote data

   requestContent();

which invokes…

  function requestContent( local ) {
    var script = document.createElement('script');
    script.src = CONTENT_URL;
    // IE7 doesn't like this: document.body.appendChild(script);
    // Instead use:
    document.getElementsByTagName('head')[0].appendChild(script);
  }

We’re going to load our data by creating a new <script src="..."> element into the page. We can’t use XmlHttpRequest, so this is all we have. This means that the response must be pure Javascript, and must invoke a callback function so that the browser does something with the data.

Step four – process response

The browser automatically executes the returned data (introduced earlier):

MyMagicXss.serverResponse(['Dr Nic', 'Banjo', 'Angus']);

This invokes our callback function:

  this.serverResponse = function( data ) {
    if (!data) return;
    var div = document.getElementById(ROOT);
    var txt = '';
    for (var i = 0; i < data.length; i++) {
      if (txt.length > 0) { txt += ", "; }
      txt += data[i];
    }
    div.innerHTML = "<strong>Names:</strong> " + txt;  // assign new HTML into #ROOT
    div.style.display = 'block'; // make element visible
  }

This inserts “Names: Dr Nic, Banjo, Angus” into the element #my_magic_xss, as per the demostration at the start of the article.

Magic!

Epilogue – dynamic interactions

Loading dynamic data and adding HTML into a host page is easy, as shown. Supporting user interactions, where you dynamically load new data and show new HTML, is slightly more complicated.

Remember the rule: the returned JSON data must be wrapped in a callback function (e.g. MyMagicXss.serverResponse(…data…)).

[1] I suggest Ajaxian.com to you because I fancy they might give me something free for doing it. Here’s hoping.

[2] This snippet got into the page because the authors of the site embedded it for you. You can’t make them. But copying a bit of text into a website template is common place thanks to Google AdSense etc, so unless your target audience is 1800th century quilting website owners, you should be fine. Write up some instructions, and give them the small chunk of HTML that they need to embed.

Related posts:

  1. Using CoffeeScript in Rails and even on Heroku I’m pretty excited about CoffeeScript as a clean-syntax replacement for...
  2. Autotesting Javascript in Rails I used to love Javascript so much that it would...
  3. Syntax Highlighting in Tumblr I wrote an email to the lovely Tumblr people asking...
  4. Supporting JSON callbacks in Rails Now you know how to write JavaScript widgets, now you...
  5. Debugging Javascript in IE7 – how to clear your Javascript cache JavaScript first appeared in December 1995 within the Netscape browser....

80 Responses to “DIY widgets – How to embed your site on another site”

  1. Dr Nic says:

    @jd – if its a worry for you, you could give them ids or class names and then find them via document.getElementById etc, then remove them from their parent. They are just normal DOM nodes.

  2. Ramon Jaime says:

    This one makes sence “One’s first step in wisdom is to kuesstion everything – and one’s last is to come to terms with everything.”

  3. Rimi says:

    Dear Dr, Nic. thanks for the great tutorial.
    I am a fresh developer, and have meagre knowledge of the field.I wish to know however, how do you reverse the above technique? How do I embedd a client’s dynamic form(built using ASP.net) on my website, simultaneously updating my database on each fill of the form?
    Please find time to answer this soon.

  4. Dr Nic says:

    Rimi – this is truly a different problem space and beyond the realm of a simple answer. The simplest solution is for your own server to render a similar form as the 3rd party form, store the data and then forward/POST the data to the 3rd party server. Off the top of my head, I can’t think of another solution. It’s a very difficult problem to consider chopping up a 3rd party sites’ page but include its CSS and Javascript, AND add your own Javascript.

    Go the simple route – do your own form and forward the results on behalf of each user.

  5. mel says:

    Excellent, just excellent.

  6. Harrison says:

    Hi, Dr Nic, I tried to use build simple user interaction base on your code.
    I add a , the init function got called, but the browser complains that the response’s MyXssMagic object no found.
    Could you give me some hint about the user interaction part?
    Thanks.

  7. Harrison says:

    Hi, Dr Nic, I tried to build simple user interaction base on your code.
    I add a button that call the MyXssMagic.init() onClick. the init function got called, but the browser complains that the response’s MyXssMagic object no found.
    Could you give me some hint about how to build the user interaction?
    Thanks.

  8. Dude, you rock. In like 2 minutes I bookmarked your blog. In your tagline: “Javascript makes Ajax” — you know how many times I’ve heard people say they program //in// Ajax?

    And then: “Wikipedia is for 15 year olds who are too lazy to research their own essays.” So true.

    Awesome.

    …now to finish reading your article…

  9. [...] Wearing a practical hat for a while, maybe I should ask if you had a look/view/etc. regarding XSS @ all?? [...]

  10. guy says:

    Dr Nic, adsense use a pre indexed version of a page so google only need to serve a pre made iframe on demand, the scenario you suggest is different. here we can interact with the data and do everything ajax does on our own site through a widget/badge on a 3d site.

    how does other widgets implement interactivity when embedded into 3d part sites? except flash and XSS isn’t there another option?

  11. Bill says:

    Dr. Nic—have you thought much about the other way ’round, i.e. embedding another site into my site. I’m looking at Dapper.net which does that. Is there a ready-made widget to do that browser-within-a-browser thing. Frames is out because I’d like my app to “see” into the embedded document. I’d at least like to see the location.href of the embedded doc so I could follow where the navigation goes.

  12. Dr Nic says:

    @Bill [via] – you’d need to rebuild the foreign site within the target site I think

  13. I dont really get how to get dynamic data from a php script…. are u saying that
    the CONTENT_URL can link to a php file, I thought it was only a javascript file that was linkable toooo

    function requestContent( local ) {
    var script = document.createElement(‘script’);
    script.src = CONTENT_URL;
    // IE7 doesn’t like this: document.body.appendChild(script);
    // Instead use:
    document.getElementsByTagName(’head’)[0].appendChild(script);
    }

  14. i want to create a small adnetwork like google and I am looking @ doing it with this code, but I really dont know how to link to my php file which will create the file and publish the ad……….and also publish the data through the javascript you have above…..

  15. monozub says:

    Hey DrNic,

    Dude, that was an awesome article (usually I get so much from your posts), that absolutely misled me. In reality it can be done in a very simple way without getting away from rails’s format and getting in to other technologies. I created a page on my wiki that shows u how to do they same thing using pure rails with js just for the presentation.

    http://wiki.m001.net/technical/show/WidgetsAndRails

  16. Dr Nic says:

    @monozub [via] – heh, sorry to mislead you :) Thx for the extra wiki page.

  17. raza says:

    Its really full of knowledge article and nice tricky tips to handle XSS, very handy post for me. thanks :)

  18. andrew says:

    Anyone try putting 2 of the same script embeds in an html page? IE only shows one of them. Can someone explain why?

  19. andrew says:

    I should add….even if I designate different element ids. It seems like it is a timing thing for IE. If I add waits in, the 2 will display.

  20. [...] Nic has written a great article with example on how to build your own website javascript widget embedding [...]

  21. [...] DIY widgets – How to embed your site on another site [...]

  22. Josh says:

    This method looks great.

    I am trying to make a widget that will be able to be put on any website. It simply will display text/link based on the current day.

    So my content (day_list.js) needs has several entries…

    Not sure the best way to handle this…
    Say I have 100 entries and it’s on the 56th day. So the widget displays the 56th item but the user also needs to be able to scroll through the list of days…

    Is this possible using this method?

    Any help would be greatly appreciated.

  23. [...] is a very detailed explanation of how JSON callback works at this website. Yahoo developer network also got an article on this [...]

  24. [...] like a good thing to have a go at. Most of the techniques are based on code from Dr Nic’s DIY widgets – How to embed your site on another site article. However I tried to add a few more features/constraints into the [...]

  25. Joel says:

    Hi,

    This is a greatt post, and maybe i missed something, but you didn’t explain about interaction. Just about “dynamic data”
    If i want to insert a form in any web site by this technique. I will want to have a roundtrip to my server when user puts data in.

    Are there techniques to do that?

    Thank you

  26. [...] This technique is based on embedding a script <tag> on the host site that will refer to a script residing on your service. The script should then dynamically create the widget on the host site by manipulating the DOM and adding stylesheets and script tags as needed. Dr. Nic gives a nice overview of this technique in this detailed tutorial. [...]

  27. harryd says:

    How do you avoid js_widget/user_site css conflicts

  28. Kamalakannan says:

    Really exciting article. I searched more than 5 hours for this. Finally i found this. Very Exciting article.. Hats Off

  29. Kamalakannan says:

    How can i give my html tag or div tag to insert or embed into other website to show my content. I used Object method but it failed to show in internet explorer. Am coding social sharing for my website i need to give that small code and script to other users to embed that social sharing widget. How can i do this help me pls