Saturday, May 31, 2008

Information Architecture Graceful Degradation Accessibility Case Studies

posted by Alex Grande
Based on the following website case studies, here are rules for planning graceful degradation into the architecture of a website: 1. Use forms, scrollbars, and other features that do not rely on javascript to display content. 2. When content is hidden using JS-dependent components (such as tabs) figure out how to design websites to display all this content without breaking surrounding widgets and components. 3. Hide javascript-dependent functionality from javascript disabled users. 4. Make sure all displayed links (anchors) or buttons do something, like go to new page or scroll to specific content for users who don't have javascript. 5. Build new pages that display content that otherwise would only be seen by JS users. Then you can use normal anchors to reach those pages. You can have unobstructive javascript that has event listeners on those links for sexy ajaxy functionality that users with JS can experience. Blogger.com Right now I'm drafting this article in Blogger.com without javascript. A few things immediately drew my attention that are confusing or do not work. Sure I can type in a textbox; that is no surprise. Here are some things don't work: 1. The shortcuts (shown at the bottom) for ctrl + B for bold or ctrl + P for publish do not work at all. They should have hidden these. 2. Post Options link that is normally a drop down menu for JS users does nothing at all- a dead anchor. 3. The top has an area called "Enclosures" with a URL input box and a MIME Type input box. I have no idea what these do. Although I can still post, many of the features that are designed for JS users still show up and should be hidden or graceful degrade to a new page showing opinions and so forth. I'm turning JS back on to finish writing this article! Seattle International Film Festival
At my company Pop Inc we care a lot about graceful degradation. We recently remade the SIFF website. I hadn't checked it out without JS before so lets take it for a spin.
I actually liked it more with javascript disabled. It loaded faster (without those JS scripts) and all the content was displayed on the page and not hidden in tabs and drop downs. I guess what to take from this website in the means of graceful degradation is designing the site ahead of time to be scalable to show all content without intruding on other components on the page. Ford About a year ago Ford's website displayed blank without javascript enabled. Since then they have stepped up and made a pretty nice graceful degraded homepage. They have honed in on a great tool to display content with minimal real estate: The scroll bar. While tabs, drop downs, carousels, accordions, and sliders all rely on javascript, the scrollbar works in any scenario as a way to show hidden information on the page. Ford has taken advantage of a horizontal scrollbar to display different featured cars. Horizontal scrolling is a means that is not often utilized, but is nonetheless intriguing. KEXP KEXP is a Paul Allen/Publically owned radio station in Seattle. It is the source for indie rock music, as well as other speciality music shows. They have a pretty badass website featuring streaming archived radio shows of the past two weeks. You can listen live broadcasts anywhere in the world. Their live stream gracefully degrades as it is an anchor to a .pla file that can be opened in your favorite streaming music client. But their archived "On Demand Radio" feature is completely broken for non-javascript users. Nothing is more maddening than to click a link or button and have nothing happen. That is exactly the case for KEXP's archived radio "go" button. It relies on a javascript intrusive function. Sadly, the "go" button's A tag's hyper reference reads "javascript:selectArchive('program');". This problem can be easily rectified by rewriting it as form with the "go"action being a input submit button. This solution works up until you reach the archive player page, the final step before listening to music. This is where a small popup comes up with a Windows Media or Real Audio player that allows you listen but not download the radio broadcast. There may be legal reasons why this cannot be a downloadable file. If this is the case KEXP should notify their users with some sort of message detailing why you must have javascript enabled to use the archive player. AOL AOL did it right with their news story switcher on their homepage. With javascript allows cycling through the latest news without a page refresh. What a nice user experience! If javascript is disabled the right and left arrows on the switcher does exactly the same action except with an additional page refresh. In other words, each news item has it's own page viewable to any user. iGoogle We all know that Google's products rely heavily on javascript. Without javascript their maps will not display and their web browser email client won't send mail. But even simple things like the calendar and date/time widgets on iGoogle.com are completely broken and blank. To a user who is not web savvy, this may be confusing to why the calendar is not loading while it says it is. Like blogger.com's issues, Google needs to hide functionality that is javascript-dependent for javascript disabled users or inform the user why it isn't showing.

Labels: , ,

Tuesday, May 27, 2008

What is Bad About CSS Sprites?

posted by Alex Grande
CSS Sprites have changed the way we think about website performance efficiency, but we should not be using them on every graphic and here is why: 1. Accessibility How are you going to set alt tags to background images? You can't! Screen reader users will hate you if you use background images for all your graphics just to save some seconds on page load. Being hated is bad. 2. Printing Core graphics should not be CSS Sprites because background images are not shown when printing. In short, CSS Sprites are great for banners, rounded corners, drop shadows, certain icons, and certain buttons. But be careful how you use them. They should not be used everywhere.

Labels: , ,

Sunday, May 25, 2008

User Interface Design: DIY vs Copy-and-Paste

posted by Jonah Dempcy

Drawing from a SxSW 2008 talk titled "Filching Design," I'd like to discuss some of the pros and cons of designing user interfaces completely from scratch versus lifting elements of them from other sites.

First

Conducting Technical Interviews for Web Developers

posted by Jonah Dempcy

Presumably most readers of this blog will have been through a technical interview, and I'm betting that some of them have to give technical interviews as well. The purpose of this article is to go over some of the points to consider while conducting the interview, as well as suggestions for coming up with good questions to ask.

The first thing to understand as an interviewer is the requirements of the job position and what competencies a candidate must possess to be a good fit. If the job is for back-end development, then experience working with databases, performance tuning, and writing scalable code is crucial, and only a cursory understanding of HTML and CSS is required. Alternately, for UI development work, it isn't necessary to have a candidate who is wrapped up in the back-end code or has Big O notation at the tip of their tongue. There are certainly jacks-of-all-trades but when interviewing, it's better to focus on a few crucial areas than try to set high standards for all areas across the board.

These are some of the areas you may want to explore, depending on the requirements of the position:

Software Development Skills

  • Proficiency in at least one high-level programming language: C++, Java
  • Basic understanding of object-oriented design & analysis: classes/inheritance, interface vs implementation, encapsulation
  • Experience with source code version control: SVN, CVS, P4
  • Algorithms: binary search, quicksort
  • Data structures: array, hash, linked list
  • Complexity/performance analysis: Big O notation, performance testing

Web Development Skills

  • Candidate tests web-deployed code in big 3 browsers: IE6+, Firefox 2+, Safari 2+
  • Candidate uses browser-specific debugging tools: IE Web Developer Toolbar, Script Debugger/Script Editor (IE), Firebug (Firefox), WebKit/Drosera (Safari)
  • Extensive knowledge of at least one JS framework: jQuery, Prototype, MooTools, YUI
  • Experience creating dynamic, database-driven websites: PHP/JSP/ASP, MySQL etc
  • Candidate uses Selenium for front-end automated testing
  • Proficient in Fireworks or other image editing software, and knowledgeable about how to best export images for the web (CSS sprites, GIF/JPG/PNG, etc)
  • Follows front-end best practices like CSS-based layouts and unobtrusive JavaScript
  • Experience developing rich user interfaces (drag-and-drop, Ajax, web applications/RIAs, animation effects)

General and Miscellaneous Skills

  • Familiarity with agile development methodology: scrum, burndown charts
  • Proficiency at Search Engine Optimization (SEO), Search Engine Marketing (SEM), and related fields
  • Knowledgeable about security vulnerabilities in code. Experience with threat analysis software (e.g. Fortify for Java)
  • Familiarity with automated unit/integration tests: JUnit (Java), Selenium (front end)
  • DBA-related skills such as database optimization
  • Usability skills and knowledge of user behavior
  • Project management skills

You can choose from these or other criteria to create a baseline for what makes an ideal candidate, ignoring the skills which aren't relevant. Once you've determined the skills necessary for the position, you may find that a Ph.D. is less qualified than someone without a college degree, depending on the skills required. So, never make assumptions about candidates simply based on their credentials or previous work experience. Developers can take drastically different amounts of time to complete tasks based on their prior experience and their ability to learn new things quickly.

Criteria for Various Job Positions

Let's look at a few example job positions and apply criteria from the list above.

Quality Assurance Engineer

An ideal QA engineer for a website will be an expert at creating automated test suites for front and back end, and should also be aware of security issues. So you could focus the interview on how they choose to write test suites, and come up with sample test suites for real-life objects (e.g. an elevator). Then you could ask them to list some of the common security threats (e.g. XSS, XSRF) and how to mitigate them.

Of course, if you already have a security engineer at your company, then you may not require your QA to wear another hat. But for most small- to medium-sized businesses, it's most efficient and cost effective for your developers to wear many hats. In fact, you may have a software developer who also does QA on the site, or a QA engineer who does project management. It all depends on the size of your company and the particular restraints in budget for your development team.

