Medryx Observations

August 15, 2008

Transparent PNG’s on IE with dojo

Filed under: dojo, javascript — Tags: , , , — maulin @ 5:55 am

Transparent PNG’s are fabulous. They are the key to making your app look really professional and slick. And they allow you to make easily themed web pages without having to redesign all your images, since you can just rely on the background colors shining through the transparency. Transparent PNG support is available on Firefox and Safari, and IE7. But IE6 does not support it.

There are, however, workarounds. And when it comes to making all browsers behave the same in the background, equalizing browser differences so you can just focus on writing great web applications — dojo is the best tool around. So it seems only natural to me that we should try to create a solution to this problem with dojo.

Fortunately, there has already been great work done by Angus Turnbull (which by the way is a GREAT name!) He has created a nifty solution to the problem. It turns out that IE6 does support transparent PNG’s when a special “filter” is used to load the image. Well angus has created a script that uses IE6’s “behaviors” via *.htc javascript files to implement a simple solution that will replace all your images with this filter-loaded version. This includes css background images and IMG tag images. And in his latest version he handles background-repeat and background-position, so this really becomes a simple drop-in solution, and your transparent PNG’s suddenly work perfectly on all the major browsers.

There is just a *little* configration needed to get Angus’s IEPNGFix to work. First you drop the iepngfix.htc, iepngfix_tilebg.js, and blank.gif files somewhere on your server. You then add the following to your CSS file:

img, div { behavior: url(/path/to/iepngfix.htc) }

You also need to open and change the path to blank.gif in iepngfix.htc to the absolute path to the blank.gif file. (Or a relative path, but relative to *any* page that will actually use the file, which is inconvenient). Finally you use a typical script tag to include iepngfix_tilebg.js in your page if you want background-repeat and background-position to work.

Its really not too bad, but its still more work that I want to do. And I would love to be able to use it any dojo web app without all this configuration. Well, dojo to the rescue! With a few modifications, you can make this available with a simple:

dojo.require("medryx.util.png");

(Or you can use whatever namespace you want!) Ideally, this could some day be included in dojo natively, so you wouldn’t have to do any work to get this support. In this technique, you don’t worry about absolute or relative paths — because you replace all that stuff with calls to dojo.moduleUrl
and you can eliminate the change to your CSS with a nifty CSS trick that Angus provides. Finally, you can eliminate the script tag to load iepngfix_tilebg.js by just adding a dojo.provide() call at the top of that file and using a dojo.require() to get it.

Sounds like a lot of work, but really it wasn’t, and now I can use it in any of my web applications without any real work. So here is the step-by-step process:

  1. download the IEPNGFix.zip file
  2. create the following directory structure and copy the files from the zip as follows — lets assume you will put this in a “medryx” module, though obviously yours would be named differently:
    dojotoolkit/
    -- dojo/
    --dojox/
    --dijit/
    --medryx/
    ----util/
    ------png.js --> see below
    ------png/
    --------iepngfix.htc
    --------iepngfix.php --> optional, see below
    --------iepngfix_tilebg.js
    --------blank.gif
  3. Create a file called “png.js” whose contents are quite simple. It simply checks your browser, and if needed, adds a rule to your first stylesheet to handle png’s with the behavior fix (this is a slight modification right from Angus’s demo page). The content of the file is as follows:

    dojo.provide("medryx.util.png");
     
    (function() {
    //a fix for transparent png in ie6 (only works with php, or change this to the iepngfix.htc file if you can get the server to set content-type correctly!
     
    if (dojo.isIE && dojo.isIE < 7 &&  document.styleSheets && document.styleSheets[0] && document.styleSheets[0].addRule){
    	//dojo.require("medryx.util.png.iepngfix_tilebg"); needed to support repeat, but doesn't work for me.
    	var fixUrl = dojo.moduleUrl("medryx.util.png", "iepngfix.php"); //use *.php if your server doesn't server the *.htc file correctly
    	document.styleSheets[0].addRule('*', 'behavior: url("' + fixUrl.path + '")');
    }
     
    })();

    One small thing to notice — the iepngfix.php file is really only needed to set the content-type header of the *.htc file correctly so IE6 will know what to do with it. Obviously there are other (better) ways of doing this on each type of server. But the php file is a simple workaround if you don’t have access to changing how *.htc content-type is set on your server.

  4. Open the iepngfix.htc file and search for “blank.gif”. Replace that line with:

    IEPNGFix.blankImg = dojo.moduleUrl("medryx.util.png", 'blank.gif').path;

    You might also want to search for “alert” and change it to console.warn instead, but that’s not mandatory!

  5. Open the iepngfix_tilebg.js file and add the following to the top line:

    dojo.provide("medryx.util.png.iepngfix_tilebg");
  6. Now on any app that you want to use transparent PNG, just add to your set of dojo.require statements:

    dojo.require("medryx.util.png");

