Wednesday, May 11, 2005

Lessons Learned #5: Cross-browser Javascript and CSS

Unlike previous "Lessons Learned" articles this one will describe my current project and list some of the little annoyances that I have had to solve. I mentioned in my last post that this project was to build a web based administration system for Open Harmonise that mirrored the Java Swing version as closely as possible. Obviously the Java Swing version was pretty dynamic in nature so this project was going to require a lot of Javascript and funky CSS which would have to work in both of our target browsers (Internet Explorer and Mozilla/Firefox).

I have to admit, that when starting out I really did not think the project would be successful. The Java Swing version of the client includes many custom components to allow the management of complex information. The layout includes Outlook style tabs, a highly customised table view and a tabbed form that is built on the fly from data definitions. These form elements are not simple either, for example a text field is made up of the following components;

  • text of the field name.
  • button to open a help text window.
  • valid/invalid icon (updated in real time, also impacting a similar icon on the tab).
  • the text entry field itself.
  • a counter which counts down how many characters you have left based on the data definition. This counter turns amber when you get close to the maximum length and red when you go over it.
That is just one of the simple types of form field, we also support number (with or without floating point), date (with or without time) and boolean. Then there are the complex types including vocabulary lookups, relationships to other resources in the CMS and finally compound properties (nested fields like address). Each field in the form can have a min and max number of occurrences and therefore needs to have plus and minus buttons for adding and removing extra fields.

I have ranted on a bit, but you can see why I was a little worried about building this and supporting it in html's request response model. But this was my task so I forged on. Overall it has been nice to discover that building these types of web application is a lot easier now than in days gone by. The latest versions of IE and Firefox interoperate quite well, but there have been many points where I have had to stop and scratch my head for a while. The following are my experiences and solutions, if for any of these you know that I am just being stupid and that there are much easier ways of doing these then please leave a comment.

1) Dynamically adding dynamic content.

Problem;

There are several approaches to adding content to a html page, the easiest of these is to append to the "innerHTML" attribute of an element. In Internet Explorer this doesn't work for forms as any information people have already input to form elements will be lost. Instead you must construct your new elements as html DOM objects and use the "appendChild" method. While this works fine for simple things life becomes complex in Internet Explorer with some special (for want of another word) attributes. These attributes include;

  • name on form elements
  • value on form elements
  • class
  • name on iframes
  • all Javascript event handlers such as onchange and onclick
The problem with these is that Internet Explorer treats them as plain DOM attributes and does not do the special processing that each requires. The worst offender for these is the Javascript event handlers, they are not parsed by the Javascript engine so they are not actually set as listeners to the events.

Solution;

For most of these it is a simple mater of using the relevant Javascript attribute for the object, such as doing;

element.name='NAME' ;

instead of;

element.setAttribute('name', 'NAME');

However things get a little more complex where the Javascript event handlers are concerned. For these you must again set the relevant Javascript attribute for the object, but you must set it to the method pointer rather than a string. E.g.

inputElement.onchange = elementChangeMethod;

instead of;

inputElement.setAttribute('onchange', 'elementChangeMethod(PARAM1, PARAM2)');

One thing that should be obvious from this is that when having to use the method pointer you cannot provide any parameters to the method. When mocking the interface up in static HTML I was mostly passing "this" into these methods, which is still accessible, but not as a parameter, from a method called using the method pointer. So the rule is to remember that if you are going have to insert a new version of some html that has Javascript event attributes on it, ensure that those methods only deal with the "this" context object and use no other parameters.

2) & in links and window.location

Problem;

Because this is a very dynamic html web application there are many points where I use Javascript to either load a page into a frame/main window or insert an IFRAME in the page. Something that foxed me for quite a while was the fact that "window.location.replace" was working in IE but not in Firefox (and that Firefox was not showing any errors), I would just get a blank page.

Solution;

The solution was embarrassingly simple, but not obvious at all. IE allows you to put "& amp" into the URL for "window.location.replace" and Firefox does not. Mostly this is hard to spot as Firefox doesn't complain, it just shows a blank page.

3) Submitting a form and using window.location

Problem;

I replicated the Java Swing toolbar as a series of buttons in a frame across the top of the page. These submit various forms in other frames to accomplish their tasks. These forms may be visible to the user, for example the "Save Resource" button submits the main visible data entry form. Some are not, the "New Resource" button submits a hidden form. To keep life simple, these forms submit to a page that performs the action and then I redirect the frame back to the previous page which is now in a new state because of the action. However the changes were not happening.

Solution;

After quite a while of fiddling it appeared that the "window.location.replace" was happening before the "form.submit" commands in the Javascript, even though they were in the other order in the source. The evil hacky solution was to put the "window.location.replace" into a delay of a second to allow the "form.submit" to happen, truly horrible but it does work.

4) Comparing the value of a html DOM attribute to a string

Problem;

