Monday, July 25, 2005

Check All/Uncheck All JavaScript buttons: Take 2

The trouble with JavaScript widgets is that they aren't self-aware, and they're typically designed by someone who has minimal assumptions about the page they're on (or the browsers they're compatible with). DHTML magnifiers assume the content they're meant to magnify is safely at the top of the page, and break when it isn't. Check All/Uncheck All button scripts don't account for the possibility of subranges. And so on and so forth.

One of our upcoming pages on the site redo is a form with several checkboxes all within the same name group, but subdivided into academic disciplines. The CGI is still expecting the same form data it did before these C/U buttons were added.

The basic script for checking all boxes goes as follows: an onclick handler passes the entire Input collection to a script, which loops over it and sets the input.checked property to true or false, based on a flag which is flipped at the end of the routine, right before toggling the button text. The trouble here is subranges.

Fortunately, the subdivisions are separate tables which can be given IDs: the onclick handler sends instead the ID, and walking the DOM tree, the script collects just the inputs which are children of that ID.
function check(ctainer) {
var field = document.getElementById(ctainer).getElementsByTagName("input");
for (i = 0; i < field.length; i++) {
if (field[i].type.toLowerCase()=="checkbox") {field[i].checked = !checkflag;} 
}
checkflag = !checkflag;
return (checkflag) ? "Uncheck All":"Check All";
}

Here comes the complexity. The Check All/Uncheck All buttons are useless and confusing to scriptless UAs. Being associated with black magic, they should be inserted dynamically using black magic. In The Good Old Days we would merrily employ document.write or innerHTML to insert the content.

Well, when you serve pages as XHTML, those methods disappear. Poof. And the reason is simple. XHTML is supposed to be valid XML. If a script can insert any text string into a page, it can break the XML. So, instead we create the C/U button by constructing an input element, feeding it the necessary attributes, and finding an appropriate node to insert it into. 'tdid' is the ID for the table, and 'garth' is the ID of the TH element whose contents the button will be appended to.
function makeButt(tdid,garth) { 
var l=document.createElement('input');
l.setAttribute('type','button');
l.setAttribute('value','Check All');
l.onclick = function() { this.value=check(garth); } 
document.getElementById(tdid).appendChild(l);
}

The theory is that all elements will be manipulated through setAttribute and removeAttribute, and tag specific methods will be deprecated. The reality is that IE 5/6 has a somewhat incomplete model for which attributes this works. People discovered that in IE, setAttribute didn't like events. So, we're attaching the check function directly to the object's onclick method.

If you were paying attention above, you'll notice the check function could have used setAttribute to check the boxes and removeAttribute to uncheck them, instead of a tag specific method. The reason it doesn't is because IE can't remove the checked attribute. It just can't. It can, however, set the attribute value to "" which unchecks the box... except in every other browser, which follow W3C spec on arguments and formerly collapsed attributes. If any value, even a blank one, is attributed to a collapsed attribute in HTML code, it's considered to be the same as the collapsed attribute in HTML 3. FF and Opera treat setting the value="" as checking the box.

The only way to do it in JavaScript is to pack both methods into an if/then subroutine, with the IE method first and the DOM method second. Forked code sucks.

Oh, and why check the input elements for whether they're checkboxes? The C/U buttons are themselves input elements and should be excluded. Currently, no browser complains when you manipulate the checked property of an input element which isn't supposed to have one (even in XHTML mode), but it isn't inconceivable this too will someday break.

Tuesday, July 12, 2005

Potentially overlooked CSS tips

A friend of mine who does vids just bought her first set of CSS primers and plans retooling her website. In honor of her recent birthday I'll pass along a wordy but useful set of tips. If she values the observations of three years dancing in landmines, she'll pay attention.

Prologue: in 2002, I started an unrequested skunkworks project of redesigning a tables-based, JavaScript-rollover-infested, accessibility hobbled website from HTML4 to XHTML/CSS. The next year a formal call for a makeover was requested and my R&D was implemented; the results of that redesign are the Cline Library website. Links below to the current 2005 revision:
These are mentioned not to show off, but because the tips refer to them. I did not design this layout, merely made it CSS based and SSI constructed. And now, the tips.

CSS Padawan 101:
  • It's natural for a beginning CSS developer to wrap everything in classed DIVs/SPANs, and to overdo it. Over time, reexamine your code and look for places where specificity would do the job better with less code. Where my pages seemingly diverge from this advice, it's because of complex inheritance issues: list elements on the newblue homepage behave in sometimes radically different ways depending on their section and purpose. The choices are to stick class attributes on all of them (revisiting the HTML4 formatting tag soup and lengthening the page code) or to wrap them in DIVs with IDs which guarantee separate behavior.
  • DTDs matter when it comes to how browsers treat your CSS. Even if you're a diehard HTML 4.0 enthusiast, leaving out any DTD ensures browsers play in 'quirks mode,' a genteel euphemism for 'browser regresses to flaws of previous major version.' I favor XHTML 1.0/Transitional as a good place to work from.
  • DTDs affect more than just CSS. Older DHTML JavaScripts manipulate object size/position values with unit-less integer values (e.g. 'property.style.top=300' instead of 'property.style.top=300px'). Just as you can't do this with CSS values in the presence of a DTD, IE is appropriately fierce about throwing errors in JavaScript when an XHTML/Transitional DTD is present and units are not, and older scripts may have to be rewritten. Better solutions are to find more current, DOM-friendly versions/alternatives or dump JavaScript altogether.
  • An XML prolog will bork the DTD in IE 6. Get rid of it. If your HTML editor automatically inserts an XML prolog at the top of the file, IE 6 will ignore the DTD below it and throw your browser into quirks mode. This didn't matter with the first layout when IE 5 was king, but it matters plenty with newblue: this 3-column liquid layout is a simple bit of CSS, but in quirks mode IE 6 reverts to pretending it's IE 5 which never understood that simple bit of CSS. Until all mainstream browsers expect websites to be served as XML with an XML MIMEtype, that XML prolog is useless and delete it. Even the W3C validator doesn't balk at its absence.
