Reliving the Uncanny Valley

Daring Fireball Linked List: Bill Higgins: The Uncanny Valley of User Interface Design.

The Uncanny Valley is making the rounds again.  Daring Fireball says this is a good argument against building desktop-like applications on the web.

I agree that you can’t simply copy the desktop.  You shouldn’t in fact.  The web is a different platform with a different interaction model.  We’re lucky enough to be working at one of those rare moments when users are adopting a brand new platform en masse, which means we developers and designers get to experiment with whole new usage models.

On the other hand, that doesn’t mean we should throw out the rich experience found in desktop applications.  Drag and drop, multi-selection, keyboard control and direct selection make applications faster and easier to use.  They are especially useful on the web.

For a great example of this, take a look at MobileMe Gallery, which I think is one of the best user experiences on the web right now.  It seriously rivals iPhoto for quality of experience.

Of course, implementing these rich UIs means the complexity of your app will go way up.  This is why you need a framework like SproutCore to help you manage it.  SproutCore’s purpose is to worry about the mind numbing details of these rich interactions so you don’t have to.

We have a lot more to do before we can truly rival the desktop, but we’re well down the path.  The CollectionView classes, for example, provide some of the richest interactions for list and table views around. You can easily drag and drop, multi selection, and keyboard control right out of the box.

By all means, don’t copy the desktop on the web.  (Windowing systems don’t belong in the web browser for example.)  But don’t throw the baby out with the bath water.  Rich desktop-like interactions, when done right, can make web experiences vastly better.  And with frameworks like SproutCore, they are becoming easier to do.

(Thanks to jtaby for pointing out this article.)

SproutCore 1.0: SC.Event

SproutCore has always done its own event handling for the most part.  In fact, it was one of the first framework to implement a technique called “event delegation” to make event handling significantly faster.  In SproutCore 1.0 we’ve improved this event handling with the new SC.Event class.

About Event Delegation

If you’ve ever written regular DHTML-style JavaScript, you know the “standard” way to observe an event is to add an event listener to the DOM element you are interested in.  For example, if you want to receive mousedown events from a BUTTON element, you add an event handler to that element.  In Prototype you use a method like this:

Event.observe('#my-button', 'mousedown', function() {
  ... called on event...
});

This approach works fine when you want to enhance a few elements on your page, but when you try to display thousands of contacts or hundreds of photos or emails, it falls apart.  Each time you register an event handler on an element, there is a cost.  You have to wait for the browser to setup your handler, which can take a lot longer than you might think; especially in IE.

We solved this problem in SproutCore sometime ago using a technique called event delegation.  With this approach, SproutCore registers about 6 event listeners on the root document object when your application loads.   These listeners are notified anytime any event occurs on the page.  From there, SproutCore itself determines which SC.View’s you have created that are interested in receiving these events, if any, and invokes the event handler on that view directly.

How do you indicate interest in receiving an event?  It’s simple: just implement the event handler method.  For example, let’s say you want to receive the mouseDown event from the same button.  Just add the following to your button view class:

mouseDown: function(evt) {
  .. called on event ...
}

And you’re done.  No setup time.  It’s very fast.

Going Further

While event delegation helped speed up event handling in SproutCore, working with events was still not a panacea.  The problem is that the events you receive from browsers differs slightly from platform to platform.  While SproutCore always normalized this to some degree, it still meant you had to implement platform specific code in your handlers quite a bit.

For SproutCore 1.0, we had to implement some additional event handling code anyway to become DOM-library agnostic, so we decided to improve on this aspect as well by introducing a new class called SC.Event.

SC.Event is a W3C-compliant implementation of the Event interface with some useful additions.  All incoming events from the web browser are now wrapped in an SC.Event object before being passed onto your event handler.  This means you can now use the standard SC.Event API to work with any incoming event, regardless of platform.  This alone will make your code simpler.

In addition, SC.Event makes it much easier to dispatch your own custom events or to dispatch mock events for testing in a cross-platform way.  It also simplifies cleanup of event handlers on page unload, which will help to avoid memory leaks in your application.

Using SC.Event

Usually SC.Event will work transparently in the background to make your coding easier.  Whenever you implement an event handler (like mouseDown() above), the event object you are passed will be an instance of SC.Event.  Simply write your code as if you were working in events in Firefox, and you should be good to go.  You can also use some useful additional utility methods defined on the SC.Event class itself such as SC.Event.stop(), which both cancel’s bubbling and prevents the browser’s default action.

If you need to observe an event on a DOM element that is not handled by event delegation, such as the onscroll event, for example, you can also use the SC.Event API to register your own event handler.  Just setup your listener like so:

