Archive for the ‘web’ Category

Script-Assuming Styles

Sunday, August 26th, 2007

Recently, the topic of script-assuming styles came up again. With all the ajax and RIA flying around, its easy to forget that some folks fly with javascript disabled. This trap may lead to the developer creating styles that will assume script is enabled (since our heads are in script-land SO much) and wind up being useless to those without javascript. For example, a slideshow widget may have the style for the images set to hide them by default, then use javascript to display the images in-turn. Users that don’t have javascript will be looking at a page full of hidden images. No big deal to fix though, just make them visible by default and then have javascript hide them when the widget loads. The problem with this is that there will be a visible flicker when the page loads. The images will be visible for a split second, then they disappear.

The noscript tag could work here also (and without the flicker). Basically, have a noscript tag that would load a stylesheet specifically to ‘fix’ the widget to be usable when javascript is not enabled. This currently works, but there is some talk of the noscript tag not being included in XHTML5 and its a bit annoying to have that much distance between what is effectively the same logical group of styles just to cover the 2 modes. It would be especially annoying if this were a ‘3rd party’ widget or tool being dropped into a page.

I’ve talked about how it’d be nice if there was a :scriptEnabled pseudoclass available, but there (still) isn’t. The workaround is to have javascript code set a class on a root element that will just cascade and hide the images for you. Until today, I thought the body element was the best candidate for that: just add a ’scriptEnabled’ class to the body element (document.body) and then the styles will cascade. The trick was getting the script so that it would reliably be run AFTER the body was available. Without using a ‘dom ready’ approach, the best I’d could muster was to just have the script in line as the first child element of the body. It was a trade-off that I could live with.

Then I read this thread and I felt like a noob. In it, Simon Pieters describes the same approach, but used a much more appropriate element…and one that was much easier to get at without all the timing concerns. The root element itself: the html element (document.documentElement). The scriptEnabled class can be set on the document.documentElement and then the styles that want to assume that script is enabled would just be prefixed with .scriptEnabled. I had never thought about styling with the html element. I’ve used it (the html>blah css hack), but I’d never thought about using it for non-hackery. The nice part is, the code can be anywhere…the documentElement is (obviously) available when script is executing.

document.documentElement.className += " scriptEnabled";

On a related note: I now have a ‘noob’ category.

Selling Accessible Web Applications

Wednesday, August 22nd, 2007

Roger Johansson talks about one of the key elements in writing an accessible website: JavaScript interaction must be input device independent. He talks about some low-hanging fruit for how to take steps to follow the rule and allow non or limited mouse users the ability to use your site. I try to push accessible, unobtrusive javascript as much as possible on the projects I’m on, but the pushing gets more difficult when the project is an (increasingly more and more rich) web application. I never seem to have enough ammo to convince the customer of the need (even with the Target lawsuit).

Should I need to do the convincing though? Is it something the customer would just expect out of the rich web application they are having built? Maybe it should be a basic assumption, but cost comes into play. It is more expensive to code an app so that it’s accessible or so that it will degrade gracefully without javascript. Therefore it is something the customer should probably buy off on if it wasn’t part of the initial bid. So, I do make sure the topic comes up with the customer, but apparently my sales skills need some work. Roger gave a good list of users that don’t use a mouse (some of them not-so-obvious)…maybe that’ll help my next pitch ;)

* Mobility impaired people who cannot use a mouse at all
* People with motor impairments who can use a mouse but lack fine motor control
* Screen reader users who do not use a mouse, or even a monitor
* People using mobile phones
* Laptop users, since most laptops have really bad trackpads or other means of positioning the cursor (ever tried using a hierarchical dropdown menu with a trackpad while riding on a train?)
* Speed typers who have learned to use keyboard navigation efficiently and are slowed down when they have to switch to their mouse (if they have one)

Interesting Back Button Bugs

Thursday, May 10th, 2007

