Skip navigation

Monthly Archives: June 2008

Well, I spent quite a bit of time trying to figure out what was going on with IE this time…I use Dojo 1.1 for the application I’m building at work, and with their theming system for Dijit, they have a tundra.commented.css file, which includes each individual widget’s corresponding CSS file via an @import statement.

Well, I thought, “Hey, this is pretty cool and easy to manage, so I’ll do it this way for my widgets as well for development purposes (we compile our CSS into separate, compressed files, afterward)…Later on that day, QA came to me and said “Hey, this thing here isn’t showing up!” and “I can’t see this dialog anymore, do you know where it went?!” Sure enough, I went over to look at my QA colleagues’ screen, and things looked all mangled and wrong…But only in IE! What the heck could be going on?!

Come to find out, after using the awesome IE Development Toolbar (yes, I’m being sarcastic), the styles weren’t even loaded into the browser!! Well, WTF?! So I cleared my cache, deleted all cookies, etc., just to make damn sure nothing funky there was happening…(refresh)…Same thing, again.

After a while of fussing with style sheets, checking other browsers, getting the generic -218760-whatever error error code, etc. I finally decided to include them all via <link> tags. Now a new error, only, a run-time error this time! “Invalid Argument.” and then..BAM! a second error! “There is not enough free memory to perform this operation.” Again…WTF?!

So, at this point, I was desperate and decided to try including them via JavaScript. What’s that syntax again? document.createStylesheet? Hmmm…Better go look it up…”Whoa, what in the name is this?! You can only create 31 stylesheets with document.createStylesheet, but yet can add as many as you want if you do a document.createElement(“STYLE”) and append it to the DOM?! Hmmmmm….”

So now, I was curious, and imported only 31 stylesheets for widgets that I knew would show right away…Sure enough, everything I specified, show up perfectly! Then, to take it a step further, I decided to split out the master CSS file with all of the @imports, into 2 files, and then import those…Amazingly, it worked!

So there you have it, the answer to fixing the extremely ridiculous, and hardly documented, IE CSS @import issue.

Lessons learned

  • IE seems to support only 31 @import statements per CSS-file, <link></link> tags on a given page, or creations of stylesheets via document.createStylesheet
  • You may noticed styles start getting ‘lost’ or ‘messed-up’, or you may get the errors “Invalid argument.” or “There is not enough free memory to perform this operation.” when this limit is reached.
  • To get around this limitation…
    • Split your @imports into 2 or more files, and then load those files.
    • Use document.createElement(“style”) statements and append those items to the HEAD element
  • IE most likely uses the same internal methods for @imports and/or <link></link> tags, that document.createStylesheet uses, due to this 31-limit.

Resources

createStyleSheet Method – http://msdn.microsoft.com/en-us/library/ms531194(VS.85).aspx

For a long time I have been trying to find the right JavaScript framework for serious AJAX development.  At this point there are a few choices.  However, they all have a long way to go.  It seems that Dojo is the most mature one around and it has a pretty good user base.  However the documentation and examples still are lacking.

Recently I picked up a small side project.  I didn’t want to spend a whole lot of time on it so using a JavaScript framework would really speed things up.  I chose Dojo and development has gone fairly smoothly.  The only issue is there are some real mysteries about the framework that I had a hard time solving.

The first issue is the ComboBox and FilteredSelect.  I had the hardest time finding an easy way to simply add items to them.  Dojo uses a data model that is similar to ASP.NET data binding.  The difference is in ASP.NET you can add individual items to all the controls instead of binding if you want to.  With Dojo you cannot (from what I can tell).  So, the easiest way to add items to a ComboBox or FilteredSelect is this way:

var options = new dojo.data.ItemFileWriteStore({data: {identifier: ‘value’, items:[]}});
for (var i = 0; i < collection.length; i++) {
options.newItem({name: collection[i].Name, value: collection[i].ID});
}
dropDownNode.store = options;

Take note of the identifier portion of the ItemFileWriteStore constructor.  It tells the drop down the property name of the item that corresponds to the value of the drop down item.  I had a really hard time finding this information, and when I did find it, it wasn’t in the official documentation.  This is a very simple concept that any UI developer needs to know when building UI.

The next issue I faced, and actually I had faced in the past, was using the Dojo grid.  I had no luck with the grid in the past and decided I wouldn’t waste my time trying again.  The reason it is so difficult is getting it to bind correctly with JSON is not very easy.  The JSON has to be formatted just right or the grid will not work, and I’ve never been able to get it formatted right apparently because the grid has never worked for me.

So I decided to build my own simple grid.  I didn’t need anything complicated, it just needs to display in a table format and allow the user to select a row (for editing, deleting etc.)  Below is the solution I came up with.

Here’s the template:

<table class=”dojoxGrid-row-table list” dojoAttachPoint=”containerNode”>
<thead dojoAttachPoint=”headerNode” class=”dojoxGrid-header”>

</thead>
<tbody dojoAttachPoint=”bodyNode”>
<tr>
<td><img src=”images/5-1.gif” /></td>
</tr>
</tbody>
</table>

