Jan 15, 2010

On-Screen Keyboard Widget using jQuery-UI

One Last Update: It's been a good while since I gave a good update on this project's state, so here's the deal. I started this project to fulfill a need I had at the time and opensourced it so someone might have a something to start with to fulfill a similar need. Turns out Mottie had a similar need and found this a good starting point. He has now built an on-screen keyboard widget much better than anything I had intended to develop. This post is staying here for reference, but I highly recommend going over to Github and get the latest version of Mottie's branch instead.

A while back I came to a project which I was developing for a small touchscreen device with a limited Linux install which may or may not have a physical keyboard attached. While our overall plan for the project was to not have any forms to fill out and thus no requirement for a keyboard, we saw a potential need down the road and rather than wait until we must have it I set out to see what kind of touchscreen keyboard jQuery could make possible.

Now, as a touchscreen with no keyboard is a very limited need in the jQuery world there were very few examples out there and none that took full advantage of many of the things build into jQuery UI. So, I new pretty early that I'd be building something from scratch. I wanted it to not just be a function you call that happened to use jQuery, but rather built as a true widget much like the datepicker built into jQuery UI. I also wanted it to be very customizable: resize with the reset of the page, have multiple keyboard layouts, even allow users to define their own keyboard layout. After a little planning and a couple days of coding here and there I came up with this:

Samples removed due to jQuery plugins site being erased

With a simple jQuery call and a few additional lines of css you can have any keyboard layout you could possibly need.

$('input[type=text], input[type=password], textarea')
    .keyboard({
        layout:"qwerty",
        customLayout:
            [["q w e r t y {bksp}","Q W E R T Y {bksp}"],
            ["s a m p l e {shift}","S A M P L E {shift}"],
            ["{accept} {space} {cancel}","{accept} {space} {cancel}"]]
    });

It has been tested on most browsers supported by jQuery UI and works. At this time the keyboard will center itself top-to-bottom on the left side of the screen, this is a feature I plan to change so that the keyboard appears from the field you are actually editing. I have they entire source available for download at both jQuery Plugins and Snipplr. Feel free to give any feedback you may have.

Creative Commons Attribution-Share Alike 3.0 Unported License

Update: It seems something in Blogger's style are forcing the keyboard to only appear at the top of the page. I'm working on it quickly, until then you are able to scroll to the top when it appears and use it.

Update: Fixed!

Update: After a little more tweeking, the positioning of the keyboard works much better across the board. All sources have been updated.

Jan 12, 2010

Cascading Select Boxes using jQuery

Today, we came across a need for a set of cascading select boxes to filter through a list of Organizations, Territories and Location. When I've come across this need in the past I tended to use ajax calls to load a new list into the select box based on the selection. While the ajax option is good for very large sets of data to keep the initial load time of the page down, it creates a few minor issues for smaller data sets.

  1. Returning a short list of options via ajax can be slow due to having to make a separate call to the server.
  2. Additional requests being sent to what may already to a taxed server
  3. There can be a good amount of server-side code that needs to be written to handle and respond to the ajax requests.

For our smaller data set we felt it would be more beneficial to start by loading all available options during page load and use jQuery to quickly filter and show only the options we need from the options already loaded into the browser. I came up with this function to do just this.

function cascadeSelect(parent, child){
 var childOptions = child.find('option:not(.static)');
 child.data('options',childOptions);
 
 parent.change(function(){
  childOptions.remove();
  child
   .append(child.data('options').filter('.sub_' + this.value))
   .change();
 })
 
 childOptions.not('.static, .sub_' + parent.val()).remove();
}

I was shocked at the ridiculously small amount of code needed to provide this functionality. After pre-building may page with a form containing the select boxes and added a class to each option in the child select to define which option from the parent it was associated with ("sub_1" where parents value is "1"), simply call the function with the parent and child defined as jquery objects:

cascadeForm = $('.cascadeTest');
orgSelect = cascadeForm.find('.orgSelect');
terrSelect = cascadeForm.find('.terrSelect');
locSelect = cascadeForm.find('.locSelect');
 
cascadeSelect(orgSelect, terrSelect);
cascadeSelect(terrSelect, locSelect);
And voila:
Organization:
Territory:
Location:

I've posted a full sample html at Snipplr. Feel free to comment and give suggestions there. As with all my code it's licensed as Creative Commons Attribution-Share Alike 3.0 Unported License. Feel free to consume and improve accordingly.

Update: ::face-palm:: In my joy of completing a task that seemed to good to be true, I posted this before realizing it was to good to be true. At this time I've only had a chance to test this code in Firefox. After an attempt to show it off, it appears to not work properly in WebKit browsers and maybe others. I will run it through the full gauntlet soon and do by best to ensure cross-browser compatibility for all browsers officially support by jQuery.

Update 2: After a little work and a better understanding of what doesn't work to "hide" options in browsers other than Firefox, I now have code that does just as I explained above but works cross-browser. I have updated the code above as well as the full example on Snipplr. Again, please feel free to give any feedback you might have.

Confirmed Working:
  • Firefox (3.6 RC1, 3.5, 3.0, 2.0, 1.5, 1.0)
  • IE (8, 7, 6 SP2, 6)
  • Safari 4
  • Opera (10, 9)
  • Chrome 4