In many places I am allowing the user to move values from a list into a "select" field. The field has the "multiple" attribute set to "true" so that it appears as a list instead of a drop down, but I want everything in it to be submitted. So I wrote a small Javascript function to find all the elements with a tag name of "SELECT" and a "multiple" attribute with a value of "true". This is a very simple DOM navigating script, so imagine my surprise when it found the elements with the right name but would never match on the attribute value.

Solution;

The html DOM method "getAttribute('ATT_NAME')" returns a "DOMString" object not a "String" object, therefore you cannot do a simple comparison. In the end I did the following;

var attrubuteValue = element.getAttribute('ATT_NAME');
var sValue = '' + attrubuteValue;
if(sValue == 'STRING_TO_COMPARE') {
...
}

I am guessing that I could have compared against the DOMString's "toString()" method, but I was a bit fecked off by the time I thought of that and so haven't gone back in to fix it. But you get the idea. Again, no warnings or errors to be seen.

5) Dynamically inserted IFrame not findable by name

Problem;

Inserting an "IFRAME" element using Javascript works fine, until you come to try and find it by its' name in the "frames[]" array. The name doesn't appear to be available.

Solution;

Another bit of a hack, I found it by comparing the location instead.

Conclusion

So those were the most annoying issues, and the ones that I can remember while writing this. Although these did cause me major headaches, again I must say that the process of building this site has not been anywhere near as bad as I thought it would be. There is a resurgence in the art of building web "applications" instead of websites. Most of this has been the interest surrounding Google's efforts in this area and the fantastic results that can be gained from using asynchronous calls with XMLHttpRequest objects. Of course I would have loved to build this site in that way, but it was a bit too much of a risk given the time and budget allowed. Still I did create the layout structure to allow us to begin replacing various parts in this way over time.

Finally, please to do let me know if I am wrong in anything I have said here. I really do want to find the best way of fixing these issues, and I must thank the Mozilla/Firefox teams for supporting all the non-standard IE ways of doing things so that I could build truly cross-browser Javascript.

6 comments:

Anonymous said...


[url=http://www.homebasedbusinessprogram.com/profiles/blogs/modules-pour-hommes-et-dames][b]sac longchamp[/b][/url]
[url=http://shenenmaoyis.webnode.cn/][b]sac longchamp[/b][/url]
[url=http://shenenmaoyi.publr.com/][b]sac longchamp[/b][/url]
[url=http://shensacens.hpage.com/][b]sac longchamp[/b][/url]
[url=http://shenenmaoyis.sosblogs.com/The-first-blog-b1/Fossil-Winslet-II-Sac-besace-transformable-recemment-revele-b1-p1.htm][b]sac longchamp[/b][/url]

Anonymous said...


[url=http://proyectok.com/blogs/viewstory/56489][b]Cheap NHL Jerseys[/b][/url]
[url=http://wikimommy.com/index.php?title=Few-Secrets-To-Simplify-michael-kors-outlet][b]michael kors outlet[/b][/url]
[url=http://hrpedia.ru/index.php?title=A-Number-Of-Tips-To-Make-Ease-Of-michael-kors-outlet][b]michael kors outlet[/b][/url]
[url=http://abilipedia.com/w/index.php/index.php?title=Funny-But-Nonetheless-][b]Cheap NHL Jerseys[/b][/url]
[url=http://marysdiary.net/mw/index.php?title=A-Handful-Of-Guidelines-To-Simplify-michael-kors-outlet][b]michael kors outlet[/b][/url]

Unknown said...

mont blanc
tory burch outlet
michael kors handbags
louis vuitton handbags
nike roshe runs
michael kors outlet
louis vuitton outlet
christian louboutin sale
nike uk
true religion outlet
fitflops sale clearance
michael kors
coach outlet
hollister clothing store
adidas originals
nike basketball shoes
michael kors handbags
michael kors outlet clearance
coach outlet store online
michael kors bags
louis vuitton outlet
jordan 3 infrared
jordans
nike air max
true religion jeans
air jordan 13
louis vuitton outlet
michael kors outlet
louis vuitton outlet
ugg boots
coach outlet
true religion shorts
celine
kate spade
christian louboutin shoes
cheap oakleys
jordan 6
jordan shoes
louis vuitton outlet
oakley vault
20164.22wengdongdong

raybanoutlet001 said...

boston celtics jersey
michael kors outlet clearance
new england patriots jerseys
air max 90
moncler jackets
valentino shoes
ecco shoes
versace
patriots jerseys
michael kors handbags

chenmeinv0 said...

louboutin outlet
cheap rolex watches
canada goose outlet
burberry scarf
spurs jerseys
ugg boots outlet
michael kors outlet clearance
louis vuitton handbags
uggs sale
gucci outlet online
chenyingying20161130

chenmeinv0 said...

louboutin outlet
cheap rolex watches
canada goose outlet
burberry scarf
spurs jerseys
ugg boots outlet
michael kors outlet clearance
louis vuitton handbags
uggs sale
gucci outlet online
chenyingying20161130