Software Developer / Programmer

Let me say right off the bat that I've never conducted a software development interview. But, I have been through a few, and I have an idea of what types of questions I would ask depending on the position.

There are some general software development skills that I would consider, such as experience with SVN (or other source control), automated testing, knowledge of data structures and algorithms, and perhaps object oriented design and analysis.

If the candidate is a Java developer, then you can ask about things like exception handling, reflection, familiarity with libraries (Swing, Struts, JSF etc), or other Java-specific questions. If they are a C++ developer then you should drill down into memory management, constructors/destructors, and other tropes of C++. It's up to you as the interviewer to determine the right questions to ask based on the job requirements, or to find someone else to conduct the interview if you can't. I know I would have a hard time conducting an interview with a C++ developer, unless I had a "cheatsheet" with all the correct answers to the questions.

Web Developer

For a web development position, the focus will be less on knowledge of traditional computer science and more on the candidate's knowledge of current best practices. Since best practices are always changing, the ideal web developer is enthusiastic and passionate about their field, and stays up to date by reading/contributing to blogs, belonging to email lists or other online communities and generally keeping "in the know" about the best way to solve problems.

Unlike traditional computer programming, in which code is executed in a controlled environment and changes to the language take years to complete, web development is a fast-paced field where changes happen all the time and code is executed in a wide range of environments, each with its own caveats and bugs. So, the ideal web developer is familiar with the issues surrounding development on each platform and environment, especially the Big 3 browsers (IE, Firefox and Safari).

Interview Coding Challenges

A great way to test a developer is to actually have them write some code for you. As they say, "the proof is in the pudding," and even a developer who successfully answers your questions may not be able to complete the test put forth. The test can be verbal (if conducting the interview by phone), on a whiteboard (if in person) or you can give the candidate a time limit and have them email you the code. The choice is yours, just make sure that you choose one modality for all candidates. It isn't fair comparing one candidate's typed code to what another said on the phone, or drew on a whiteboard. Also remember that generally typed code will be higher quality than that spoken over the phone or written on a whiteboard.

I've posted a few JavaScript challenges on this blog that would make good interview questions for a JS developer. You could also adapt some of these to other languages such as C++, PHP or Java:

Other challenges include reversing a string, reversing the words in a string, finding words that occur in both files, converting a clock from digital time to degrees (e.g. for an analog clock's hands), or generally any other relatively simple test you can come up with.

Layouts and HTML/CSS

For candidates doing CSS, just send them a mock-up and give them a reasonable amount of time to complete the HTML/CSS for it. You could optionally send a PSD or layered PNG, but more commonly people just send flat images. Really, the test shouldn't be much -- maybe the wireframe for a site, or a navbar, menu or UI component. You can also give them some challenge questions about explaining various CSS properties and how to use them. Challenges I've had in the past are:

  • Make a banner and horizontal navbar that matches the dimensions of a wireframe
  • Make a 3-column site with a fluid-width center column
  • Create a vertical drop-down menu or horizontal flyout menu (bonus points for a cross-browser CSS-only solution, though JavaScript solutions are accepted)
  • Explain the difference between absolute and relative positioning, and what happens when you put one type of element inside another
  • Explain the difference in block and inline display
  • Explain what effect float has on elements. Does float change elements' display property, and if so, does it make it block or inline? (Answer: block)
  • Explain what effect z-index has on elements and the natural z-index order.
  • Explain CSS specificity.

On this last point, CSS specificity, I find it's fun to show a list of CSS rules and ask which rule wins out. If you know the trick how rules are calculated, it's easy to tell:


<!-- Sample HTML code -->
<p class="description" id="main">
  Some text.
  <span class="special">
      Some more text.
  </span>
</p>
/* Challenge:
 * What color is the "special" text? */

body .description span {
   color: blue;
}

p.description {
   color: red;
}

body .description .special {
   color: pink;
}

#main .special {
   color: orange;
}

#main span {
   color: green;
}

For those of you unfamiliar with how CSS specificity is calculated by browsers, it is a point-based system where the selector with the highest point value wins. The three main things selectors consist of are elements, classes and ids. These are worth 1 point, 10 points and 100 points respectively. Given this knowledge, do you know which rule wins out in the above example?

By calculating the point values of the rules, you see that the rule #main .special is worth 110 points and is the winner, hence the color of the "special" text is orange.

Hiring People who Write Good Code

Besides "just working", the criteria for what makes good code differs from person to person, but in general, you'll want to look for readability, useful comments, simplicity, accuracy, good formatting and code that doesn't repeat itself. So, even if two candidates submit working solutions to a problem, you can still determine which is the better candidate by analyzing the code itself with these criteria in mind.

In addition to this, you may consider their talent, as opposed to their current skill in any one area. If the candidate is highly talented and passionate about the position, then they will most likely be able to learn any particular skills required for the position, within reason. Of course, talented individuals should demonstrate expertise in a number of domains already, which will give you an idea of their potential for growth in job-specific areas. You may also consider their participation in online forums, discussion groups and blogs (like this one, wink wink) related to their job field. Active participation in online communities demonstrates dedication and passion for those particular fields of interest.

I hope this article has been informative for interviewers and perhaps interviewees as well. Feel free to post your favorite (or least favorite) interview questions.

Tuesday, May 20, 2008

Inspecting Javascript with Firebug

posted by Alex Grande
1. Go to http://www.alexgrande.com/breakable/ and add the link to your bookmarks.


2. Go to the page you are interested in inspecting and click on the bookmark in your browser called "Inspect JS". That will load breakable.js into the head of the page you are currently using.


3. In firebug go into the scripts tab and view breakable.js.
4. Set a break point on line 7 where it says funct();


5. Find out what the ID is of the element that has the event listener.


6. Click the console tab in firebug. Type in: breakOn("elementID", "eventTrigger"); Example: breakOn("prettyImageOver", "onmouseover"); Example 2: breakOn("ajaxImageChanger","onclick");


7. (1)Initialize the event by mousing over it or whatever you you chose as the event trigger. If the original js author chose a mouse over event then you should also use a mouse over event. Because if you choose an onclick for an onmouseover event then by the time you click it the event has already passed you by..
8. (2)Firebug will transfer over to the breakable.js in the scripts tab at the breakpoint funct(). Step into it by (3)clicking the arrow right in firebug script section.


9. and you will enter into the javascript code of whatever function is called by the event.


I love this! Big thanks to my friend Grady Morgan for writing this short script!

Labels: ,

Saturday, May 17, 2008

The Group Class in MooTools

posted by Jonah Dempcy

The MooTools Group class is for grouping a collection of objects and calling a function when all of those objects have fired a particular event. For instance, you could group an array of Element objects and fire an event when all of them have been clicked. Or, you could group a collection of Ajax request objects and fire an event when all have completed. (Both of these examples are straight from the MooTools 1.2 Beta docs for Plugins/Group).

How is this useful? Well, it's a convenience for when events depend on each others' completion but occur asynchronously, either by Ajax or through use of timers. Let's look at an example of how to handle dependent events without grouping first, to see where there is room for improvement:

// Flags to keep track of the request status
var firstRequestIsComplete = false;
var secondRequestIsComplete = false;

window.addEvent('domready', function() {
   var firstRequest = new Request({
       url: 'example-1.html',
       onSuccess: function() {
           firstRequestIsComplete = true;           
           if (secondRequestIsComplete) {
               console.log('Both requests are complete.');
           }
       }       
   }).send(); // Fire it
      
   var secondRequest = new Request({
       url: 'example-1.html',
       onSuccess: function() {
           secondRequestIsComplete = true;           
           if (firstRequestIsComplete) {
               console.log('Both requests are complete.');
           }
       }       
   }).send(); // Fire it   
});

Obviously, this isn't a stellar solution. Having to keep track of which Ajax requests have already fired using flags is a hassle, and this example only uses two requests. Say you had 10 different requests and you wanted to fire an event once all of them had completed. You'd need to store flags for which request had fired and which hadn't, perhaps in a hash table, and write a function that checks if all of the requests had fired or not. Then, for the onSuccess event handler of each request, you'd need to set a condition that calls the function to check if all had succeeded, and performs your desired action if so (in the above example, logging to the console).

What a hassle! All of this checking and logic duplicated in each request; there must be a better way.

Grouping Ajax Requests in MooTools

Ajax Ajax by nickwheeleroz

As it turns out, there is a better way, thanks to MooTools. Let's see how this code is cleaned up by the use of grouping with MooTools' Group class:

window.addEvent('domready', function() {   
   var firstRequest = new Request({url: 'example-1.html'});
   var secondRequest = new Request({url: 'example-1.html'});           

   var ajaxRequests = new Group(firstRequest, secondRequest);
   ajaxRequests.addEvent('onSuccess', function() {
       console.log('Both requests are complete.');
   });
  
   // Fire requests
   firstRequest.send();
   secondRequest.send();
});

