Tuesday, February 21, 2012

death of the mouse: film at 11

"It's Time to Exterminate the Mouse" reads Mat Honan's Gizmodo headline, the lead feature for the site. And at the risk of sounding like a cranky old man, I think he has an extremely half-assed understanding of how people and computers interact. His whole tone is kinda provocative and juvenile, to the extent that the whole article feels like a troll. After a few potshots complaining about how 8-bit this hardware feels and how "anyone pushing them with new computers is selling crap" (NB: this point is stronger than it would be otherwise because he's tangentially talking about desktop systems, which are even more on the way out than mice..) he rhetorically asks:
When was the last time you really loved a mouse? When was the last time you were like, "damn, this is a great mouse and I enjoy using it?"
That's an easy one-- the last time I tried to do coding work on my laptop for a few hours. Going back to the mouse after a day on the touchpad is a blessed relief... stuck with just the touchpad for a day, an almost tangible pressure of annoyance builds and builds.


He then gives examples of a gesture I agree with the utility of: pinch to zoom is pretty decent and intutive. And I think inertial scrolling is probably a bit better than the scrollwheel, and definitely (usually) better than manipulating a page pointer by finger or mousepoint.


Then he says
Today at any given time we may be running 20 apps at once, with a dozen or more browser windows open, while trying to sort through more data in a few seconds than an early Cray supercomputer saw over the course of its lifetime. And yet we still use tools designed for a simpler time, with simpler needs.
Which is true, but how is that an argument for gestures etc over the mouse? The iPad and the iPhone are lovely because they do ONE thing at once. In fact, much of the physicality comes from the way your glass slab has temporarily been turned a unitasking device for that activity. You need a lot of real estate to pinch and slide and poke. 


It goes on...
Gesture-based computing gives us far more precision and control over the interface. We can manipulate not just points but entire screens. We can perform complex actions that once required keyboard shortcuts, with just our fingertips.
Precision? Really? And keyboard shortcuts are nerdy but they are full of precision and control. Plus, you can see easily written descriptions of what keyboard shortcut goes with what command...


He then points to the Kinect as REALLY ahead of the curve with this stuff. Admittedly I was an early adopter, but using the Kinect to navigate the most simple menus is a nightmare. Each game invents its own paradigm for "select this option" (some use a swipe, some use a hold, etc) and for "back" and "pause". With the right game, using your body as the controller is fun as heck, but that's because you have a great mapping to a physical activity. (Ironically, the one place he admits to the superiority of the mouse is for FPS games. I was going to argue that the other way games have better interfaces is with the two stick controller and all those buttons, but then again each game has its own buttons to learn, and it only really works well because of the physicality of it.)


Other advantages of the mouse: has this guy ever heard of "gorilla arm"? That's what your arm feels like after a few hours of poke, poke, poke at the screen. For people who work long hours in front of a computer, you need something that's not going to tire you out. (Not to mention all the greasy fingerprints...)


Similarly, Mouse use on screen pointers. These pointers are nice because they keep state on the screen even when you put your hand down for typing. This allows bimodal systems that react to your projected interest (for example, hover states on buttons as you mouse around.) in a different way from your project intent (some form of clicking). Very few gesture systems carry that, and with the rise of touch devices, the hover state might be on its way out. 