I didn’t mention these in the previous post (it was getting too long already), but there were some funky problems we ran into when dealing iframes for the back button. We were using an iframe so we expected that no matter what, if you leave a page and come back, the iframe will still contain the content it had when you left. This simple expectation failed in both ie and firefox for 2 different reasons.

IE:
It took us maybe a full day to figure the problem with IE and it STILL bothers me today because I cannot reliably reproduce the bug in isoloation. IE didn’t like doing ANY dhtml above the iframe that we were using. If ANY dhtml was done above the iframe, the iframe would be ‘reset’ to its original ’src’ when returning to the page via the back button. Sounds similar to the bug with safari that Brad Neuberg describes.

To get things rolling again in IE, we just moved the iframe to the very top of the page. That worked until just the other day. I decided to be fancy and use yahoo’s onDocumentReady implementation. Well I’m assuming they do some dhtml above the top of the body (maybe add script tags to the head?). Regardless, when I removed the yahoo files, the back button magically worked again.

Firefox:
We were seeing the EXACT same symptom: the iframe refused to remember its content. I couldn’t believe it…how did we break firefox’s new back button feature (where it stores the full state of the page including javascript and just restores it when you return…the uber back button fix). In the end, it wasn’t our code at all. We had firebug installed. All we did was disable firebug and the back button was as good as new. I can only suspect that firebug does some rigorous cache flushing or something.

Our Back Button Story

Wednesday, May 9th, 2007

Ah, the back button. That thing users want and don’t understand why it isn’t so simple anymore. Our project has a user story for the back button. It described a specific set of scenarios and what the users expectations were for when the back button was clicked. Suprisingly enough, it was easily one of the biggest stories for us in terms of difficulty and time to implement. I just wanted to talk through our path to making this story work. The back button is a fun, difficult challenge to be sure ;)

For the impatient, I’ll lay it out for you right up front. Put simply, the approach we were able to take to tackle the back button was as follows:

  • Have a client-side model that represents the state of the app (the user’s search criteria in our case)
  • Serialize that model (JSON) and post it to an iframe in response to user actions (actually, ours had a 300ms dirty-check interval that would post to the iframe).
  • Have the content that is loaded in the iframe notify (call a function) on the parent window passing it the result and the model used to get that result (basically just echo back the serialized model alongside the results).

And the longer-winded walk-through:

The site is for a real estate company. It allows users to find listings based on the typical (and some custom) criteria they specify via some widgets on the page. As they make their selections, the results are ajax-erifically loaded for them in the results area. They can page through the results and click on a listing to see further detail. The detail page is a full page load with a restful url.

From what I’ve described so far, the back button should behave as follows:

  • If the user is viewing page 3 of the results, then goes to page 4, the back button should take them back to page 3 of the results.
  • If the user is on a detail page, the back button should take them back to the results (and it should be on the correct page if the user was paging).

Sounds simple enough right? In Web1.0, this is no big deal. The user changes search criteria and clicks ’submit’, server answers with the results and pre-populates the page with the users’ criteria. One full-page load later, the page has everything it needs wired right into its very source code. User adds more criteria, clicks submit and voila, another page load and another full page that has all the data it needs. Click the back button and that first page returns…still wired up with the results AND the criteria for that page…as though the last page load never even happened.

Web2.0(-ish, there really isn’t a social aspect to this app, but its more lively that the typical sites) we want to stay on the same page…just load the results portion. This makes the user experience more fluid as the page reloads tend to be more jarring. It should also be more performant as there is less to render (fewer database queries) and ultimately less data to transfer.

The first task to handle is actually getting some results via ajax, and this is fairly simple. The user makes their selections and an ajax call is made to get the matching results. The results are then plopped into a div on the page when they return. The server code is pretty straight forward, simpler than 1.0 even, because it just has to answer the specific question and render only the results. No need to re-populate the user’s criteria. Now, how to handle the back button?

As described, the back button will do what it was designed to do: take the user to the previously viewed page. When the user is searching in our app, they are only ever on one page, so nothing they are doing is added to the browser history. The actions that they expect to be able to be ‘backed out’ of are not. For the back button to EVER work, we needed to have our ajax code add to the browser history.

