Mashups suck. You end up with all sorts of CSS and JS overlaps and conflicts... If only there was a sandbox element in HTML...
This is a work in progress, hence me adding it as a forum topic.
The Problem
Let's say you've got an enterprise web app. It's already going to be quite complex, pulling in various JS libraries and style sheets.
Now let's say you want to start adding in some widgets pulled from web services - those too have their own JS files and style sheets.
All sorts of problems occur when you do this, primarily because different teams and organisations are working in isolation. For example, you buy an enterprise web app from vendor A and decide to embed some widgets from your internal system and some other vendors, etc. To tie everything together you get some of your in-house developers involved along with some consultants and maybe some people from the vendor A and the people who made some of the widgets.
If you've ever encountered scenarios like these, you'll be aware of both the importance of making it work (ie. you'll have specific business goals in mind) and the utter pain and expense of achieving a successful, and more importantly, maintainable solution.
Let's wade through some of the common issues...
Let's say the outer page includes jQuery 1.2.6 and a widget contains jQuery 1.1.2. The widget's version of jQuery will generally overwrite the version used by the outer page.
There are ways to get around this problem but they are grim:
- Make the parent page run jQuery in noConflict(true) mode and create a unique reference to jQuery, eg. call it "fish" or "ksdhf824"
- How do you guarantee a widget won't use the same name?
- It's a PITA for developers who are used to referring to jQuery
- Don't include JS libraries in the widget, make it rely on the outer page
- Makes the widget harder to test because you need a sample "outer page" stub to include the libraries
- Makes it harder to embed the widget in to an outer page because you'll likely have to start modifying the outer page (which in itself can be a pain in the ass)
- What if the libraries used on the actual outer page are the wrong version or missing?
- If something goes wrong, what's responsible - the widget or the outer page?
- Use an <iframe> to isolate the content of the widget from the parent page
- See general gripes about iframes later
Let's move on to CSS - what if the widget includes styles that conflict with the outer page?
For example, the outer page could define:
ul, ol, li {
font-size: 11px;
}
And the widget could define:
body ul, body ol, body li {
font-size: 20px;
color: red;
}
Again, there are ways to get round these problems, but they are grim:
- Make the widget CSS very specific, eg. the widget has an outer <div> with a specific id attribute or a highly unique class
- If you use an id, how do you guarantee it won't be used in the outer page or by other widgets? Also, what if the widget is included more than once on a page?
- If you use a highly unique class, how do you guarantee it's not used by other widgets or the outer page?
- Prefixing all the styles defined by the widget with the id or class is tedious!
- By making the widget styles more specific, if you want to override them in outer page style sheet you need to make things even more specific or start messing with !important - again, grim.
- Don't include style sheets in the widget, make it rely on the outer page
- Makes the widget harder to test because you need a sample "outer page" stub to provide the styles
- Makes it harder to use the widget because the styles defined by the outer page need modifying (which can be painful in enterprise apps)
- What if the styles defined by the outer page are wrong or missing?
- What if the widget changes and needs new styles?
- If something goes wrong, what's responsible - the widget or the outer page?
- Use an <iframe> to isolate the content of the widget from the parent page
- See general gripes about iframes later
Without resorting to using an <iframe>, just two basic aspects of the overall UI (JS and CSS) cause extreme pain during implementation and result in something terribly painful to maintain.
This is before we even consider security issues, bad coding (eg. miss a few closing tags in the widget and the whole page layout breaks), etc.
The normal way of getting round these problems is to use an <iframe> - but they are grim too:
- Style sheets in the outer page can't be use to customise the design of the contents of the <iframe>
- An <iframe> requires an external source - you can't just dump some inline HTML in to it
- When traversing the DOM of the outer page, you can't readily see in to the contents of the <iframe>
- You can't easily control what aspects of the parent page are made available to the contents of the <iframe> (as far as I'm aware?)
Enter the Sandbox
A <sandbox> would be quite different from an <iframe>:
- CSS defined in the outer page can override css defined in the <sandbox> easily (using new CSS syntax)
- The sandbox would "feel" more like a <div> than an <iframe>, but still ensure that any CSS and JS inside the <sandbox> wouldn't affect the outer page
- The DOM in the <sandbox> would be part of, and traversable from, the DOM of the outer page
- The sandbox content can be defined via a src attribute (just like an <iframe>) or in the element body
CSS
Any CSS defined in the <sandbox> is scoped to the contents of the <sandbox> (not the <sandbox> itself).
By default, the parent CSS doesn't affect the contents of the <sandbox> - it might mess up what's in there.
To get CSS on the outer page to affect the contents of a sandbox, you have to specify the "sandbox" element name in the style sheet:
sandbox, sandbox#foo, sandbox.bar {
/* styles that customise the <sandbox> element itself */
}
@ammend sandbox, sandbox#foo, sandbox.bar {
/* styles that affect content within the sandbox, for exmple: */
ul, ol, li {
font-size: 11px;
color: default;
}
}
@replace sandbox, sandbox#foo, sandbox.bar {
/* styles that replace styles within the sandbox, for exmple: */
ul, ol, li {
font-size: 11px;
color: default;
}
}
The @ammend keyword is required to allow styles defined in the outer page to start having an effect on the <sandbox>.
The @replace keyword is something I've always wanted - it would instruct the browser to remove styles relating to specific elements, classes, etc., and optionally set new styles for them.
Let's say someone's defined this in a style sheet I can't control:
ul {
font-size: 10px;
color: red;
}
ul.foo {
font-size: 20px;
color: blue;
}
Then I call this in my style sheet:
Suddenly ul.foo gets 10px red text - the style for ul.foo has been deleted and replaced with nothing.
I could alternatively do:
@replace ul.foo {
color: green;
}
In which case ul.foo is deleted and replaced with "color: green" style so now it shown as 10px green text.
The big win for @replace is that you don't have to "un-do" existing styles for something, you can just get rid of them. It's a CSS-based way of saying "look for any prior definitions of this style and delete them, then replace with this new style instead".
Maybe we could just apply some of these ideas to iframes instead?
That's all for now, back to work.
Douglas Crockford suggested a similar idea:
http://json.org/module.html