All right! Now we've removed the duplicate logging code and onSuccess events, which were previously declared in two places. This code has the exact same effect as the previous implementation, but we're following the "write once" principle.

You could make it even more concise using chaining, if that's your thing:

window.addEvent('domready', function() {   
   new Group(new Request({url: 'example-1.html'}),
                       new Request({url: 'example-1.html'}))
       .addEvent('onSuccess', function() {
           console.log('Both requests are complete.');
       }).instances.each(function(instance) {
           instance.send();
       });
});

To me, the chaining isn't necessary here and iterating over the instances array stored in the group object probably isn't the best idea, since it's exposing the inner workings of the class by direct reference, rather than through the API. So, this example is only shown for the purposes of demonstrating how you can chain functions with MooTools, if you so desire.

Caveats and Gotchas with MooTools Groups

There are a few crucial gotchas that will hang you up if you aren't aware of them. First, the API documentation for MooTools 1.2beta is not accurate. It may be accurate for 1.1, but it certainly isn't for 1.2. They show an example of grouping using the Ajax class which was renamed Request in MooTools 1.2. The docs call the request() method to initiate an Ajax request but in actuality, it is the send() method. Can you spot any other inaccuracies in the example provided by the docs?

// From http://docs12b.mootools.net/Plugins/Group
//
// NOTE: This code will not work for MooTools 1.2.
// Shown for educational purposes only.

var xhr1 = new Ajax('data.js', {evalScript: true});
var xhr2 = new Ajax('abstraction.js', {evalScript: true});
var xhr3 = new Ajax('template.js', {evalScript: true});

var group = new Group(xhr1, xhr2, xhr3);
group.addEvent('complete', function(){
   alert('All Scripts loaded');
});

xhr1.request();
xhr2.request();
xhr3.request();

A few other problems with the example: It shows the onComplete event instead of onSuccess, and the arguments passed to the Ajax constructor are incorrect as well. Finally, it shows the event to monitor without the 'on-' prefix. Given the plethora of inaccuracies in the docs, I believe these nuances warrant further discussion. Let's look at each of these in turn.

The Ajax class was renamed to Request in MooTools 1.2 and the API was significantly overhauled. Instead of calling request() to initiate the request, now you must send() it. Now, the constructor takes an options object which has properties for all of the arguments, such as url. In the example given, the URL is passed as the first argument, but in the current code you provide it as a property of the options object instead:

// This code demonstrates the differences in how to make
// Ajax requests between MooTools 1.1 and MooTools 1.2:

// Old way
new Ajax('path/to/your/data', {
   // options go here
}).request(); // fire the request

// New way
new Request({
   url: 'path/to/your/data'
   // options go here
}).send(); // fire the request

As you can see, the URL is passed in via a property of the options object, rather than as the first argument of the constructor. I prefer this, as the MooTools API has been made much more consistent in 1.2 with most classes simply taking an options object, rather than a variety of parameters.

Another problem with the example provided in the docs is that it shows the onComplete event when this has been changed to onSuccess. And, to make matters worse, the docs exclude the 'on-' prefix in their example. Compare the right and wrong ways to monitor group events in MooTools 1.2:

// Wrong way (as shown in docs)
group.addEvent('complete', function(){
   alert('All Scripts loaded');
});

// Right way
group.addEvent('onSuccess', function(){
   alert('All Scripts loaded');
});

This is kind of odd, I'll admit, and inconsistent with the addEvent() API. Usually, you register events without the 'on-' prefix. But, for some reason, when you monitor an event in the group, you must use the 'on-' prefix. I discovered this after 20 minutes fruitlessly searching the web and stepping through my code, so hopefully this gem of information will save others that same trouble.

Testing MooTools Ajax Locally

Something crucial to be aware of when testing these examples is that you must serve the response to the Ajax request from a web server. If you access the page through the file:// protocol instead of http:// protocol, certain headers will not be present and MooTools won't fire the onSuccess event.

There's actually a funny story about this: At my old job, there was a contest to build an Ajax widget using various JavaScript frameworks. The MooTools team got hung up because they were testing through the file:// protocol (without a web server, just by opening the file directly with a web browser) and the Ajax requests were never firing their onComplete event handlers (this was back in MooTools 1.1 where it was called onComplete instead of onSuccess). Well, what's funny about it is that the Ajax request would fire onFailure, but it called the event handler with the Ajax response! The team building the widget actually got it working fine but it was "failing" every time-- the Ajax callback function was registered to the onFailure event.

So, the moral of the story is, try to avoid hacky workaround like this and serve up the Ajax responses the right way, through a web server.

Besides Ajax, what are some other uses of groups you can think of? Post your ideas in the comments and they may become the basis of future articles.

Labels: , ,

Friday, May 16, 2008

MooTools mouseenter and mouseleave Events For Dropdowns

posted by Jonah Dempcy

One of the frustrations many coders have encountered when creating JavaScript flyout menus is the fact that onmouseover and onmouseout events fire when entering children elements. This is usually not the desired effect when creating popup, dropdown or flyout menus.

MooTools

MooTools offers its own mouseenter and mouseleave events which alleviate the problem of the events firing when entering child elements, but before diving into that, let's explore why it's such a problem in the first place.

At first thought, it would seem that you want onmouseout to fire when leaving the element, right? Well, generally that's only the case when you're mousing off a flyout menu, for instance. But when you're staying inside the flyout menu and mousing over one of the child elements, you don't want the onmouseout event to fire, since it will have an event handler registered to close the flyout.

Take, for example, this simple flyout menu. Shown here is the barebones HTML for the menu:
  • Level 1
    • Level 2
    • Level 2
    • Level 2
  • Level 1
    • Level 2
  • Level 1
    • Level 2
    • Level 2

This results in a list with 3 top-level headings, each containing 1-3 sub headings. Each sub-heading is represented as a ul element with class flyout-sub, and will be hidden using CSS and only visible when the user mouses over its respective top level heading. Here's the CSS:

ul.flyout-sub {
    display: none;
}
li.active ul.flyout-sub {
    display: block;
}

Pretty simple stuff. We just hide the second level by default and show it if it has a parent li with class active. So, it's up to the JavaScript to toggle the active class.

Here is the JavaScript. Note the use of oldschool JavaScript event registration using the core DOM events onmouseover and onmouseout.

window.addEvent('domready', function() {
    $$('ul.flyout-main > li').each(function(li) {
        li.onmouseover = function() {
            li.addClass('active');
        }
        li.onmouseout = function() {
            li.removeClass('active');
        }
    });
});

If you test this code, you'll see that it doesn't have the desired effect: The onmouseover and onmouseout events fire when entering child elements so you're unable to mouse over the sub menu without closing it. A simple change to MooTools mouseenter and mouseleave events (which only fire when entering/exiting from the parent) solves this problem nicely:

window.addEvent('domready', function() {
    $$('ul.flyout-main > li').each(function(li) {
        li.addEvent('mouseenter', function() {
            li.addClass('active');
        });
        li.addEvent('mouseleave', function() {
            li.removeClass('active');
        });
    });
});

Before I knew about MooTools' mouseenter and mouseleave events, I devised a few workarounds to this using onmouseover and onmouseout. One such solution was to have the event handler function check to see if the targetElement of the event object was a child of the flyout menu. But, whichever solution I devised, it was sub-optimal and involved additional logic for something that should be free in a programming language. Now, thanks to MooTools, it is-- providing you include the MooTools library, of course.

Armed with this knowledge of how mouseenter and mouseleave differ from the conventional onmouseout and onmouseover events, you now have another powerful time-saving tool for making dynamic web interfaces. Instead of mitigating annoying issues with the limited set of core JavaScript events, you can focus on what you want the code to do, not how you want to do it.

For more information, visit the MooTools 1.2 beta docs for Element.Event.

Labels: ,

Wednesday, May 14, 2008

Recursive Functions in JavaScript

posted by Jonah Dempcy

Recursive functions are functions which conditionally call themselves. A common use for recursive functions is anywhere you need to iterate on data, where you would normally use a for() or while() loop.

I can't say that I've used much recursion in production code, but it's a fun topic to write about, as there are plenty of fun uses of recursion to explore.

Let's start with a basic use of recursion, and replace a for() loop with a recursive function. In this case, we'll implement a simple function to reverse a string, first using iteration and then recursion.

// Iterative
function reverseString(str) {
    var rev = new String();
    for (var i = str.length - 1; i >= 0; i--) {
        rev += str[i];
    }
    return rev;
}

console.log(reverseString('test')); // Logs 'tset'
// Recursive
function reverseString(str) {
    var firstLetter = str.charAt(0);

    function iterate(str) {
        if (str.charAt(str.length-1) != firstLetter) {
            var charToReverse = str.charAt(str.length-1);
            str.charAt(str.length-1) = '';
            return iterate(charToReverse + str);
        }
    }
    return iterate(str);
}