SC.Event.add(SC.$('#my-button'), 'scroll',  myObject, myObject.buttonOnScroll);

Note that the add() method takes both a target object and a target object method.  This means you can avoid having to call the Function.bind() method that is often required for both jQuery and Prototype code.

If you no longer need to listen to an event, you can remove your observer in a similar way:

SC.Event.remove(SC.$('#my-button'), 'scroll', myObject, myObject.buttonOnScroll);

When your page unloads, all event handlers are automatically removed, so there is no need to do this yourself.

Using SC.Event with Other Libraries

Most DOM-manipulation library provide an API for listening for events.  You can use these alternative methods to listen for events yourself if you want and SproutCore will work just fine.  If you are integrating a component from YUI, jQuery-UI or any other widget library, there is no need to rewrite their event handling.

However, I strongly recommend that you consider using SC.Event and the Responder API for view’s when writing your own code instead.  Since SC.Event is designed specifically for use with SproutCore-style applications and hence it will generally be faster and more efficient for your code.

Getting The Code

SC.Event is part of the new SproutCore 1.0 code base.  You can check it out today on the new Bitburger branch in Git.  See this wiki page for complete instructions on how to try out the code for yourself.

SproutCore 1.0: Introducing CoreQuery

One of the main things we use Prototype for is DOM manipulation.  Whenever a view’s state changes, Prototype help’s SproutCore change CSS classes, edit styles and change element nodes.

Now that we are trying to make SproutCore DOM-library independent, we needed a way for SproutCore’s built-in views to continue to work with the DOM without Prototype installed.  The solution we came up with was to create a new component in SproutCore called CoreQuery.

CoreQuery is a strict subset of jQuery.  In fact, if SproutCore detects jQuery’s presence when it loads, it will use jQuery instead.  By itself, however, CoreQuery is designed to be very small and lightweight.  It’s only about 200 lines of code without comments or whitespace.  It contains only the bare essentials needed by SproutCore view’s to render DOM.

This means that if you are creating a SproutCore application with no custom views, you could opt to use SproutCore without any other DOM library at all.  Or, you could load jQuery, YUI, Prototype, or whatever other library you prefer and use them to help you render your own custom View classes.  You could even choose to use CoreQuery yourself with your custom views.  SproutCore will work the same in all circumstances.

Using CoreQuery

If you want to try CoreQuery yourself, you can find the CoreQuery object in SproutCore 1.0 (aka “Bitburger”) today at SC.$().  This works just like the $() function in jQuery. (Subject to the limitations described below.)

Also, although this is not in the source yet, each SC.View instance will also have a $() that will return a CoreQuery object matching elements inside the view itself.  This will often be the fastest way to work with CoreQuery.  For example,  if you were creating a custom button and needed to update the button state when it changed, you might write:

// invoked whenever the button state changes...
render: function() {
  // set title, enabled state, and selected state...
  this.$().text(this.get('title'))
    .setClass('disabled', !this.get('isEnabled'))
    .setClass('sel', this.get('isSelected'));
},

CoreQuery code, like jQuery, tends to be very clean and concise, which is just what you need for a DOM-manipulation library.  It is a good companion to the more state-oriented approach of bindings and observers.  I’ll have more to say about how you can combine these kinds of API’s in the future, but suffice to say the code is just beautiful and really fun to write.

CoreQuery Limitations…

So what makes CoreQuery different from jQuery?  There are a number of big things left out:

  1. Complex Selectors.  CoreQuery understands simple selectors like “tagname.classname”.  None of the pseudo-selectors provided by jQuery will work.  Also, you can’t do any of the direct-desendant stuff like “parentag > .child-class”.  This is usually not a problem since you often use CoreQuery within the context of a single view and you only need to look for elements with a given class name.
  2. Event Handling. CoreQuery doesn’t know anything about listening for events on DOM elements since this is handled by SproutCore’s built-in event delegation system already. (Which is much more efficient.)
  3. All jQuery plugins. Anything not part of jQuery-Core is omitted.  If you want the full jQuery with plugins, use jQuery.
  4. Effects. jQuery’s effects (i.e. animation) library is not as useful for SproutCore-style views.  Additionally, we are working on something that makes better use of CSS-transitions.
  5. Ajax. SproutCore has its own Ajax functions; no need to copy jQuery.
  6. Script execution.  When you insert HTML that contains a <script> tag in jQuery, the JavaScript inside of those tags will be eval’d.  This does not fit with typical SproutCore usage and its evil anyway, so CoreQuery does not support it.

CoreQuery’s remaining scope is fairly limited to very basic DOM manipulation.  The good news is you generally do not need much else when building SproutCore views.  If you do need more, you can always import your favorite DOM library to get it.  The code overlap will be very minimal.