Browsers:
  • "It works in IE" is a terrible fucking excuse for anything. I test against several browsers knowing full well their combined marketshare is less than 15% of our users(although it's a growing share), because coding to standards is a smaller headache longterm than coding to browsers. From experience I speak, padawan. In order for IE 7 to get through this they will have to rewrite a lot more of their renderer than I think they have the will, brains or balls to do.
  • Opera isn't the yardstick it used to be on web standards. When they entered the business they made themselves felt as a company more concerned with standards than IE emulation: their insistence that the correct alternative to BODY MARGIN="0" is body{padding:0px;} rather than the {margin:0px;} all other browsers use is still the case with Op8. OTOH Firefox is killing their marketshare outside embedded and certain niche markets, and their product which once had IE UA spoofing as an option now defaults to it, and more critically, slavishly imitates IE's default HTML rendering and proprietary JavaScript properties. They took being shut out of MSN hard. Don't get me wrong, it's still a damn fine browser, with probably the best accessibility integration for Windows (and free integration at that), but at this point the best their fans can say is "Look at the beautiful pluma--er, mouse gestures!" It's still important to test against, since they still claim to use the same render engine in all their products (including embedded).
  • Mozilla/FF/NS isn't God either. Form elements like buttons and checkboxes should be resizable, colorable, etc. with CSS, and IE gets it. The Gecko engine does not. But as of this writing it's still the first place to test modern technologies like CSS2.
  • No matter what Apple tells you, Safari and KHTML (Konqueror) are not interchangeable terms. Safari relies more and more heavily on WebKit technology which is not open source, and Dave Hyatt is unapologetic about it. Major improvements in either product's renderer should appear in the other eventually, but only if they're in the KHTML portion. Speaking as someone who uses Konqueror, it's an excellent file browser. As a Web browser, it's an excellent argument for bundling Firefox in Linux distros. And no matter what the Safari user-agent string says, KHTML is NOT "Gecko-like." The Apple tech who put that into Safari is part of the witless protection program now.
  • Current screen readers do not respect 'media=' attributes. This means that the content you meant to hide from visual browsers using display:none; in a stylesheet aimed at media="screen" won't get spoken in aural browsers either, even if you have a separate aural stylesheet overriding the selector. Those of us who deal with the vendors of these "solutions" are livid that the one sole attribute in HTML meant for their consumption and processing is ignored, and we fervently pray Freedom Scientific finds itself sued by the Australian/British courts for flouting it. It's not much of a secret that FS is run by Visual Basic hackers cocksure that Microsoft will buy them out and integrate it into Windows. Again, visibility:hidden means "render a blank space the same size as the content." Display:none according to W3C specs actually means "remove content from HTML stream" (although not the DOM).
  • Lynx is not a bad choice for crude accessibility testing, and it's still a decent choice for mobile device testing.

  • Just as you're being told to avoid using fixed (e.g. px) units for fonts, consider eliminating them for paddings and margins on a case by case basis. Page margins are a good place for fixed units; paddings between LI elements are not, no matter how many years of Microsoft word stylesheet have told us (then again, we never had scalable units as an option). Ems make good neighbors.
  • This is old hat by now, but Navbars should be the bottom of your HTML document. People with screenreaders detest hearing them repeated at the beginning of every page, and skip links are a holdover from purely linear HTML rendering. Moreover, people reading your site on mobile devices will thank you for putting content first. All the above examples on our site use absolute positioning to move the navbar where the sight-unimpaired expect it (top and/or left). BION, there was a time when this technique was considered controversial.
  • CSS hacks suck for several reasons. First, they presuppose that the parsing errors they exploit will not be patched inside a major release. Secondly, they screw with validators. Thirdly, they screw with integrated web development packages which treat CSS files as valid data structures. Most serious IE hacks are aimed at IE 5.x problems, and local/national stats on 5.x usage are somewhere around that of Netscape 4 users. Pages should degrade gracefully, but if they can't because one end-of-life browser has a seriously broken model, cut your losses.
  • IE and Opera arbitrarily default to sizing the text inside text-based form controls at 85% the container font-size whereas Gecko and others use 100%. This means that if those form elements have padding defined in scalable units (ems/exes) that spacing is calculated on the size of the text in those elements, not the parent container's. Without devoting three paragraphs to explain why, it's a good idea for accessibility and consistency to formally declare INPUT,SELECT {font-size:100%;} if you plan to design CSS form layouts which gracefully scale for visual disabilities. Trust me on this one.
  • Join css-discuss. It's as much for n00bs as pros, and you'll get exposed to some cutting-edge stuff. Before you know it, you'll be helping people, too.