console.log(reverseString('test')); // Logs 'tset'

JavaScript Challenge: Reverse a Linked List

posted by Jonah Dempcy

This article is a follow-up on my previous post on writing a linked list class in MooTools. In that post, I offered an implementation of a singly-linked list in JavaScript and gave a challenge: To reverse a linked list in place, starting with only a reference to the head. The challenge also forbade the use of storing the data externally -- that is to say, you can't just iterate over the linked list and stuff the items in a new array, then reverse the array.

As promised, here is my solution to the challenge. If you are planning on trying the challenge yourself, read no further.

I've included the code for defining the LinkedList and LinkedListItem classes as well. It's separate from my solution, so if you want to give it a try yourself, go ahead and copy the code below.

var LinkedList = new Class({

    // Input: Array collection
    // An array of items to turn into a singly linked list
    
    initialize: function(collection) {

        this.collection = new Array();
                   
        // Construct the linked list
        collection.each(function(item, i) {
            var item = new LinkedListItem(item);
            this.collection.push(item);
            
            if (i > 0) {
                this.collection[i - 1].setNext(item);
            }
        }.bind(this));
        
        this.setFirst(this.collection[0]);
    },
    
    // Input: LinkedListItem item
    // Sets the head to be the given item
        
    setFirst: function(item) {
        this.head = item;
    },
        
    // Returns: LinkedListItem head
    
    getFirst: function() {
        return this.head;
    },
    
    // Returns: Array result
    // An array with all items' values, in the order they are linked
    
    getAll: function() {
       var result = new Array();
       
       result.push(this.getFirst().getValue());
       
       var item = this.getFirst().getNext();
       while (item) {
           result.push(item.getValue());
           item = item.getNext();
       }
       
       return result;
    }
});

// This class defines an individual item in the linked list.
// The value it is instantiated with is stored in the value property,
// and is accessible via getter/setter methods.

var LinkedListItem = new Class({
    initialize: function(item) {
        this.value = item;
    },
    
    getNext: function() {
        return this.next;
    },
    
    setNext: function(item) {
        this.next = item;
    },
    
    getValue: function() {
        return this.value;
    }
});

For an in-depth discussion of this code including step-by-step commentary, check out the previous article, LinkedList Class in MooTools.

Reverse a Linked List: Solution

This is what I came up with:

window.addEvent('domready', function() {

    var list = new LinkedList(['a', 'b', 'c', 'd', 'e', 
                               'f', 'g', '1', '2', '3', 
                               '4', '5']);

    // Challenge: Reverse the linked list in place (list.collection) 
    //            without using another array

    // Input: LinkedList list

    function reverseLinkedList(list) {

        // Inputs: LinkedListItem item, LinkedListItem next
        //
        // Returns true if next item exists
        // If not, sets the head to be the existing item
        
        function checkForNextItem(item, next) {
            if (!$defined(next)) {
                list.setFirst(item);
                return false;
            } else {
                return true;
            }
        }
        
        // Our new tail is the old head
        var tail = list.getFirst(); // 0

        var itemOne = tail.getNext(); // 1

        // If there is only one element in the list, return it
        if (!$defined(itemOne)) {
            return list;
        }
        
        // Remove the old link from the tail
        tail.setNext(null);                      

       // Prepare third item variable for while() loop
        var itemThree = tail;
        
        // The while() loop will stop when the checkForNextItem() 
        // function fails to find a next item and calls 
        // list.setFirst() on the final existing item.
        
        while (list.getFirst() == tail) {
            
            itemTwo = itemOne.getNext(); // 2
            itemOne.setNext(itemThree); // link 1 to 0            
            if (!checkForNextItem(itemOne, itemTwo)) break;
                              
            itemThree = itemTwo.getNext(); // 3            
            itemTwo.setNext(itemOne); // link 2 to 1
            if (!checkForNextItem(itemTwo, itemThree)) break;
            
            itemOne = itemThree.getNext(); // 4
            itemThree.setNext(itemTwo); // link 3 to 2
            checkForNextItem(itemThree, itemOne);                       
            
        }
        
        return list.getAll();  
    }
    
    console.log('Forward: ' + list.getAll()); 
    console.log('Backward: ' + reverseLinkedList(list));
    
    // RESULT:
    //
    // Forward: a,b,c,d,e,f,g,1,2,3,4,5
    // Backward: 5,4,3,2,1,g,f,e,d,c,b,a    
});

The main part of the function that does the work of reversing the linked list is the while() loop. I use a helper method, checkForNextItem(), which is declared at the top.

You'll notice I declared the function inside the reverseLinkedList() function. The reason I did this is that checkForNextItem() is a private method that shouldn't be accessible to code outside of the scope of the reverseLinkedList() function. Just like declaring local variables when you don't need a global, you can declare private methods inside the scope of the function block. It's perfect for helper methods like this and it doesn't clutter the global namespace.

To explain what the checkForNextItem() actually does, let's examine the while() loop where it's used.

        while (list.getFirst() == tail) {
            itemTwo = itemOne.getNext();
            itemOne.setNext(itemThree);         
            if (!checkForNextItem(itemOne, itemTwo)) break;
                              
            itemThree = itemTwo.getNext();           
            itemTwo.setNext(itemOne);
            if (!checkForNextItem(itemTwo, itemThree)) break;
            
            itemOne = itemThree.getNext();
            itemThree.setNext(itemTwo);
            checkForNextItem(itemThree, itemOne);                              
        }

The while() loop continues until list.getFirst() no longer returns the tail (the old head). At that point, the loop knows to finish because the checkForNextItem() method, unable to find a next item, will set the head to be the final remaining item in the list. Thus, the old tail becomes the new head and the linked list is reversed.

The only other area of the code which warrants discussion is the check if there is only one item in the list, and calling setNext(null) on the new tail (the former head):

        // Our new tail is the old head
        var tail = list.getFirst(); // 0

        var itemOne = tail.getNext(); // 1

        // If there is only one element in the list, return it
        if (!$defined(itemOne)) {
            return list;
        }
        
        // Remove the old link from the tail
        tail.setNext(null);        

The check to make sure itemOne is defined is simply a fail-safe for linked lists with only one item. In that case, tail.getNext() will be undefined so we just return the list as it is, with only one item. Originally I wrote the check using the JavaScript typeof keyword (i.e. if (typeof itemOne == 'undefined')), but then I remembered to use MooTools' nifty $defined() method which is both more succinct and easier to read (in my opinion).

Besides the check for single-item lists, I also had to remove the old link from the tail (formerly the head). If you don't remove the link, the while() loop will never end, since the tail will link to the next-to-last item which links back to the tail, forever and ever. So that's why I call tail.setNext(null) there.

Refactoring the Code

After writing this, something just didn't feel right about the while() loop. It was messy and seemed to be doing the same thing 3 times. I remembered back to the first time I solved this challenge and recalled a more elegant solution. After changing the code, I ended up with this:

window.addEvent('domready', function() {
    var list = new LinkedList(['a', 'b', 'c', 'd', 'e', 
                               'f', 'g', '1', '2', '3', 
                               '4', '5']);


    // Input: LinkedList list
    // Returns: LinkedList list (reversed)

    function reverseLinkedList(list) {

        // Our new tail is the old head
        var current = list.getFirst(); // 0

        // If there is only one element in the list, return it
        if (!$defined(current.getNext())) {
            return list;
        }
                
        var previous = null;
        var next;       
        
        while (current != null) {
            next = current.getNext();
            current.setNext(previous);
            previous = current;
            current = next;        
        }
        list.setFirst(previous);
        
        return list;  
    }
    console.log('Forward: ' + list.getAll()); 
    console.log('Backward: ' + reverseLinkedList(list).getAll());
    
    // RESULT:
    //
    // Forward: a,b,c,d,e,f,g,1,2,3,4,5
    // Backward: 5,4,3,2,1,g,f,e,d,c,b,a    
});

This code is more optimized and nicer to read. It has the same functionality but the while() loop has been significantly simplified and the checkForNextItem() method has been eliminated altogether. It's a good thing when you can reduce complexity like that.

One other minor edit I made during refactoring was to change the function from returning list.getAll() to just returning the list itself. After thinking about it, I prefer for the input and outputs to both be of type LinkedList and then you can just call getAll() on the result. It's a minor nuance but worth pointing out.

Leave comments if you'd like a deeper explanation of a specific area of the code or if anything is unclear, and of course your own implementations are always welcome.

Labels: , ,

Tuesday, May 13, 2008

LinkedList Class in MooTools

posted by Jonah Dempcy