And the widget:

dojo.provide(“hockey.widgets.list”);

dojo.declare(“hockey.widgets.list”,
[dijit._Widget, dijit._Templated],
{
templatePath: dojo.moduleUrl(“hockey”,”widgets/layout/list.htm”),
templateString: “”,
list: null,
columns: null,
selectedItem: null,
emptyListText: “”,

selectedId: function() {
if (this.selectedItem != null) {
var returnValue = this.list[this.selectedItem.itemRowIndex][“ID”];

if (!isNaN(returnValue))
return parseInt(returnValue);
}

return 0;
},

selectedIndex: function() {
if (this.selectedItem != null) {
return this.selectedItem.itemRowIndex;
}

return 0;
},

removeRow: function(index) {
if (this.bodyNode.childNodes.length > index) {
this.bodyNode.removeChild(this.bodyNode.childNodes[index]);
}
},

postCreate: function() {
this.inherited(“postCreate”, arguments);
},

clear: function() {
if (this.headerNode.hasChildNodes()) {
while (this.headerNode.childNodes.length > 0) {
this.headerNode.removeChild(this.headerNode.firstChild);
}
}

if (this.bodyNode.hasChildNodes()) {
while (this.bodyNode.childNodes.length > 0) {
this.bodyNode.removeChild(this.bodyNode.firstChild);
}
}
},

showLoading: function() {
this.clear();

var row = document.createElement(“row”);
var cell = document.createElement(“cell”);
var loading = document.createElement(“img”);

loading.src = “images/5-1.gif”;

cell.appendChild(loading);
row.appendChild(cell);

this.bodyNode.appendChild(row);
},

bind: function() {
var instance = this;

this.clear();

// generate columns
if (this.columns != null && this.columns.length > 0) {
for (var columnIndex = 0; columnIndex < this.columns.length; columnIndex++) {
var header = document.createElement(“th”);
header.innerHTML = this.columns[columnIndex].headerText;
header.className = “dojoxGrid-cell”;

this.headerNode.appendChild(header);
}
}

// generate rows
if (this.list != null && this.list.length > 0) {
for (var itemIndex = 0; itemIndex < this.list.length; itemIndex++) {
var row = document.createElement(“tr”);
row.className = “dojoxGrid-row”;
row.itemRowIndex = itemIndex;

// loop through each column
for (var columnIndex = 0; columnIndex < this.columns.length; columnIndex++) {
var cell = document.createElement(“td”);
var cellValue = “”;

if (this.columns[columnIndex].formatString)
cellValue = this.list[itemIndex][this.columns[columnIndex].dataField].format(this.columns[columnIndex].formatString);
else
cellValue = this.list[itemIndex][this.columns[columnIndex].dataField];

if (!cellValue)
cellValue = ” “;

cell.innerHTML = cellValue;

if (this.columns[columnIndex].bindCallback) {
this.columns[columnIndex].bindCallback(this.list[itemIndex], cell);
}

row.appendChild(cell);
row.onclick = function() {
instance.selectItem(this);
}
}

this.bodyNode.appendChild(row);
}
} else {
var row = document.createElement(“tr”);
var cell = document.createElement(“td”);

cell.innerHTML = this.emptyListText ? this.emptyListText : “Nothing to show”;

if (this.columns)
cell.setAttribute(“colspan”, this.columns.length);

row.appendChild(cell);
this.bodyNode.appendChild(row);
}

this.containerNode.style.display = “none”;
setTimeout(function() { instance.containerNode.style.display = “table”; }, 50);
},

selectItem: function(item) {
// unselect the last item
if (this.selectedItem != null) {
dojo.removeClass(this.selectedItem, “dojoxGrid-row-selected”);
}

// select the next item
if (item != null) {
dojo.addClass(item, “dojoxGrid-row-selected”);
this.selectedItem = item;
}

console.log(this.selectedItem);
}
});

It is a very simple grid, but it does exactly what I need it to.

The last issue I faced was uploading a file.  I wanted to be able to upload an image file using Dojo without having to use something like Adobe AIR (although it is really cool, I didn’t have time to learn it for this project).  After much searching around I found a few examples.  Most of the examples I found were for older versions of Dojo and did not work.  I finally pieced together exactly what I needed to do.  All that needs to be done is the upload input has to be wrapped in a form.  The form should be built just like it would if an actual postback were occurring.  Then, to upload the file use the following code:

// gather all parameters from a form
dojo.io.iframe.send({
form: “imageForm”,
hanldeAs: “text”,
load: function(data) {
// handle callback here
},
error: function(data) {
// handle errors here
}
});

The form property should be the id of the form you want to post.  Make sure the form’s enctype is multipart/form-data and the method is post.  On the page that the form is being posted to make sure to return something.  And for whatever reason, that something has to be wrapped in a textarea tag or Dojo will assume an error has occurred.

There you have it, things that I have learned about Dojo that do not seem to be common knowledge.