…And Benefits

In addition to the basic jQuery functions, CoreQuery will also include some additional plugins designed specifically for working with SproutCore-style views.  One such plugin already available is $().setClass(), which will add or remove CSS class names depending on a flag parameter that you pass.  $().setClass() is more efficient than $().addClass()/$().removeClass() for common View operations.

In addition to some utilities methods like this one, we’re also working on something to make animation easier.  To work well on the iPhone, it is important that any animation library SproutCore supports make use of CSS transitions when available.  We’re looking into solutions for this, but the end result will likely be implemented as a CoreQuery plugin.

All enhancement like these are implemented as jQuery-compatible plugins and automatically installed into jQuery if you choose to use it.

Why CoreQuery?

So why create another DOM library?  Why not just create some kind of emulation layer that can hook to Prototype, jQuery etc?

The simple fact is that the functionality provided by most of these libraries is really not that complex when you get down to it; at least not the parts needed by SproutCore.  We found that simply copying the core pieces of a small, properly namespaced library like jQuery ultimately yielded the best performance and had the lowest code overhead than any other option.

Additionally, I liked the simplicity of making SproutCore standalone.  We’re making it much easier to mix and match our framework with others.   I expect to see a lot more hybrid applications in the future that use SproutCore to manage their business logic and drop in UI widgets from jQuery-UI, Scriptaculous, YUI, Cappuccino or various other toolkits when needed.  No extra code is needed to make this work.  Just mashup and go.

A Bright Future

Though CoreQuery is a “hidden technology” used inside of SproutCore, I’m very excited about what it represents for us.  It’s a good example of how we constantly rethink SproutCore’s insides to make sure we’re always delivering a high quality development experience in the web browser.

UPDATE: What about Sizzle? A few people have asked about Sizzle, the cross-library DOM selector library being written by jeresig.  Sizzle is a great idea, but more relevant to lower-level DOM manipulation libraries like Dojo, Prototype or jQuery (the ones who will adopt it).  If you import these libraries and they use Sizzle, then you can use Sizzle with SproutCore.

For SproutCore itself, Sizzle has too much of one thing and not enough of the other.   It’s DOM selector-API is far richer than what we need, which adds unnecessary weight to SproutCore.  On the other hand, it currently lacks any of the other DOM-manipulation tools found in jQuery, which we would have to rewrite from scratch anyway.

As I understand it, Sizzle is expected to gain some of these added features in the future.  If that happens, it is possible CoreQuery will one day be based on Sizzle source instead of jQuery.  Until then, a reduced form of jQuery seems to give us the features we need for the least amount of code, which was the objective.

Try out CoreQuery in SproutCore 1.0 today. Checkout the Bitburger branch now!

SproutCore Blog: It’s Got What Plants Crave

Evin Grano  and Mike Ball recently launched a new blog covering SproutCore and other tech topics.  They have some great articles on there so far covering KVO, the TextMate bundle and more.  It’s new but worth a look. (And an interesting name)…

Read It’s Got What Plants Crave »

At Last: SproutCore 1.0 Code You Can Play With

The legal hold I mentioned a few days ago just cleared today so we are able to start publishing the SproutCore 1.0 code again.  I have just published our current SproutCore 1.0 code base on the “bitburger” branch at Github:

Check out the source: Framework - Build Tools - Samples

You can also see some detailed notes on what we are working on, what is done, what is left to be done, along with instructions on how to try out the code yourself and even how to make contributions.  Just visit our new Bitburger wiki page!:

About Bitburger

Finally, in case you missed them, I’ve been writing about some of the features coming in SproutCore

Where is the SproutCore 1.0 Code?

Although I explained some of this before, a lot of people have noticed there have been no new commits to the Git repo in the last 6 weeks or so.  Let me explain what’s happening.

Portions of SproutCore are being developed by some big companies.  Although those companies are happy to contribute, their contributions needs to go through a legal review process before they can be made available to the public repository.  Due to the major changes in SproutCore 1.0 and the role these changes play, this means we’ve had to temporarily continue SproutCore development in a private repository until the changes are approved by legal and can be submitted back.

Once those changes are made available to the public, you’ll see it in the github repository and I’ll post about it here.  I will also merge in all those wonderful patches that are pending in Lighthouse right now.

Just to be clear, this block is temporary.  Once all of this code hits the public repository, we should be able to resume more public development, at least for parties external to these big companies.

Until then, I’ll continue to post articles here describing some of these features, to be followed up with a post when the code hits pointing you to the source so you can play with it.  Since there are so many great new changes coming, it will be nice to have a collection of articles highlighting the major points the day the code hits.