A commonly used data structure in Java and other programming languages is the linked list (Wikipedia). It is similar to an array in that it is a way to store a collection of objects in sequence, however unlike an array you cannot access items in the collection by index. Instead, you access them through links from other items in the collection, or from the initial head or tail link (which can typically accessed with getFirst() and getLast() methods, respectively).

Data Structure - Linked List

Each item in the collection has a getNext() method which returns the next item in the collection. If it's a doubly-linked list then each item (save for the head and tail) will have both getNext() and getPrevious() methods.

In JavaScript, no such construct exists, so we'll create one. This is mostly for academic purposes but there's nothing to stop you from using it in real code if you're so inclined. For the sake of this practice, we'll just make a singly-linked list as it is a bit simpler.

This also provides an introduction to defining classes in MooTools. We aren't using much MooTools-specific functionality here so it could easily be ported to another library, or even written without the use of a library. But, since I use MooTools for most of my projects, I decided to use it here. I think you'll find defining classes in MooTools to be quite easy.

The LinkedListItem Class

Here is the code to define the LinkedListItem class. The LinkedListItem takes a value of any type (string, object, etc) and returns an object of type LinkedListItem, which has the original value stored as the value property on the object. It also defines accessor methods for getting/setting the next item in the list. There is also an accessor method for getting the original value.

// This class defines an individual item in the linked list.
// The value it is instantiated with is stored in the value property,
// and is accessible via getter/setter methods.

var LinkedListItem = new Class({
   initialize: function(item) {
       this.value = item;
   },
  
   getNext: function() {
       return this.next;
   },
  
   setNext: function(item) {
       this.next = item;
   },
  
   getValue: function() {
       return this.value;
   }
});

Read on to see how the LinkedListItem class is used by the LinkedList.

Defining the LinkedList Class

The LinkedList class will take an array of objects of any type and create a linked list from those objects. The linking occurs in the order of the array. So, if you call it with ['a', 'b', 'c'] you'll get back a linked list with a as the head, and c as the tail. The list will be linked like so: a -> b -> c.

The LinkedList class has three methods: setFirst(), getFirst() and getAll(). The former two are accessor methods for changing the first element, i.e. the head. The latter is a method which returns an array of all the items in the linked list, in the order they are linked.

There are quite a few other methods you could implement for the LinkedList but for now, these will suffice. If you're particularly brave and inclined to add to the class, go ahead and check out the Java LinkedList docs to see what other methods are available.

Here's the LinkedList class I came up with:


var LinkedList = new Class({
  
   // Input: Array collection
   // An array of items to turn into a singly linked list
  
   initialize: function(collection) {

       this.collection = new Array();
                 
       // Construct the linked list
       collection.each(function(item, i) {
           var item = new LinkedListItem(item);
           this.collection.push(item);
          
           if (i > 0) {
               this.collection[i - 1].setNext(item);
           }
       }.bind(this));
      
       this.setFirst(this.collection[0]);
   },
  
   // Input: LinkedListItem item
   // Sets the head to be the given item
      
   setFirst: function(item) {
       this.head = item;
   },
      
   // Returns: LinkedListItem head
  
   getFirst: function() {
       return this.head;
   },
  
   // Returns: Array result
   // An array with all items' values, in the order they are linked
  
   getAll: function() {
      var result = new Array();
     
      result.push(this.getFirst().getValue());
     
      var item = this.getFirst().getNext();
      while (item) {
          result.push(item.getValue());
          item = item.getNext();
      }
     
      return result;
   }
});

There are only two areas of the code that need explaining: constructing the list in the initialize() method, and constructing an array from the list in the getAll() method. I'll touch on each to make sure it is clear what exactly is going on.

The initialize() method is called on each instance of the class, with the arguments given to the constructor of the class. Calling new LinkedList(['foo', 'bar']) will call the initialize() method with the argument ['foo', 'bar'] as well. This is an automatic function call handled by MooTools and should not be done manually, unlike Prototype where the developer must manually call the initialize() method after instantiating the class.

The first thing our LinkedList initialize() method does is create the collection property. This is so it can store the data internally, and this property is not meant to be publicly accessible to other code. The collection array will be used to store the LinkedListItem objects created from the input array.

I use MooTools each() method to iterate over the items in the input array (collection). For each item, I create a new LinkedListItem and add it to the collection property where it will be stored. I then set the previous item's link (if it exists) to the current item, using the setNext() method.

Finally, once the each() loop has finished iterating over all the items in the collection, I set the head link to be the first item in the array, using the setFirst() method.

The other interesting area is the getAll() method, which does basically the opposite of the initialize() method. Instead of constructing a LinkedList from an array, the getAll() method returns an array in the order of the LinkedList. So you could instantiate a LinkedList in a certain order, change the order around by various setNext() calls, and then call getAll() and get back an array in the new order. This might not seem very useful at the moment, but bear with me and I'll demonstrate its utility through a challenge or two.

The getAll() method uses a while() loop instead of an each() loop, because we don't know the length of the list. Even if the length of the collection array is 10, for instance, we have no guarantee the list will be 10 items. The coder may have called setNext(null) on the head, in which case the list would only have a length of 1. Therefore, we must use a while() loop that continues as long as their are next items, in other words, as long as getNext() on the current item is not null.

Inside the while() loop, we simply add the current item to the result array and then redefine the item variable to point to the next item. After the loop finishes, we return the result, which is an array in the order that the list is linked.

You can test that this works by instantiating a LinkedList and calling getAll() on it.

window.addEvent('domready', function() {

   var list = new LinkedList(['a', 'b', 'c', 'd',
                              'e', 'f', 'g', '1',
                              '2', '3', '4', '5']);

   console.log(list.getAll());
  
   // RESULT:
   // a,b,c,d,e,f,g,1,2,3,4,5
});

LinkedList Challenge

Next up, a LinkedList challenge! Can you write code that reverses a linked list in place, without creating a new array?

My next post will provide an answer, but in the meantime, give it a try. To make it clear, the challenge is to reverse a singly-linked list, using either the LinkedList class defined here or your own implementation, in place. That is, without creating another array, hash or other data structure to store the data in the list temporarily before writing it again.

This is the expected outcome:

   var list = new LinkedList(['a', 'b', 'c', 'd',
                              'e', 'f', 'g', '1',
                              '2', '3', '4', '5']);

   console.log('Forward: ' + list.getAll());
   console.log('Backward: ' + reverseLinkedList(list));
  
   // RESULT:
   //
   // Forward: a,b,c,d,e,f,g,1,2,3,4,5
   // Backward: 5,4,3,2,1,g,f,e,d,c,b,a   

I should also mention that you can't access the collection array inside the linked list directly. Otherwise, you could just do something like this:

function reverseLinkedList(list) {
   return list.collection.reverse();
}

That would be a cheap, and is not an acceptable solution. This challenge is to reverse the list in place, so your only choice for starting is to call getFirst() on the list:

function reverseLinkedList(list) {
   var head = list.getFirst();
}

This challenge was given to me in Java by my mentor at Amazon, Chris. It took me a good 45 minutes to solve the first time (though Chris made me solve it on a whiteboard, which is always more difficult for me than typing it up). Good luck and stay tuned for the next post which will offer a solution to this challenge.

Labels: ,

Monday, May 12, 2008

JavaScript Challenge: Dispense Change

posted by Jonah Dempcy

This challenge is to write a function that takes an amount of change and returns a String Array with the coins to dispense.

Examples:

  • .25 returns ['quarter']
  • .26 returns ['quarter', 'penny']
  • .30 returns ['quarter', 'nickel']
  • 1.01 returns ['dollar', 'penny']
  • 1.16 returns ['dollar', 'dime', 'nickel', 'penny']

One of the requirements is that this returns the least amount of units of currency as possible. In other words, you can't return 100 pennies or even 4 quarters when 1 dollar would suffice. This is modeled after how real-life vending machines work.

Land of the Vending Machines Land of the Vending Machines By gullevek

If you want to attempt to solve this challenge yourself, read no further! The rest of the article will go on to describe my solution to this problem and some of the issues I encountered along the way.

Using MooTools' round() Method

I originally wrote my solution to this challenge without the use of JavaScript libraries. However, I ran into a common problem when dealing with dollar values, which is that I needed to round them to the 2nd decimal place. Without rounding to the 2nd place, you end up with subtle flaws in the data.

For instance, I was using an if() statement to check if the change remaining to dispense is greater than or equal to 0.1 (one cent). Before I added the code to round to the 2nd decimal place, the if() statement wouldn't evaluate to true when there should be a penny left to dispense. The change remaining should have been .1, but it wasn't entering the if(changeToDispense >= 0.1) statement. How could this be? Well, I was subtracting the change, but upon closer inspection, some of the values were not what I expected. When I subtract .25 from .48, I ended up with 0.22999999999999998 for instance.