Also gestures tend to have poor discoverability. There are only a few universal moves (the zoom pinch, the swipe, the slide) and everything else varies greatly-- in fact, what a swipe "means" will vary from app to app. (Maybe if there was a universal "help I need the cheat sheet!" gesture?) So you get to a new app (or better yet to an old one you haven't used in a while) the answer to "how do I use this" might not be so clear.


Many gestures are residents of the universe of "moves the user can make unintentionally". Sometimes I mouse and shake a window on my Windows 7 machine, and suddenly all the other windows are minimized, including my chat windows and virtual post-its and things I like to keep around. And there's no real "Undo" for that, so I have to go and finds the things I wanted open. It's a clear violation of the Dao of UX, which is "a computer should always do that which surprises the user least."


So I might be sounding like that old "get off my lawn!" geezer. I might be underestimating the ability for gestures to standardize (they already are, like the "two finger scroll" on Macs) as well as having an exaggerated sense of the limitations of  touchpads, since I often use these nasty tiny PC things that lack both the 2-finger-scrolling and the generous dimensions of Apple's Magic Touchpad... Still, I think this article is a overly provocative and insufficiently thought out or balanced. On the other hand, it lets me (and the majority of the Gizmodo commenters by the look of it) feel as if they're rather smarter than the author.

Monday, February 20, 2012

inputWrapper: default text and masks

I'll start with my usual disclaimer: this blog is meant to be notes to my future self along with stuff other developers might find useful, with code snippets that suffer from a heap of my inexperience and the usual "Get It Done!"/"Worse is Better" rush of my employer, and so isn't always representative of js and jquery best practices. (That said, the stuff we come up with is well-QA'd and I always aim to make robust and readable code, so there is that.)

A common UI trick is to have a text entry field that has a prompt in the body of the input itself that goes away when the box is clicked on, and returns if the box is left without text. (A UX note is in many cases you don't want the box to be otherwise unlabled (no matter how slick your designer is trying to make the form) since that label goes away when text is entered, and depending on the context, the user might not know what the data he or she entered means.)

A while back we experimented with different techniques for making this effect, and came up with the following. It's a function that you call as follows:
makeInputDefault(".selectorForInputField",
"Prompt To Put There","some_unique_identifier");


It's a little wonky... possibly it should be wrapped in as a proper chainable jquery function, and having the developer pick a unique identifier is a bit odd.

What the code does is make a sibling span that actually covers the input box. The js for that is

function makeInputDefault(sel, def, classForSpan){
$(sel).blur(function(){inputDefault_inputBlur(this);});
$(sel).focus(function(){inputDefault_inputFocus(this);});
wrapperIdCounter = wrapperIdCounter + 1;

var inputWrapperID = "inputWrapperId_" + wrapperIdCounter;
$(sel).wrap("<div class='inputWrapper' id='" + inputWrapperID + "'>");
$(sel).parent("div").append('<span class="unpicked" onclick="inputDefault_spanClick(this);" style="display: inline;">'+def+'</span>');

if(classForSpan != undefined){
$(sel).siblings(".unpicked").addClass(classForSpan);
}

if($(sel).val() != ""){
   $(sel).siblings(".unpicked").hide();
}
setTimeout(function() { inputDefault_inputLoad("#" + inputWrapperID) }, 500);
}




function inputDefault_spanClick(obj) {
ccdebug('spanClick');
var currentSpan = $(obj);
var currentInput = currentSpan.parent().find("input");
currentInput.focus();
}
function inputDefault_inputFocus(obj) {
ccdebug('inputFocus');
var currentInput = $(obj);
var currentSpan = currentInput.parent().find("span");
currentSpan.fadeOut(250);
}
function inputDefault_inputBlur(sel) {
ccdebug('inputBlur');
var currentInput = $(sel);
var currentSpan = currentInput.parent().find("span");
if(currentInput.val() == "") {
currentSpan.fadeIn(250);
}

}
//this next function makes sure we initialize to the right state,
//in terms of showing the overlay or not...
function inputDefault_inputLoad(obj) {
var currentWrapper = $(obj);
var currentInput = currentWrapper.find("input");
var currentSpan = currentWrapper.find("span.unpicked");
if(currentInput.val() == "") {
currentSpan.show();
} else {
currentSpan.hide();
}
}
(I think the timeout is a hack to cope with browsers auto-filling the field after the sibling tag had been rendered...)

This goes with a jot of CSS:

.inputWrapper {
    padding: 2px;
    position: relative;
}
.inputWrapper input, select {
    border: 1px solid #D7D7D7;
    color: #666666;
    font-size: 16px;
    width: 150px;
}

.inputWrapper span{
position: absolute;
top:3px;
left:6px;
display:none;
}

.inputWrapper .picked {
color:#666666;
}

.inputWrapper .unpicked {
color:#D7D7D7;
}

So that seems to work ok. But why is it so complex? this dailycoding article outlines a method that is much more straightforward, and keeps the default text with the HTML. Well, I found two reasons... one is we are also using this masked Input plugin to autofill the field with hyphens, and it kind of short circuits the simpler prompt method. Another is (and this is a guess) that the dailycoding technique means you have to check if the current input value is equal to the prompt, and ignore it accordingly, when the form data is actually used.

I guess my overall suggestion is to use the dailycoding technique first (nice site btw, I just subscribed to its feed) and then use something like this span technique if the straightforward way isn't up to par.

Thursday, February 16, 2012

a bit of wisdom to my future self

Future self, when having to resize elements based on some other elements' animation of size or position, you will get better results if you put a callback at the end of animate() rather than trying to read the size or position right there and then.

Cheers!

Monday, February 13, 2012

basalmiq's big blue arrow

At my company, our product owners like to sketch things out in basalmiq, layout-mockup software that makes it easy to give a rough outline of what the various product screens should look like. What's nice about basalmiq is that everything has a clean but crude look, as if someone with a very clean style was sketching it on a whiteboard:
The deliberate crudeness lets people reviewing see the bigger picture, and not got tripped up on commenting on small details of the design. (As is the reviewer's wont, I know from personal experience.)

The program's "presentation mode" has a beautiful UX detail I simply adore. It features an oversized mouse pointer, so that people in the back of the room can see what you're pointing to. But MORE than that, they have the pointer rotate so that it's always directed at the center. It took me a second to realize that was the simple algorithm they were using, because in practice it does a terrific job of subtly grabbing the eye and really seeing what's being pointed at.

I used processing.js to mockup a replica of it in action, full of this that and the other... (my apologies, and sympathies, to users of older versions of IE)

It's such a great idea-- really thinking outside the box. Or rather, noticing that there was a box to think outside of. UX detail at its finest.

Tuesday, February 7, 2012

basic diy pubsub


So to solve a problem at work I wrote a basic pubsub, a publish/subscribe model so parts of our page can notify other parts of events they care about... it's a powerful design pattern for decoupling various components on a page. There are other implementations we coulda used, but it's pretty fun and easy to roll our own, I think the resulting code is small and robust:
var localDataBreaker = new function(){
this.callbacks = {};
this.subscribe = function(event,id,funk){
if(this.callbacks[event] == undefined){
this.callbacks[event] = {};
}
this.callbacks[event][id] = funk;

}
this.unsubscribe = function(event,id){
if(this.callbacks[event] != undefined){
delete(this.callbacks[event][id]);
}
}


this.publish = function(event,args){
if(this.callbacks[event] != undefined){
for(key in this.callbacks[event]){
var funk = this.callbacks[event][key];
funk(args);
}
}
}

}

(I like the idea of functions subscribing with a string key for later unsubscriptions, that seemed safer than assuming there will be a reference to the same function handy when a function wants to stop listening.)

So here's a basic test of it... we subscribe to event "foo" with an anonymous function that we key with "bar", we practice firing "foo", then we also subscribe with an event tagged "baz", we see both fire when the event occurs, then we remove "bar" and see just "baz" fire when we fire the event a third time.

localDataBreaker.subscribe("foo","bar",function(o){alert('bar:'+o);});
localDataBreaker.publish("foo",'event  1 after just bar ');
localDataBreaker.subscribe("foo","baz",function(o){alert('baz:'+o);});
localDataBreaker.publish("foo",'event 2 after bar and baz ');
localDataBreaker.unsubscribe("foo",'bar');
localDataBreaker.publish("foo",'event 3 after bar removed ');



Thursday, February 2, 2012

onerror and busted images

My company's site alleyoop.com just had its big public launch! Hooray for us!

So we have a lot of Math "subtopics", each with its own representative image. Those image files live in the file system, but the subtopic names live in the database (with the front end getting the list of available subtopics from various endpoints.)

We use the subtopic name as the file name, which is of course asking for trouble a little bit, but in practice it works ok, and it's a lot easier for our design people to work with meaningful long file names than having to look up unique IDs.

To make sure all subtopics had correctly named art, I made a QA tool that polls the endpoints and slaps up the images. I found an easy way to get generate a list of the broken images, so that we don't have to carefully inspect the whole very long page by hand... I build the HTML with something like


buf += "<pre>"+showname+"</pre>"
+"<img src='"+path+name+".png' onerror='doerror(this)' >\n";

I know some people prefer to build objects with templates (though I'm sad that my favorite templating for jQuery isn't gonna make it outta beta) or with DOM objects... (which annoy me a little, because now I have two ways of making up parts of a page, by writing HTML, or the "language of DOM elements". I'd rather speak one language when possible!) but this is more than adequate for most purposes, especially quick and dirty QA purposes...

So that "onerror" nicely gets called when the image is indeed busted, and so I have a function doerror that takes the object and does this:

function doerror(o){
var parts = o.src.split("/");
var name = parts[parts.length-1];
name = name.substring(0,name.length-4);
name = replaceAll(name, "%20"," ");
$("#error").append(name+"<br>");
}
(I should probably have it replace escaped ^s not just spaces...)

So that works well, and is a powerful way of quickly detecting broken image references before they make it into production.

BONUS TIP:
Our work laptops all have encrypted harddrives, so sometimes when we transfer files straight to PC from Mac via IM, the files all show up with green filenames, and Apache Tomcat can't actually display the files! In that case I have to select all the files, right click to Properties, click "Advanced..." and then uncheck "Encrypt conctents to secure data".