One approach (described http://dojotoolkit.org/node/100) is to simply add to the fragment identifier portion part of the url (everything after the #). The browser treats these as links to elements within the current document and will attempt to scroll to the element whose id matches the hash value. So it will add to the history, but it won’t reload the page. In our case, we’d do this for every criteria change or pagination change (switch from results page 3 to results page 4).

At this point our app would be adding to the ‘history’ and the back button would be changing the url…but the results won’t be changing just yet because we don’t have anything to respond to/reset the results when the back button was clicked. Essentially, we need to store some state (the users criteria and the results) with the value we put on the url, so that when the back button is clicked, that state can be restored. That is exactly what the dojo undo and the RSH and others do for us…but we have to have clean urls so adding #’s to the current url won’t do.

The other approach is pretty old-school. Load the results via a hidden iframe. As the user changes criteria we modify the src of an iframe to get the results instead of via an XmlHttpRequest. The iframe src changes are added to the browser’s history as well. We’d need a way to know when the iframe was ready with the results so that the page could pick them up and display them. We knew that some ajax frameworks would degrade to using an iframe approach when a proper XHR is not available…but the simplest thing we could get to work at the time was to have an onload in the result content that the iframe loaded that would tell the parent window what the results were by calling a javascript function on the parent. Basically the results would announce themselves to the containing window. (In hind-site our final approach may be more re-usable if we switch to monitoring the readystate of the iframe via and onreadystatechanged handler).

So at this point, we have results loading in the iframe, then the iframe telling the parent that results were loaded and the parent placing the results on the page. When the back button is clicked, the iframe loads the previous page of results (because iframe src changes are added to the browser history) and the iframe again tells the parent that results were loaded and the parent displays those results. And we still have our pretty urls ;)

However…we don’t have the correct criteria. The iframe only loads the results. So if the user changes the criteria a few times, then clicks the back button, the results will ‘go back’ but the criteria will not change, so they still appear to be the last criteria used. Not only that, but if we load a detail page (those are full page loads) and hit the back button, the correct results page will load, but there will be NO criteria present (because the criteria change on the page dynamically and hence are not part of the source that is restored by the back button…and the iframe that IS restored only has the results). If only the criteria were part of the results…

That’s pretty much what we did. We decided that we needed to have the results page also pass along the search criteria. When loaded, the results AND the criteria used to get those results would be passed along to the parent. Then the parent would render the results and restore the criteria.

The real key in simplifying the back button problem was to organize our javascript into true objects and have a model that could be used to represent the state of the page. That model could be passed around via the iframe and restored with ease in response to the back button. The road to refactoring the javascript to get to this point is another interesting topic for another day.

Thinking out loud, it would be interesting to revisit the RSH and dojo approaches and see if there could be a hybrid to what we needed to do here (or maybe they could already support what we need and we just didn’t see it ;)). I’m thinking like an api that would make an XHR call, then when the results come back, adds a # to the src of an iframe, then puts the result text as well as the model into an input in the iframe. There would be a readystatechanged listener on the iframe that would attempt to restore the state of the parent page from the ‘results’ and model stored in the iframe.

Sorry, no code for this one…just our story.

Test First Javascript

Sunday, March 11th, 2007

I want to try an experiment. My goal it is to demonstrate how good OO principles and patterns can be applied when solving a problem with javascript. To achieve this goal, I’m going to develop a custom javascript component Test First and try and document the process as I go. I’m hoping this will also help me with some ideas I’m struggling with in the javascript component arena.

The component is a Task List. The first set of requirements are:
The user can add tasks to a list
Tasks are just a short description of what is to be done
The tasks can be marked as complete