Of course, I should have ended up with 0.23, not .022999 (repeating). But, without rounding the number, it ended up slightly less than the correct amount, introducing subtle bugs into the code. Since the full amount wasn't being subtracted, the last penny would never be dispensed because there wasn't a whole penny left (it was something like 0.09999999999999998). Thus, I rounded to the second decimal place and it solved the problem.

Using core JavaScript methods, it's possible to work around this problem. It involves multiplying by 100, rounding and then dividing by 100. But, since most of the projects I work on use JavaScript libraries and nearly all of them have round() functions in some form or another, I decided to get a little help from a library. Specifically, I use the MooTools round() method for solving this problem.

The round() method takes how many places after the decimal to round to, so by calling round(2), a number like 0.0999 (repeating) is converted to 0.1.

Vending Machine - Japan Vending Machine - Japan By rytc

Note that as the method is added to the prototype of the Number object (i.e. base class), it is called as an instance method of the number you want to round, not as a method of the Math object as with core JS methods. Compare:

  • Core JS: Math.round(1.8);
  • MooTools: 1.8.round();

Designing a Solution

My idea for the solution is to store a hash of the coin values keyed by their name, and then iterate over them checking if each value is higher than the amount of remaining change to dispense. For this to work, I need to iterate over them in order from highest to lowest. Otherwise, if I start in random order, it will break one of the requirements: This must return the least units of currency as possible. If I start at the penny, then it would keep dispensing pennies until there was no change left, and never get on to the other units of currency.

Therefore, the loop needs to iterate over the currency values in order of highest to lowest: dollar, quarter, dime, nickel, penny. Here is the hash object that I created to represent the currency values, and then we'll discuss how to iterate over them in order:

 var money =
     'dollar': 1.00,
     'quarter': 0.25,
     'dime': 0.10,
     'nickel': 0.05,
     'penny': 0.01                                 
 };

You might think it's as easy as using a for/in loop, but unfortunately, browser compatibility issues prevent it from being this simple. Using a for/in loop will iterate over the items but there is no guarantee of the order. Internet Explorer and Firefox use the correct order (the order of declaration) as far as I know, but Safari in particular does not follow this. So, it's not as simple as:

function dispenseChange(changeToDispense) {
 var result = new Array();
 for (var unit in money) {
     if (money[unit] > changeToDispense) result.push(unit);
 }
 return result;
}

It would be nice if it were this simple, and indeed in most browsers it is. But due to obscure quirks with the order of iteration over objects of properties, we need to store the order in its own array.

Asahi Beer Vending Machine: Komatsu Asahi Beer Vending Machine: Komatsu By jpellgen

A Working Solution

I've changed the money object to now contain two properties, a values hash and an order array.

 var money = {
     values: {
         'dollar': 1.00,
         'quarter': 0.25,
         'dime': 0.10,
         'nickel': 0.05,
         'penny': 0.01                                 
     },
     order: ['dollar', 'quarter', 'dime', 'nickel', 'penny']
 };