A couple more side notes — I can’t seem to get background-repeat or background-position to work right with the *.js file. I haven’t had time to really troubleshoot it, since I don’t need it right now. If anyone has the solution, please let me know! A second note is that you should consider NOT using 1px images that are repeated, but instead use a more reasonable size, to limit the number of repeats. So if you know the smallest size of your display is 100px wide, then make your PNG 100px wide, and then repeat. Each repeat takes browser processing, and these days the download size of a 1px image and 100px image is not significant enough to make much of a difference.

Hope this helps someone! It has certainly helped me. Thanks Angus!

July 29, 2008

A Dynamic Tab Container

Filed under: dojo — Tags: , , — maulin @ 4:37 pm

I think tab containers are great. And I think dojo’s tab container implementation is better than great. Its so easy with simple markup to make tabs and really create elegant user interfaces. Further, you lazy load the content of the tabs to make for a reall good user experience, all without much effort at all.

But I frequently use tabs as a tool to filter data views. For example, if I have a table, and want a few standard “views” of that table, I’ll put each view in a tab. Well this leads to a lot of repetitive code. What I really want is to tell the tab container what tabs I want to build, and then create a few small bits of data that can be plugged into each view to “customize” it.

So the “old way” of doing this looked like the following. I’ll use the “stateStore” from many of dijit’s tests as my sample data.

<div>
<div title="All">
<h2>All</h2>
<table border="0">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
</tr>
</thead>
</table>
</div>
<div title="Countries">
<h2>Countries</h2>
<table border="0">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
</tr>
</thead>
</table>
</div>
<div title="Cities">
<h2>Cities</h2>
<table border="0">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
</tr>
</thead>
</table>
</div>
</div>

That’s a lot of code to have three tabs each with a slightly different filtered view of the same table. Enter DataTabContainer.

<div>
<h2>${name}</h2>
<table border="0">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
</tr>
</thead>
</table>
</div>

Ahhh.. Much better. Well, once I had this working for a simple array of tabs, I thought — “how about I swap in a store for the tabs?”. So instead of (or in addition to) the “data” attribute of the widget, you can provide a “store”, “query”, “queryOptions”, and “sort” attribute, much like DataGrid. If you have both a store and data, the tabs in “data” are pre-pended to the store objects. This allows you to include an “All” tab quite easily at the front of the container.

This was actually quite a bit trickier to build than I initially thought. I ended up using a lot of code from dojox.widgets.Iterator, munged with the store/query code from DataGrid to put it together. It basically works like this:

  1. Convert the child elements of the DataTabContainer to a custom widget via dijit.Declaration (DataTabContainer, it may surprise you, actually extends dijit.Declaration, and not TabContainer).
  2. In the buildRendering stage, copy down to the location of the node (since dijit.Declaration is a “pseudo” widget that destroys its own domNode after parsing it).
  3. In place of the destroyed node, create a TabContainer.
  4. Iterate over the data array (or fetch the store items) and create tab for each item in the array. (Using the “titleAttribute” attribute of DataTabContainer as the title of each tab).
  5. In startup(), create an instance of the dijit.Declaration class for the currently selected tab. Before doing so, do a string substitution on the dijit.Declaration class’s prototype.templateString. Note that this is not a simple dojo.string.substitute because it uses the store’s getValue method to get the value to place in the substitution.

And that is it. It works pretty well. Other features I could imagine:

  • If the DataTabContainer is the child of an already existing TabContainer, then use an attribute like “embed=’true’” to skip the creation of the TabContainer step in buildRendering, and instead, just add the tabs to the surrounding container.
  • Allow a “pre-data” and “post-data” to add items around the store fetch items. Right now it only supports “pre” data.

Let me know what you think!


Update:

Well you can forget almost everything I mention about the implementation of this above. I have figured out a much simpler implementation that creates a mixin class to TabContainer, AccordionContainer, or StackContainer and uses the content of the container as a “templateString” that is sent to ContentPane.setContent. Much easier. And now you can do dynamic Accordions and Stacks. Nice! So the updated source is here. And you can try it here. Just remember, to use this code you need to dojo.require the original container (TabContainer, AccordionContainer, or StackContainer), the original pane (ContentPane), and the mixed in stuff (medryx.widgets.DynamicStackContainer).

Powered by WordPress