Traces Of

Code, Design and Random Thoughts

Übersicht 0.2

Comments

A new version of Übersicht has been released today, which brings a much requested feature: screen control. For those of you with multiple monitors, you can now select which monitor Übersicht runs on.

It should also remember which monitor(s) you used last and use it again whenever it is available. Note that it doesn’t remember the exact monitor, but rather which port the monitor was connected to. So if you connect a different monitor to the same port, it should use that one.

The screen-shot to the left also shows another new feature: manual refresh. You can now force your widgets to refresh manually, which is handy when you just woke your machine from sleep and don’t want to wait for your widgets to update.

Finally, there also have been improvements to error reporting to aid you in writing your own widgets. CoffeeSCript and Stylus compile errors were previously only logged to the system log, but now they will appear in the developer console as well.

This is the first release that has seen some community involvement and I’d like to thank every one who helped out! Special thanks go to Guillaume Boudreau and Siemen Sikkema.

Übersicht

Comments

Earlier today I released Übersicht, a little Mac app I originally wrote to scratch my own itch. It is essentially like GeekTool, which lets you run system commands and display their output on your desktop. The main difference that widgets are written in HTML5, which was originally motivated by these two main reasons:

  • I can plug in a different monitor without my widgets breaking
  • I can easily write my own widgets

So in essence the app is just a large WebView that is glued to your desktop and widgets are little snippets of HTML+CSS+JS. Of course you can’t run system commands from within a WebView, so the app comes with a NodeJs backend. For more details on that, you can checkout out the slides for my talk at the local Amsterdam JavaScript Meetup.

Of course, also checkout the app itself. It is released under the GPL license and the code can be found on GitHub!

Text Entry Woes for Mobile Web Apps

Comments

Sometimes building web apps feels like trying to build a house of cards while the browser might pull away the carpet from underneath you at any moment. Text entry on mobile is one of these issues were you, as a developer, have very little control over what you want to achieve versus what the browser thinks it is best for you.

Mobile browsers have a reason for this of course. When the mobile web came about, websites and web apps where not designed with a small screen and a software keyboard in mind. In order to still provide a decent experience when entering text, mobile browsers had to come up with some solutions. For Mobile Safari that solution was to overlay the soft keyboard on top of the page and scroll the page so that the focused text field is in center of the visible space above the keyboard.

dont push me, ‘cause …

While this works great on normal websites, in your carefully crafted web app you might run into something like this:

This might not seem like a big deal at the first glance, but when trying to create a seamless, near-native user experience, this can ruin your day. Submit buttons and other form elements that were carefully placed to be visible and easily reachable when entering text are now scrolled halfway off the screen. In combination with the unexpected movement it really makes the app feel broken.

window.scrollTo to the rescue

So what can we do about this. There is, to my knowledge, no clean work around. A lot of default behaviour of the browser can be tweaked using css, some kind of event.preventDefault() or by other means, but this isn’t one of them. What we can do, however, is to reset the scroll offset using javascript (or coffeescript in this case), so something like this:

1
$(textareaEl).on('focus', function () { window.scrollTo(0, 0) });

This is assuming that your web app takes the whole screen space and hides the address bar. However, it doesn’t quite work; the browser scrolls just slightly after the focus event fires, so the page scrolls anyway. Using a setTimeout works, but you rely on a magic number, plus it is hard to catch it exactly when it scrolls, so that there is a visible jump happening before the text field moves back in its correct position.

hard to catch

It turns out, that if you focus the text field programatically, there is no delay before the page scrolls, so the onfocus handler catches it just in time.

So the final solution boils down to:

  • stop the browser from focusing the textfield when the user taps it.
  • focus the text field programatically.
  • scroll window back to top when the focus event fires.

I need to emphasise that this is only a valid solution if your are dealing with a full-on web app, which is sized to fit the screen and is not user scalable. Double tap zoom, or pinch zoom will break with this approach (just on the text field, not the entire site)!

bolting the carpet to the floor

In order to stop the browser from focusing the text field we need to preventDefault on touchstart (touchend should work as well). This means we have to provide our own custom tap handler, since the default click behaviour won’t get triggered. The final solution could look something like this:

1
2
3
4
5
// onClick is our custom click/tap handler
onClick(myTextarea, function () { $(myTextarea).focus() });

// scroll back to top
$(myTextarea).on('focus', function () { window.scrollTo(0, 0) })

The custom click handler could look something like this:

onClick
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var onClick = function(domEl, clickHandler) {
    domEl = $(domEl); // make sure we have a query object

    var startTime, startPos, canceled;

    domEl.on('touchstart', function (event) {
         // this prevents the browser from firing a click event and
         // focusing the text field
        event.preventDefault();

        canceled  = false;
        startTime = event.timeStamp;
        startPos  = { x: event.pageX, y: event.pageY };
    });

    domEl.on('touchmove', function (event) {
        var dx = event.pageX - startPos.x,
            dy = event.pageY - startPos.y;

        // cancel click if finger moved more than 40px
        var distance = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
        canceled     = canceled || distance > 40;
    });

    domEl.on('touchend', function (event) {
        if(canceled) return;

        // don't trigger click if finger rested longer than 500ms
        if(event.timeStamp - startTime < 500)
            clickHandler();
    })
};

And there you have it; the text field stays in place, solid as a rock. We’ve bolted the carpet to the floor and can now happily continue to build our house of cards.

Mind you, this solution is for iOS only, so on Android or other mobile platforms/browsers it won’t work and even break things. There are other solutions for these platforms, which I won’t go into further detail here.

Finally, do remember that you are breaking default browser behaviour, which exists for a reason. So know what you are doing or else you might significantly reduce the usability of your app! Having a textfield stay hidden behind the soft keyboard, or preventing other necessary user interaction is hardly ever very user friendly ;).

New Blog

Comments

Here is to a fresh start!

Since posterous shut down I had to migrate my not-so-active blog, but wasn’t successful with importing all my old posts so far. Many of them are probably not worth the trouble. I do hope to move over the more interesting posts some time soon, though!

Stay tuned!