DIY widgets – How to embed your site on another site

Posted by Dr Nic on November 21, 2006 and blessed with 77 comments

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. Autotesting Javascript in Rails I used to love Javascript so much that it would...
  2. Syntax Highlighting in Tumblr I wrote an email to the lovely Tumblr people asking...
  3. Supporting JSON callbacks in Rails Now you know how to write JavaScript widgets, now you...
  4. Debugging Javascript in IE7 – how to clear your Javascript cache JavaScript first appeared in December 1995 within the Netscape browser....
  5. Prototype: “element-id”.$() instead of $(‘element-id’) The Prototype library gives us the $() operation for converting...
Trackbacks

Use this link to trackback from your own site.

Comments

Leave a response

  1. Adam Keys Wed, 22 Nov 2006 00:48:33 UTC

    So I was looking into this a couple weeks ago. After mulling through some obfuscated/crunched JavaScript, I found that Google was using IFRAMEs to make the remote request. Got any insight into when one would use SCRIPT elements versus IFRAMEs?

  2. evan Wed, 22 Nov 2006 02:48:38 UTC

    Foreign-site iframe contents can’t be manipulated by the Javascript of the enclosing frame (at least, without special callbacks in the iframe itself). This gives Google a bit more control over the ad box, especially over the presentation, which will not be affected either by host site CSS or by Javascript attribute assignment.

  3. chris Wed, 22 Nov 2006 03:33:14 UTC

    Dude, you are SO on a roll. Keep up the great work….onward, LazyWeb!

  4. Peter Cooper Wed, 22 Nov 2006 04:15:37 UTC

    Depending on what you control (or don’t control), of course, you can get your service to spit out the actual document.write goodness around whatever it is you want to show at the application end as well, and avoid all this JS code ;-)

  5. John Nunemaker Wed, 22 Nov 2006 04:28:15 UTC

    What’s sweet is you can do so much more than just what you mentioned above. At Notre Dame, we don’t have access to any dynamic languages on a major portion of the web space so we are actually using a technique similar to this to allow search results to be show in the site template. An example can be seen here: http://globes.nd.edu/search/. Try searching globes.

    The aforementioned page includes a coldfusion file which pretends to be a javascript and unobtrusively overrides the default form behavior of posting to the Notre Dame search engine. Then if a query string is in the url, we actually include another script with a javascript variable storing the search results.

    This allows us to do dynamic coding on completely static websites. So yep, I agree with you that there is a lot of power in this technique.

  6. Thomas Ptacek Wed, 22 Nov 2006 07:13:57 UTC

    Be careful. You’re not pointing out that XSS isn’t a problem, you’re pointing out that XSS is a really, really dumb name for the security vulnerability. I agree, but my head still a splodes when I see you naming your widgets *_xss.js.

    The “scary” thing Wikipedia is talking about is Javascript Injection, where you allow some total untrusted third-party, such as a blog commenter, to provide content that will get interpreted as a script by subsequent site visitors.

    But normal people don’t call that attack Javascript Injection, they call it XSS, and XSS is _not_ OK, for obvious reasons.

  7. Jeroen Zwartepoorte Wed, 22 Nov 2006 10:22:27 UTC

    This page doesn’t display in IE7. You get a dialog box saying “Internet Explorer cannot display this webpage; Operation Aborted”.

  8. Dr Nic Wed, 22 Nov 2006 10:32:16 UTC

    Woah. I disabled the demo and its now viewable in IE7, so the XSS is the source of the problem. Perhaps it will need to be revised to include IFRAMEs for IE7. Interesting.

  9. Dr Nic Wed, 22 Nov 2006 11:53:25 UTC

    IE7 hates me. Its caching the javascript files and no amount of “Delete All Files…” can consistently refresh them to allow me to debug. Really – this is “fist through the monitor”-level of frustrating. Do Microsoft even use their own browser for development?

    Looks like I’m not the first person to discover IE7 javascript caching is dodgy:

    I didn’t find any option in IE7 to fiddle with JS caching.

    Anyone master the art of debugging Javascript in IE6/7?

    Until then, I have added an iecheck() function to the script to disable the script on IE. I can’t test it this works of course, but it looks good…

  10. [...] Dr Nic Williams has written up a piece on how to embed your components on another site using a XSS approach instead of an iframe one. [...]

  11. Chris Wed, 22 Nov 2006 17:38:32 UTC

    I’m in your javascript, xssin’ your website.

  12. daniel Wed, 22 Nov 2006 20:49:52 UTC

    I had the same issue the other day with IE7 and caching. I went and deleted the file manually to fix the issue.

  13. [...] So did Daniel. He also had a solution. Good man, Daniel. [...]

  14. [...] 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: Install any stylesheets it needs Insert an empty, invisible HTML element into the page (e.g. <div id=”my_magic_xss” />). Read in any variables (e.g. Google Adsense requires the website owner to specify a number of variables, such as google_ad_format) 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. 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.Continue for more info…. [...]

  15. To be a ajax expert » 重温js跨站点通讯 Thu, 23 Nov 2006 07:59:02 UTC

    [...] 同时,大家可以比较一下,我构建的这个跨域通讯方案与这篇文档的方案哪个比较方便使用。 [...]

  16. Dr Nic Thu, 23 Nov 2006 08:19:23 UTC

    IE7 problem now resolved and article + demo updated.

    What IE7 doesn’t like in the requestContent method:

      document.body.appendChild(script);
    

    Instead we use:

    document.getElementsByTagName('head')[0].appendChild(script);
    
  17. Bas Wenneker Thu, 23 Nov 2006 12:18:24 UTC

    Great article! Actually I was working on something like this some time ago but I’ve never finished it. I’m now working on a script that uses iframes and public proxies to execute javascript on a website given by the user, and display the results. The problem is that a lot of browsers don’t support dynamically created iframes. Script tags have better support…

  18. Dr Nic Thu, 23 Nov 2006 12:35:13 UTC

    @adam – I need to reexplore what I can get with iframes. Back when I first started learning all this, I noticed/read that iframes ie6 caused a clicking sound, and then ppl “discovered” Ajax, so that was that: iframes went out of fashion.

    But, its really something I want to explorer more thoroughly for the limitations + benefits of using/not using iframes.

  19. Dr Nic » Supporting JSON in Rails or anywhere Thu, 23 Nov 2006 13:52:15 UTC

    [...] Now you know how to write JavaScript widgets, now you need to know how to help others write widgets for your data. [...]

  20. [...] DIY widgets – How to embed your site on another site (tags: widgets Javascript json Programming xss) [...]

  21. links for 2006-11-23 « Sri’s Blog Fri, 24 Nov 2006 01:18:27 UTC

    [...] Dr Nic » DIY widgets – How to embed your site on another site (tags: widgets embed JavaScript webdev) [...]

  22. links for 2006-11-23 at SridhaReena Fri, 24 Nov 2006 01:21:02 UTC

    [...] Dr Nic » DIY widgets – How to embed your site on another site (tags: widgets embed JavaScript webdev) [...]

  23. [...] Dr Nic » DIY widgets – How to embed your site on another site How to embed your site on another site (tags: json Javascript xss widget programming webdesign code) [...]

  24. Adam McIntyre Thu, 30 Nov 2006 19:44:13 UTC

    Great technique and a very well written tutorial!

    Sounds to me like the IE7 error is a result of the script trying to append an element to the body before the body has finished loading. (Don’t trust that answer 100% as I didn’t try to debug too seriously, but that’s usually the cause for “Operation Aborted”).

    Here’s an MSDN Channel 9 post describing the (extremely annoying) problem: http://channel9.msdn.com/ShowPost.aspx?PostID=215369

  25. solutoire.com | Sat, 02 Dec 2006 18:36:26 UTC

    [...] Dr. Nix Williams wrote a nice article on how to embed your widgets on another site using XSS. [...]

  26. shagoo Thu, 07 Dec 2006 10:22:33 UTC

    [总结] 关于 JavaScript 跨站点/域通讯的资料…

      最近在一个项目中要求实现一个给网站用户提供消息定制的服务,即给用户提供一个 Javascript 脚本,让网站用户可以通过操作定制一些有用的信息(具体内容暂时不便透露,请谅解:)。…

  27. DIY Widgets: XSS components to other sites Mon, 08 Jan 2007 16:52:14 UTC

    [...] Dr Nic Williams has written up a piece on how to embed your components on another site using a XSS approach instead of an iframe one. [...]

  28. Yuval Levy Mon, 15 Jan 2007 19:18:30 UTC

    Excellent tutorial and fantastic simple and easy technique. I’ve been toying with it and the only flaw I found is with Opera that reports a ReferenceError on the MyXssMagic.serverResponse call from people_list.js.

  29. Dr Nic Mon, 15 Jan 2007 23:24:34 UTC

    @yuval – damn. Let me know if you figure out what technique works for Opera.

  30. Yuval Levy Wed, 17 Jan 2007 03:14:35 UTC

    I got it working with a horrible hack that defeat the purpose of such a neat clean code. I am not sure if it will work with the above example, I am using this technique to pass a single HTML string, not an array.

    The hack: I assign the server response to a variable and call the MyMagicXss.serverResponse again from the embedding page.

    For your example

    1) the embed a few more lines of code in the target page:

    if(isOpera){
      MyMagicXss.serverResponse(your_variable);
    }
    

    2. in the source script, _add_ that variable, i.e. people_list.js looks like

    var your_variable = ['Dr Nic', 'Banjo', 'Angus'];
    if(!isOpera){
      MyMagicXss.serverResponse(your_variable);
    }
    

    the serverResponse in the main document does not work in IE, so the response in two different places is necessary. A double response does not seem to bother any browser (well, they report an error on the page, but who notices that?), so the check condition whether this is opera or not is optional, at least in my case where the serverResponse just replaces the innerHTML of the same element, worse case it does it twice.

    regards from freezing cold Quebec (-17°C and sinking).
    Yuv

  31. Nick Fri, 02 Feb 2007 03:50:46 UTC

    Hey Dr. Nic -
    Say I wanted to use your method to get dynamic data from a database. Meaning the page that I am requesting content from (people_list.js in your example) is dependent on the user requesting the content (they will be passing an id number). Any thoughts on how to get that working? Right now, my service uses the old iframe method but a lot of people are unhappy with this as they want to be able t change the look of the content and this is not possible with an iframe. Any thoughts?
    -Nick

  32. Dr Nic Fri, 02 Feb 2007 05:34:58 UTC

    @Nick – your server code (cgi, rails, whatever) would return JSON format data (within the callback method). So you’d pass the request parameters in the URL (instead of requesting a static people_list.js file) and it returns JavaScript.

    e.g.

    <script src=".../people/45?format=json&callback=update"></script>
    
  33. Nick Fri, 02 Feb 2007 05:36:34 UTC

    Dr Nic -
    Ah Ok…so I would call dynamic.php?id=10&var=blah and format the output as JSON? Genius :)
    -Nick

  34. dbai Sat, 10 Feb 2007 22:33:40 UTC

    Hi Dr. Nic,

    This is David Bai from Taiwan. I love this article, I wish I can translate it into Chinese, so I appreciate for your approval. I will notify you the address of the translated edition in case you allow me to do this. Thank you and your articles very much!

  35. Dr Nic Sun, 11 Feb 2007 01:05:43 UTC

    @dbai – you are welcome and encouraged to translate all articles if it helps Chinese Rubyists.

  36. [...] 前幾天想找找有沒有人發表怎樣製作Web-based widget的心得,就看到了這一篇文章。它裡面完全沒有用到伺服器端的技術以及Ajax,純粹都是簡單的Javascript。我覺得很不錯,如果想從頭開始開發widget而不想直接援引現今流行的widget tool(如Yahoo!、Google、Widgetbox…都有,不勝枚舉),我覺得是非常好的一篇教學。所以特地在徵得原作者同意之後,翻譯出來以饗大家。 [...]

  37. dan Tue, 13 Feb 2007 17:24:00 UTC

    pretty neat magic, Dr Nic :-)
    thanks!

  38. Rob Sun, 18 Feb 2007 00:39:16 UTC

    Here’s a version fixed for Opera. It didn’t like the way the MyXssMagic object was defined, apparently.

    http://pastie.caboo.se/41097

    Tested on Opera9, Fx2, and IE7

  39. Dr Nic Sun, 18 Feb 2007 09:56:00 UTC

    @Rob – cheers – added update to top of article.

  40. Obi Thu, 01 Mar 2007 22:06:01 UTC

    Hello Mr. Magic:

    I was hoping you could help me out here.

    I want to AJAXify a page that has a charting component (ILOG) which unfortunatley writes some tags and divs using document.write(). Because of this I have problems when I try to load the chart (javascripts) via AJAX.

    Help!!!! Please

  41. JP Thu, 15 Mar 2007 12:35:10 UTC

    Hi, I am very new at this, perhaps you can help me? I’m trying to grab data from rails instead of an array for people_list.js, so instead of this:
    MyXssMagic.serverResponse(['Dr Nic', 'Banjo', 'Angus']);
    I have this:
    MyXssMagic.serverResponse(http://localhost:3000/widget/getlist/1);

    It does not work, nothing is returned. If browse to the url, I do get data back. Any ideas? Thanks

    Here’s a sample of the data I get get from the url I’m passing in:

    [{"attributes": {"image_url": "/images/book.jpg", "price": "20.00", "title": "2nd new book", "id": "5", "description": "ABook"}}]

  42. Dr Nic Thu, 15 Mar 2007 12:51:39 UTC

    @jp – you’d invoke the call to rails via:

    <script src=".../widget/getlist/1">

    and in turn, the result returned would be some javascript:

    MyXssMagic.serverResponse([{'attributes': {'image_url': '/images/book.jpg', 'price': '20.00', 'title': '2nd new book', 'id': '5', 'description': 'ABook'}}])
    

    You could use an rhtml/erb view, use a RJS view, or the minus_mor plugin.

  43. Tsung's Blog Sat, 17 Mar 2007 06:44:51 UTC

    好文: SQL Injection Cheat Sheet(XSS)…

    最近很流行 Cheat Sheet, 把一些常用該知道的東西整理好的小抄(或許也可以說是懶人包?), 印出來立在桌邅

  44. 重温js跨站点通讯 : To be a RIA expert Mon, 19 Mar 2007 05:57:22 UTC

    [...] 同时,大家可以比较一下,我构建的这个跨域通讯方案与这篇文档的方案哪个比较方便使用。 [...]

  45. Ron Sat, 31 Mar 2007 00:04:22 UTC

    Hello Dr. Nic,

    I’m making progress on implementing this for my own use. However, my skills are very meager when it comes to Javascript, and I’m hoping you can help me solve this last problem I’m having. I have a block of code that I’m dynamically placing through the javascript on the users page. However, I need it to be unique for that user, in that it will have the users name, and a piece of information about that user. For instance, I would want it to say, “My name is John, and I’m 37 years old”. I would need ‘John’ and ‘37′ to be dyamically put in the html code which I’m calling.

    I would like to achieve this in a similar manner as Google Adsense, where the user can define a set of variables as they please right there in the code that accompanies the “<script src="http://yoursite.com/magic_xss.js”></script>” tag. If I could define the variables in an accompanying javascript element running “sidecar” to that tag, if you will, that would be tremendous. Any ideas?

    Thanks!

  46. Ed Ruder Wed, 04 Apr 2007 10:02:12 UTC

    Dr. Nic,

    Thanks for this excellent guide! I’ve taken your code and run with it, learning a lot about the DOM, JavaScript, and the vagaries of different browsers’ implementation of both!

    I encountered the problem with Opera that’s been described previously and, I believe, the same problem in Safari. While both solutions described above undoubtedly work (one of which is in your updated Opera code), I wasn’t satisfied with just taking them without understanding better why they worked. As a result, I think I understand why the code doesn’t work in Opera, as well as a simple way the original code can be modified to work on Opera and Safari and all of the original browsers that it worked on!

    The change: wrap the last three lines of code in the MyXssMagic object in a method that’s defined (not executed) when the MyXssMagic is constructed, then call that method following the code that constructs the MyXssMagic object.

    MyXssMagic = new function() {
    
    ...
    
      this.init = function() {
        requestStylesheet(STYLESHEET);
        document.write("");
        requestContent();
      }
    };
    
    MyXssMagic.init();
    

    The difference between the JavaScript engine in Opera/Safari and that in Firefox/IE is that in Opera/Safari, the MyXssMagic object apparently isn’t defined until *after* the constructor completes. But since the constructor itself calls code (the requestContent() call) that in turn calls MyXssMagic.serverResponse(), it breaks on Opera/Safari!

    At least, this is what I found in my code, that’s based on the code in this article.

    Thanks, again, Dr. Nic!

    Ed

  47. Mark Wed, 18 Apr 2007 10:33:28 UTC

    Thank You

  48. Anthony Fri, 27 Apr 2007 18:53:03 UTC

    Hello – this is a super useful article. One question I have though – how would you support embedding multiple “widgets” (of the same type) on the same page. The approach described here would seem to have problems because of the same function names and id’s. Any suggestions on the best way to approach this would be most appreciated.

    thanks!

  49. Dr Nic Fri, 27 Apr 2007 22:30:45 UTC

    @anthony – you’ll need separate places where you’ll update the widget content, that is, elements.

    ...

    The callback code would need to decide which widget needs to be updated,

  50. jd Mon, 30 Apr 2007 03:00:45 UTC

    Thanks for the great tutorial. I have a question however about trash collection? How would one go about remove the created script tags after they have been included & executed?

  51. Dr Nic Mon, 30 Apr 2007 07:28:07 UTC

    @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.

  52. [...] W3c widgets requirements [...]

  53. Ramon Jaime Thu, 14 Jun 2007 11:09:30 UTC

    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.”

  54. Rimi Sat, 16 Jun 2007 14:00:58 UTC

    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.

  55. Dr Nic Sat, 16 Jun 2007 16:14:30 UTC

    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.

  56. mel Mon, 23 Jul 2007 23:13:26 UTC

    Excellent, just excellent.

  57. Harrison Mon, 13 Aug 2007 23:39:59 UTC

    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.

  58. Harrison Mon, 13 Aug 2007 23:42:33 UTC

    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.

  59. Dean Higginbotham Sat, 01 Sep 2007 04:57:46 UTC

    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…

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

  61. guy Mon, 31 Dec 2007 22:13:34 UTC

    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?

  62. Bill Sat, 01 Mar 2008 10:20:53 UTC

    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.

  63. Dr Nic Sat, 01 Mar 2008 11:06:26 UTC

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

  64. nnamdi okechukwu Mon, 03 Mar 2008 04:03:04 UTC

    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);
    }

  65. nnamdi okechukwu Mon, 03 Mar 2008 04:05:07 UTC

    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…..

  66. monozub Thu, 03 Apr 2008 00:42:40 UTC

    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

  67. Dr Nic Thu, 03 Apr 2008 21:30:06 UTC

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

  68. raza Thu, 17 Apr 2008 06:44:49 UTC

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

  69. andrew Sat, 30 Aug 2008 04:10:10 UTC

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

  70. andrew Sat, 30 Aug 2008 05:38:19 UTC

    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.

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

  72. SQL Injection/XSS Cheat Sheet | 鬼仔's Blog Sun, 09 Nov 2008 00:35:45 UTC

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

  73. Josh Wed, 14 Jan 2009 03:08:51 UTC

    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.

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

  75. [...] 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 [...]

  76. Joel Wed, 29 Jul 2009 19:36:00 UTC

    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

  77. Creating embedabble widgets « techfounder Fri, 05 Feb 2010 13:36:07 UTC

    [...] 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. [...]

Comments