The tasks won’t be persisted right now. It will just be in the browser. I know thats not a lot…but I’m the user and I can clarify as we go ;). Each page in this experiment is a JS Unit test page. I’m gonna do my best to communicate what the thought process is for each test and for each refactoring. The ‘thoughts’ will be followed by the test code and then the Task List code that made the test pass. Changes are in red. The test code background is blu-ish…and the code background is gray.

I’ll post more blog entries as the component progresses. You can follow the progress of the tests here. Each test page will link to the next at the bottom. At the top of each test page is a link to open the jsunit test runner for the page.

Automated Tests with Junit + Selenium + JsUnit

Tuesday, March 21st, 2006

I plan to have a more cohesive post on the process I went through and some of the specific tweaks I had to make…but right now excitement has the best of me and I’m going to let the keys fall where they may.

Recently, the project I’m on REALLY needed some automated testing. We had (have) a rich web-app (ajax/dwr) and were beginning to feel very uncomfortable without tests…a bit like going “without a net”. We had been researching Selenium for the integration/ajax testing and jsunit for some (although admittedly very few) of the javascript objects/classes/widgets we had written, but nothing automated. It was time…so I dove in.

To make a long story short, what I have today is Selenium integrated jUnit tests. The jUnit Tests look like this:

public class SomeScreenTest_UT extends BaseSeleniumTestCase{
public void testCanSearchOrders(){
open(”/products/search”);
assertNotVisible(”searchResults”);
type(”searchField”, “amber bottles”);
click(”searchButton”);
waitForValue(”searchStatus”, “complete”);
assertText(”resultsMessage”, “Found*”);
}
}

Upon execution, a browser will open to the SeleneseRunner.html and will begin running the tests. It is really pretty sweet. The base test case we’re using will open the login page and login first on setup. Then the tests do their thing and if a selenium assert or command fails, then the junit test fails.

I managed to get this working with Selenium 0.6.0.  I wound up implementing a SocketBasedCommandProcessor (can you guess what it does?) as well as a client to go with it. Extended DefaultSelenium to have SeleniumDriver (I needed to have more control over the url the browser would open…and I didn’t like the name). Also implemented a SeleniumDriverServlet to go along with it (the browser talked to the servlet…which talked to the processor via the client). I was pretty proud of it…even followed (fairly) strict TDD.

After implementing all this, I got to REALLY pouring through the Selenium code (last night) and noticed a few interesting classes…namely CommandBridge and CommandBridgeClient. In short, they do (or appear to do) what my implementation does, just without the sockets. It would have been REALLY nice to know about them first. I honestly felt I was tricked :) The docs are aparenly out of date and some of the examples refered to code that has been moved. When I couldn’t find them, I though they were just making suggestions ;) So one strike against the docs (btw not even the wiki has any mention of them). Even when reading the package structure, it wasn’t at all clear to me that the ‘outbedded’ package and the CommandBridge (which IS a servlet) were what I needed to get a servlet that talked to an external tomcat process…especially when nothing from the actual servlets package seemed to fit. Go Figure.

But after reading the forums, I’m not so sure the CommandBridge code even works. Aparently the “driven” stuff is undergoing a huge overhaul and is now going to be called “selenium remote control”. Which is good really because these guys have some good stuff under the hood and it just needs to be a bit easier to use. It could use some better names, and packaging…and definitely better docs, but it is still some great work.

Also, just today got Selenium to drive the jsunit tests for the app.  That was cool too…we needed jsunit to run in the build as well and I figured we already had this selenium setup, why not have IT drive the jsunit tests too.  It took some tweaking because jsunit expects to be the top level frame, but it worked.   I should mention that I do prefer to have jsunit run without need of the server running…and we still have that ability, this was just an easy-ish way to get it working with something we already had, just for the continuous integration.  I also know that jsunit has a server bit that ant can drive, but we wanted to be able to make it work just by running the junit test, this gives us that…no need for ant to kick anything extra off, just make sure the local server is running, which it usually is).  The test suite that selenium ‘drives’ is actually a jsp (so there IS one benefit to having the server running) that will scan the js folder for all _UT.html files, and adds the path to them to the test suite.