Thursday, August 18, 2005

Using JavaScript to dynamically add Portlet CSS stylesheets

I work as portal developer, currently we use uPortal 2.X but I keep an eye towards future implementations with stronger support for JSR168 portlets. I always endeavour to create (X)HTML standards compliant web applications but sometimes you just have to do whatever it takes in the short term.

First a little portlet and portal background...

Portlets are web applications that, usually, produce little fragments of (X)HTML. Portal software is responsible for assembling these fragments into a web page (in uPortal 2.X speak the portlet mechanism is called a channel). In recent history two related portlet specifications, JSR168 and WSRP, have been created to produce a cross platform standard portlet interface. So in theory once a portlet has been created it should work in any JSR 168 compliant portal.

In uPortal 2.X a portal page is constructed by collecting (and coercing by using a Java version of Dave Raggett's Tidy utility) all portal content into XML. Once the XML fragments have been collected a series of XSL transformations occur to build the page.

The problem...

Any good portal software will allow the user to customise their portal, so each user can receive a true personalised experience of the portal. This means that there is almost no telling what portlets might appear on a particular page. Also, the new portlet standards will make it possible to remotely host portlets and therefore the portal platform maintainers may not have any prior knowledge of the format which the portlet will take. Sure there are certain CSS conventions for the most common portal components but there will be times when some clever DHTML rendering mechanism, branding or such will be part of a portlet.

One thing that has always bothered me with respect to portals is that portlets and uPortal channels (remember these are essentially XML fragments) need to follow XHTML standards. SO therefore if you don’t know what portlets are going to appear on a page in advance how can you assemble all the CSS stylesheet information in the head of the page as the XHTML standards demand.

The solution...

People who I’ve spoken to about this have always had so few portlets in their portals that they can create a <LINK> list in the portal page head of all potential portlet possibilities but this isn’t a satisfactory solution for me.

My solution so far has been to break HTML standards (shock, horror) and include <LINK> and <STYLE> elements inside the body of the page.

I think I’ve found a better solution and that is to load the CSS stylesheets with JavaScript! This will produce standards compliant portal pages since <SCRIPT> elements can appear anywhere on the page. This seems to be a trick that is widely known in DHTML circles but I’ve only just discovered it [Note how IE uses a proprietary method createStyleSheet()]:

<script type="text/javascript">

//<![CDATA[

if(document.createStyleSheet) {

document.createStyleSheet('http://server/stylesheet.css');

}

else {

var styles = "@import url(' http://server/stylesheet.css ');";

var newSS=document.createElement('link');

newSS.rel='stylesheet';

newSS.href='data:text/css,'+escape(styles);

document.getElementsByTagName("head")[0].appendChild(newSS);

}

//]]>

</script>

Now all you have to worry about is clashing JavaScript function, variable and CSS class names! I would hope the Portlet tag library <portlet:namespace> element would be helpful here for generating unique class and function identifiers but being careful not to loose the performance benefits of static CSS and JavaScript files (i.e. dynamically JS/CSS generated files aren't usually cached).

8 comments:

Mark McLaren said...

Thanks! I found this code sample very helpful.
Note: Comment imported. Original by Anonymous at 2006-07-26 22:38

Mark McLaren said...

Legend! This was extremely helpful for me :)

Thanks!
Note: Comment imported. Original by Anonymous website: http://www.randomnity.com at 2006-08-01 12:00

Mark McLaren said...

Worked brilliantly, makes my page much more compliant (or on the way there at least).



Thanks
Note: Comment imported. Original by Anonymous at 2006-12-06 10:56

Mark McLaren said...

Hooray for stuff you can do by breaking standards! Let's break more standards, everybody!
Note: Comment imported. Original by Anonymous at 2007-01-28 00:35

Mark McLaren said...

Unfortunately, it works only in Internet Explorer, but not W3C standard compliant browsers. In other words, the "else" part doesn't work.
Note: Comment imported. Original by John Mikich at 2007-09-14 15:02

Mark McLaren said...

Hi John,



I'm sure it does work in Firefox (the main browser that I use). Unless there is a typo.


Note: Comment imported. Original by markmc website: http://content.mark-mclaren.info/ at 2007-09-14 17:30

Mark McLaren said...

Hi John,



I just checked again and it definitely does work. I tried it in IE7, Firefox 2, Opera 9, Safari 3 (for windows) and the technique works in all of them. Obviously the JavaScript needs to execute when the page definitely has a head, either by placing the script after the head element or invoking it via window.onload.





Dynamic CSS loading test





People are using this technique elsewhere such as in Apache Tapestry.





There is further discussion about this here.




Note: Comment imported. Original by markmc website: http://content.mark-mclaren.info/ at 2007-09-14 18:15

Mark McLaren said...

Thank god for Mozilla browsers! With the help of their Dom Inspector I found out that, in your code, the stylesheet does load but with the wrong href attribute, it should be just plain "escape(styles)". Also, the tapestry blog provides the correct code.

Anyway, thank you for pointing me to the right direction.
Note: Comment imported. Original by John Mikich at 2007-09-18 18:31