Now I can iterate over the order array and use that to key the values hash. It's one extra step in the process but it doesn't add too much bloat to the code, and it actually works (at least, in the browsers I've tested):

function dispenseChange(changeToDispense) {         
 var result = new Array();
 while (changeToDispense.round(2) >= 0.01) {
     for (var i = 0; i < money.order.length; i++) {
         var value = money.values[money.order[i]]; // Number, e.g. 1.00
         if (changeToDispense.round(2) >= value) {
             result.push(money.order[i]); // String, e.g. "dollar"
             changeToDispense -= value;
             break;                      
         }
     }
 }
 return result;
}   

You can test the function with logging statements:

window.addEvent('domready', function() {
 var tests = [.01, .05, .11, .27, .74, 1.08, 1.99, 2.07];
 tests.each(function(testValue) {
     console.log(testValue.toString() +
                 ': ' +
                 dispenseChange(testValue)
     );
 });
});

Improving the Implementation

One area for improvement is the nested for() loop inside the while() loop. Right now it isn't ideal, since it will waste iterations checking for currency amounts higher than the current currency. To explain what I mean, let's look at the loop again:

 while (changeToDispense.round(2) >= 0.01) {
     for (var i = 0; i < money.order.length; i++) {
         // ...
         if (changeToDispense.round(2) >=
             money.values[money.order[i]) {
             // dispense unit of currency
             break;                      
         }
     }
 }

I commented out some of the implementation details so we can focus on the structure. The reason that this is sub-optimal is that after each unit of currency is dispensed, the for() loop breaks and the while() loop starts it over again. Say there is less than a dollar remaining to dispense-- for each unit of currency dispensed, the for() loop will still check for a dollar.

To optimize this code, we'd have to only iterate over the order array once. This means the for() loop would be outside the while() loop, and for each unit of currency, the while() loop would continue to dispense change of that unit until the change remaining is less than the unit's value. It would be optimized to n (see Big O notation for details) at that point.

Optimized Code

Here is the new and improved code with the optimized loops:

var money = {
 values: {
     'dollar': 1.00,
     'quarter': 0.25,
     'dime': 0.10,
     'nickel': 0.05,
     'penny': 0.01                                 
 },
 order: ['dollar', 'quarter', 'dime', 'nickel', 'penny']
};
function dispenseChange(changeToDispense) {
  
 var result = new Array();
 money.order.each(function(unit, i) {
     var value = money.values[unit]; // Number, e.g. 1.00
     while (changeToDispense.round(2) >= value) {
         result.push(unit); // String, e.g. "dollar"
         changeToDispense -= value;
     }
 }); 
 return result;
}
  
window.addEvent('domready', function() {
 var tests = [.01, .05, .11, .27, .74, 1.08, 1.99, 2.07];
 tests.each(function(testValue) {
     console.log(testValue.toString() +
                 ': ' +
                 dispenseChange(testValue)
     );
 });
});

An observant reader will notice that I changed the for() loop to use MooTools' each() method. The reason I did this is that we no longer need to break out of the for() loop, and I prefer the each() notation. (Thanks for recommending it, Lowell!)

As always, feel free to post your own solutions to this challenge in the comments.

The latest in Japanese vending machines
The latest in Japanese vending machines By El Fotopakismo

Labels: , ,

Saturday, May 10, 2008

jQuery drop down menu delay (setTimeout)

posted by Alex Grande
To create a navbar with a subnav (drop down menu for instance) with a delay before closing using jQuery (jQ) follow this code. An example can be found here. Javascript (js):
function navbar() {
// create variables
var subNavTimer;
var open;
// to make sure that when user mouses over sub menu ul it stays open
$("ul.subNav").mouseover(function() {
 $(this).show();
 // lets remember what's open
 open = $(this).parent();
});
 
// when user mouses over main item in navbar
$("li.navMainLink").mouseover(function() {
 // close other nav item submenus
 if (open != null) open.children("ul").hide();
 // stop the timer
 clearTimeout(subNavTimer);
 // show this nav item's sub menu
 if ($(this) != open) $(this).children("ul:hidden").show();
 
});

// when user's mouse leaves the navbar item
$("li.navMainLink").mouseout(function() {
 // lets keep tabs of what is open
  open = $(this);
 // start the timer for 2 seconds until it closes.
  subNavTimer = setTimeout('open.children("ul:visible").hide();', 2000);
});
}

$(document).ready(function() {
 navbar();
});
HTML:
<ul id="navMain">
<li class="navMainLink">
  <a class="navLink" href="#">Performances</a>
  <ul class="subNav">
   <li>
    <a href="#">Season Subscriptions</a>
   </li>
   <li>
    <a href="#">About the Shows</a>
   </li>
  </ul>
 </li>
</ul>
CSS: Your decision! Make it a vertical drop down or make it a horizontal fly out. Just make sure to have the ul.subNav be display: none; by default in your CSS.

Labels: , ,

Thursday, May 8, 2008

Using MooTools' Hash.Cookie API

posted by Jonah Dempcy

MooTools makes working with cookies quite easy. Based on functions from Paul Peter Koch's QuirksMode, the MooTools API for handling cookies is intuitive and easy to remember.

For storing simple bits of information on the browser, the Cookie methods read(), write() and dispose() are adequate. As their names imply, these read, write and delete cookies from the browser, respectively.

But, if you want to do something more complex, such as save a JSON string each time a method is called on the cookie, then Hash.Cookie is for you.

What's the difference between the Hash.Cookie methods and the standard methods you say? For one, Hash.Cookie is its own module, so make sure the version of MooTools you're using has it included. Besides that, Hash.Cookie gives you all the functionality of the Hash class, including getters/setters, each(), some(), filter(), has(), keyOf() and more. For the full documentation on all Hash methods available, view the Hash docs here.

Here's an example that uses the Hash.Cookie class to store how many times a user has visited the page. Note that this uses Firebug so if you don't have it enabled, you'll need to override console.log().

window.addEvent('domready', function() {
    var cookie = new Hash.Cookie('test-cookie');
    
    var numberOfVisits = $defined(cookie.get('numberOfVisits')) ? 
                         cookie.get('numberOfVisits') :
                         0;
                         
    cookie.set('numberOfVisits', numberOfVisits + 1);
    
    console.log('You have visited the page ' + 
                 cookie.get('numberOfVisits') + 
                 ' times.');        
});

On line 1, we start by declaring everything to occur when the DOM is ready. For MooTools developers, this should be a familiar practice. Although we aren't manipulating the DOM in this example, I've left it in, as Firebug threw an error when I tried to console.log() before the DOM was ready.

On line 2, we instantiate a new cookie named 'test-cookie' without any options. Interestingly, this also has the effect of loading the cookie if it already exists. This means that the first time the user visits the page, it will write a cookie, but on all subsequent visits it will read a cookie. The end result is the same: we have a reference to the cookie as a hash object, and can use the methods that the Hash class provides.

Next, on lines 4-6, we use a ternary operator to check if 'numberOfVisits' has already been written to the cookie. If so, we read it from the cookie. If not, it is initialized to 0.

On line 8, we increment 'numberOfVisits' using the set() method. Finally, we log the number of visits using Firebug's console.log().

Getters, Setters and Auto-Saving Cookies

Getters and setters should be familiar to anyone versed in Java but I imagine some JavaScript developers may be unfamiliar with them. Essentially, get() and set() methods are provided for you to use instead of directly accessing the data. For instance, cookie.hash.numberOfVisits and cookie.get('numberOfVisits') both return the same data, but the former directly accesses the data while the latter uses a wrapper method.

There are numerous reasons to use getters/setters instead of directly accessing the data, but one practical example is the use of Hash.Cookie's autoSave functionality. There is an autoSave flag in the options which, if enabled, will save the data after each operation on the hash. It defaults to 'true' which is why I didn't have to write() the cookie in the above example.

Since I didn't define a duration in the options object when instantiating the cookie (nor did I even include an options object, for that matter), it will only live for the duration of the browser. In other words, it's a session cookie: When the user exits the browser and restarts, the cookie will be destroyed. For the cookie to live beyond the lifespan of the browser, you can specify the duration of the cookie to live (in days).

The other options which I omitted were domain, path and secure. None of those were important for my example but for some applications, setting domain- and path-specific cookies and ensuring they are only accessed over HTTPS may be necessary

This example is somewhat simple but I hope it has provided an introduction to using MooTools' excellent Hash.Cookie API. Good luck and enjoy a tall glass of soymilk with your Hash Cookies!

Labels: , ,

Tuesday, May 6, 2008

JavaScript Date Object

posted by Jonah Dempcy

In this article I will explore the use of the JavaScript date object and its methods. We won't be using any JavaScript libraries because the existing API is quite adequate, and most libraries don't really offer much in the way of extensions anyway.

Let's start with displaying the current time. You can do this by using the Date() constructor and calling the toString() method on it. If you have Firebug, open up the console and type this code:

var date = new Date();
date.toString();

You'll see that date.toString() evaluates to a string containing the current time in Greenwich Meridian Time (GMT). In my case, it is "Tue May 06 2008 16:06:39 GMT-0700 (Pacific Daylight Time)."

There are a number of other to methods available as well. You can paste these into the Firebug console to see what they evaluate to. I've put my example as a comment after each one:

  • date.toDateString(); // "Tue May 06 2008"
  • date.toGMTString(); // "Tue, 06 May 2008 23:06:39 GMT"
  • date.toLocaleDateString(); // "Tuesday, May 06, 2008"
  • date.toLocaleString(); // "Tuesday, May 06, 2008 4:06:39 PM"
  • date.toLocaleTimeString(); // "4:06:39 PM"

Practical Applications of the JavaScript Date Object

One of the most obvious applications of the date object is to display the current time, should you desire to do so. Oftentimes news websites will contain the day's date, and sometimes they have a clock as well. For example, see the BBC's homepage which displays the day's date and an analog-style clock done in Flash:

The BBC follows the format "Tuesday 6 May 2008" for example. Let's construct that using JavaScript:

function getFormattedDate(date) {
    var days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
    var day = days[date.getDay()];

    var months = ["January", "February", "March", "April", "May", "June", 
                  "July", "August", "September", "November", "December"];
    var month = months[date.getMonth()];

    // Returns a string formatted like "Tuesday 6 May 2008"
    return day + " " + 
           date.getDate() + " " +
           month + " " + 
           date.getFullYear();
}

getFormattedDate(new Date());

You could also accomplish this with some RegEx hackery but I think this is cleaner, albeit a bit more verbose. The benefit of doing this with RegEx is that you can use one of the existing toString() methods, such as toLocaleString(), and then parse the data you need from there.

Actually, given that the data is delimited by commas, you could accomplish this simply by splitting the returned string without the use of RegEx. Doing it that way would avoid the need to tediously specify the names of all the days and the months.

Creating a Countdown Timer in JavaScript

Another use case for the JavaScript date object is to count down the time remaining until a certain date. This could be the launch of a new website or product, the cut-off time for a submission, or anything else that requires notification of the time remaining until a specific date and time.

On a website I previously worked on, Endless.com, there is a countdown timer in the banner that notifies the customer how long they have to order to get next day shipping. This is a fun idea because it adds a little pressure for the customer to make an impulse buy as the cutoff time comes closer. I've seen similar uses on other e-commerce sites and it adds a nice touch, for those sites which offer next-day shipping.

To do something like this, I imagine you'd need two functions, one to update the HTML with the correct date string (given a date object as input), and another, possibly anonymous function that is declared with a setInterval, which decrements the current time and calls the update function. Without writing the implementation details, I imagine it would look something like this:

function updateTimeRemaining(date) {
    // implementation details for parsing data from date object
    // and updating the DOM
}

window.onload = function() {
    var interval = setInterval(function() {
        updateTimeRemaining(new Date());
    }, 1000);
};

The updateTimeRemaining() function would need to be written to output the time remaining given a date object. I feel that there may be a better way to do this than creating a new Date() instance every second, however. Could we not use the same date object but decrement its time manually every second? In any case, this isn't a full-fledged solution at this point, just an outline of one possible solution.

What are your favorite uses of the JavaScript date object? Do you have other use cases that I've left out here? Please leave them in the comments and I may even devote a whole article to them. There is quite a bit you can do with date and time and I'm always on the lookout for new uses. Hopefully these short examples will get you thinking about how you can use the JavaScript date object on your site.

Monday, May 5, 2008

Downloading the Full MooTools Library

posted by Jonah Dempcy

One of my minor frustrations on the MooTools download page has always been having to manually select each component to add. While it is nice that you can keep the filesize down by omitting components that you don't need, I wish they had a way to select all components at once.

Well, it turns out they do. I just found out that Mootools actually allows you to select all components by calling the Download.all() method. You can do this by opening the Firebug console while you're on the MooTools download page and entering Download.all() and clicking Run. Alternately, if you don't have Firebug, you can enter javascript:Download.all() into the addressbar and press enter. This will select all components on the page.

Labels: ,

Truncating Text with JavaScript

posted by Jonah Dempcy

Since Alex posted an article on truncating text with Prototype, I thought I would write an article on truncating text without the help of a JavaScript framework.

I won't define a class like in his example, but I will try to provide the same functionality, and perhaps add some functionality as well. This code will search for all HTML elements with the class 'truncate', or with another className if one is specified, and will truncate to the nearest word less than the character-count you specify. It also defaults to appending '...' (the ellipsis) but you can override this with an empty string as the third optional parameter, if you prefer not to have anything appended.

Note: I wrote in the comments on Alex's article that you may want to add ellipsis to his code. I realized after reading the Prototype API docs that in fact, this is exactly what it does by default. Unless you specify an override with an optional parameter, the Prototype truncate() method will automatically add an ellipsis at the end of the string it returns.

Here is my implementation of the truncate() function, without the use of JavaScript libraries:


// Input parameters:
// String text, [Number length, String ellipsis]
//
// Returns:
// String text

function truncate(text, length, ellipsis) {    

    // Set length and ellipsis to defaults if not defined
    if (typeof length == 'undefined') var length = 100;
    if (typeof ellipsis == 'undefined') var ellipsis = '...';

    // Return if the text is already lower than the cutoff
    if (text.length < length) return text;

    // Otherwise, check if the last character is a space.
    // If not, keep counting down from the last character
    // until we find a character that is a space
    for (var i = length-1; text.charAt(i) != ' '; i--) {
        length--;
    }

    // The for() loop ends when it finds a space, and the length var
    // has been updated so it doesn't cut in the middle of a word.
    return text.substr(0, length) + ellipsis;
}

Truncating Text by Class Name

In Alex's example, he wrote a class in Prototype that you initialize with an array of HTML elements. For this example, we'll register an event handler to the window onload that will look for all elements with the class 'truncate' and truncate them to our specifications.

window.onload = function() {
    var elements = document.getElementsByClassName('truncate');
    for (var i = 0; i < elements.length; i++) {
        elements[i].innerHTML = truncate(elements[i].innerHTML, 100);
    }
}

It's basically doing the same thing as in Alex's example, except that I'm using the window.onload event instead of the custom 'domready' event (called dom:loaded in Prototype) which triggers much quicker. If you are using a framework, then use one of the custom 'domready' events because they trigger as soon as the DOM is accessible for manipulation, whereas the traditional window.onload event waits to fire until each and every image is finished downloading. Good grief!

Note that this example will only work in Firefox 3 or Safari 3, as other browsers do not natively support document.getElementsByClassName. You could just implement your own getElementsByClassName function like so:

function getElementsByClassName(className) {
    // RegExp from http://www.robertnyman.com
    //             /2005/11/07/the-ultimate-getelementsbyclassname/

    var match = new RegExp('(^|\\\\s)' + className + '(\\\\s|$)');

    // All elements which match are added to the array
    var matchingTags = new Array();

    var allTags = document.getElementsByTagName('*');
    for (var i = 0; i < allTags.length; i++) {
        if (match.test(allTags[i].className)) {
            matchingTags.push(allTags[i]);
        }
    }   
    return matchingTags;
}


window.onload = function() {
    var elements = getElementsByClassName('truncate');
    for (var i = 0; i < elements.length; i++) {
        elements[i].innerHTML = truncate(elements[i].innerHTML, 100);
    }
}

Make sure that you only give the 'truncate' class to elements whose only child is a text node. If the element has other HTML children then this won't work correctly. It's possible to code around this but for the sake of this example, let's just keep it simple. Here are two examples that show proper and improper use:

This is OK:

<span class="truncate">some text </span>

This is not OK:

<span class="truncate"><span>some text </span></span>

As always, feel free to post in the comments with any questions or alternative suggestions for the code. This is a fairly simplistic implementation and truth be told, if you're using a JavaScript library (which it seems most people are these days) then this may not be too relevant for you. But, hopefully it has been informative to explore this library-free, albeit simple, implementation.

Labels: , ,

How to Truncate in Prototype

posted by Alex Grande
This is to truncate multiple items in the javascript library Prototype. If it has the class="truncate" it will be truncated! Simple way:
var toTruncate = $$(".truncate");
var len = 190;
for(i=0;i


Class Based: 
var TruncateIt = Class.create({

      initialize: function(sTruncate) {

            var toTruncate = (typeof sTruncate != 'undefined') ? $$(sTruncate) : $$('.truncate');

            var len = 100;

            for (var i = 0; i < toTruncate.length; i++) {

                 toTruncate[i].innerHTML=toTruncate[i].innerHTML.truncate(len);

            }

      }

});

 

document.observe('dom:loaded', function(){ 

  var truncate = new TruncateIt('.truncate');

}); 

Thanks to Kris for helping me write this in a class as I'm totally new to prototype. See his hip new site at komputerart.com.

Labels: , ,

jQuery Plugin Validation with AJAX

posted by Alex Grande
The JQuery plugin Validation by bassistance.de is extension, powerful, and quick-to-deploy. I recommend this library for people already using the jQ framework to provide high-quality, usable client-side form validation. We are going to discuss basic properties of this framework as well as AJAX and scalability. Here is the code we are going to step through. This code was specifically designed to create an ajax experience for the medical technology company eMedTV.com. The live example can be found at this address. Here's my validation function:
$(document).ready(function() {
    if (pageName=="researchCenter") {
// Just use a different variable for each form you use. It is too difficult to use just one for all forms because each form has it's own plethora of needs (usually).
    var validator = $("#researchCenterForm").validate({
// #messageBox holds the all encompassing message at the top to let all the users know  there is a problem.
         errorContainer:"#messageBox",
// This is very important for manipulating the placement and styles of the error message for individual cases, such as first name or zip. I'll explain this later.
         errorClass: "formError",
// wrapper is only needed if you want to wrap the error message in something. If you  want a list the errors it can be a 
  • . I didn't really find any need for this unless you were listing all the errors at the top. wrapper: null, // errorPlacement is very important. It is where in the DOM you want the error of each idividual case to appear. I wanted each error to be placed at the beginning of each form element list item. See HTML example below. errorPlacement: function(error, element) { error.insertBefore(element); }, // On to the rules and without this the form wouldn't be what it is! rules: { // All the methods are found as having the name's inside the form. Thus in order for email to work I have to have a form element with name="email". zip: { // required:true is a must if you want the validator to check this element. required:true, // digits is one of the mime types of only digits. You put in alpha characters and it lets you know it doesn't like that! digits: true, minlength: 5, maxlength: 5 }, betterEmail: { required: true, email: true }, // If you have a confirm or email again area then we have some special rules to make sure this works. confirmEmail: { required: true, // #email is the id of the first email. For example, equalTo: "#email", email: true } required:true } }, //Here are all the messages for the different errors on the page. This is OOP so the method (firstname:) must match the ID of the required field. If it is not required it will not receive an error message. messages: { firstname: "Please enter your first name.", lastname: "Please enter your last name.", username: { required: "Please enter a username.", minLength: "Your username must consist of at least 2 characters." }, password: { required: "Please provide a password.", minLength: "Your password must be at least 5 characters long." }, confirmPassword: { required: "Please provide a password.", minLength: "Your password must be at least 5 characters long.", equalTo: "Please enter the same password as above." }, email: "Please enter a valid email address.", confirmEmail: "Please enter the same email.", zip: "Please enter a valid zip code.", agreeToService: "Please check the box." }, // Here is the fun part and the tricky part: AJAX // SubmitHandler is called when the form is called. When the users clicks submit on your form this is what happens. Since we want to do ajax you use the $.ajax jQ object. It has to start with function() {} for it to work. submitHandler: function() { $.ajax({ See serialize here http://docs.jquery.com/Ajax/serialize data:$("#researchCenterForm").serialize(), // The url you are going to submit to url: submit.php, timeout: 2000, // Sends a message to the console if it failed for testing purposes. error: function() { console.log("Failed to submit"); }, // If successful you can do something here. You can change elements, update text, or simply alert the response to first test to see if it worked. success: function(response) { alert(response); } }); } }) } });
  • First thing you'll probably notice is this plugin is formatted in object oriented programming. So it is very straight forward to know which methods and which properties to use for your form. Once you locate the ultimate guide to how to use the validator, it can become relatively straight forward to use. How to stylize where error label fields go on the page CSS:
    .formError {
            position: absolute;
            margin: 21px -13pt 0pt;
    }
    li {
            position: relative;
    }
    
    The margin is to position the error label below the form field, such as an email input box. Positioning it absolutely makes sure the forms look and feel is not altered by static elements. HTML:
    You'll notice the class name "betterEmail" and "required". This is to enter in jQ validation methods into the html markup itself. Cool huh? The HTML after the error has been applied in real-time:
    Once you have your form all setup you can start writing your own mime types if you include the mime type plugin as well. Currently the validator checks if your email is alex@alexgrande and that's it. If you want to make sure there is a .com or .org, etc you need this:
    jQuery.validator.addMethod("betterEmail", function(value, element) {
     return this.optional(element) || /^[^\x00-\x20()<>@,;:\\".[\]\x7f-\xff]+(?:\.[^\x00-\x20()<>@,;:\\".[\]\x7f-\xff]+)*\@[^\x00-\x20()<>@,;:\\".[\]\x7f-\xff]+(?:\.[^\x00-\x20()<>@,;:\\".[\]\x7f-\xff]+)+$/i.test(value);
    }, "Valid Email Please"); 
    
    See I how I use betterEmail above. Another is if you have a drop down menu like a select box with options. There isn't a default way to do this. My solution is the following. Notice the option with the value="none".
    jQuery.validator.addMethod("selectCountry", function(value, element) {
     return this.optional(element) || ( value.indexOf("none") == -1);
    }, "Please choose a Country."); 
    
    
    
    Another problem I have found is if you have three different select boxes, such as month, day, and year and you want only one error message for all three since they are all related. My solution is to make all three error message overlap making it appear they are all the same to the user. HTML:
  • What is your (or the participant) date of birth?

  • CSS:
    li#date .formError {
     left: 2px;
     bottom: -15px;
    }
    
    Thus every formError will overlap each other with the same position on the page. Last problem is fixing error form position issues in IE. That's easy. If you read my article about handling cross-browser issues then you'll know most of what to do. Since we positioned the error message absolutely we can position it using IE conditional comments only for IE. CSS:
    .ie li#date .formError {
     top: 1000px;
    }
    
    This is all for this discussion. If I think of more I'll add to it later. In either case, please send questions or comments.

    Labels: , ,