Follow-up on navigator.languages

navigator.languages recently made its way to the stable versions of browsers: Firefox 32, Chrome 37 and Opera 24!

Getting all the user languages

As explained in an earlier post, navigator.languages gives us the complete list of languages as set by the user in her browser.

navigator.languages is an array of language codes sorted by priority (the lower the index, the higher the priority).

You can do cool things like:

var langNum = new Set(navigator.languages.map(function(code) {
  return code.split('-').shift();
})).size;
console.log('It looks like you can read %i %s.', 
  langNum, langNum > 1 ? 'different languages' : 'language');

(Set is used here to dedupe the array.)

Use this to show the best matching language by comparing navigator.languages to the ones supported by your app. But of course, also offer an option to change the language.

languagechange event

In addition to navigator.languages, browsers can now listen to the languagechange event fired by the window object to update the UI language accordingly:

window.addEventListener('languagechange', function() {
  console.log('You updated your browser languages to "%s"', navigator.languages.join(', '));
});

I set up this quick demo that lists the languages set in your browser and stays up to date.

This powerful event means web apps don't require to be reloaded to update their UI language and can integrate perfectly in the browser/OS.

Polyfill

Polyfilling navigator.languages couldn't get any easier:

if (!navigator.languages) {
  navigator.languages = [navigator.language];
}

Setting the language independently to the OS

As a side note on this topic, on most mobile OSes (and some others like MacOS), the language of the browser is tied to that of the OS. Since last year, it's now possible to change the language of Firefox for Android independently (and without restarting the browser!). Why is it important? See this blog post for more details.

Comments

Software gamepad

I recently resumed my work on jsSMS a JavaScript recompiler for Sega Master System and Game Gear. Among the new features, I wanted to improve the software gamepad for touch screens.

Naive implementation

When I started adding the software gamepad, I did something quick and dirty. I didn't really have time to waste developing a nice looking controller in canvas (though that would have been easier). So I just threw some <div> tags, styled them and attached a couple of event listeners.

To have a gamepad compatible with touch screens, I naturally listened to touch event on each key (up, down, left, right, fire1, fire2 and start). touchstart and touchmove communicate the active key to the emulator and touchend release it.

The main issue I found was that if the user moves her finger, the JavaScript will recognise the activation of a particular key, but will release a different one. In other word, the key initially pressed will never be released.

Clearly, this was unusable.

Better implementation

I looked for tutorials and references about how to build a gamepad using HTML5, but it looks like most HTML5 games use canvas and detect the currently touched key using the location of the touch.

The location of the finger relative to the screen is stored in clientX and clientY properties of the touch event. If you use jQuery, like myself, make sure to reference the original event using originalEvent property (i.e. evt.originalEvent.clientX).

To get the position of the currently touch key in HTML, I could have computed the position and size of each buttons, then loop through them to find the element touched. In my case, that wasn't possible because I use a complex layout made of rounded and rotated elements and not just square buttons.

Thanks to StackOverflow, the answer was actually quite easy and requires a single DOM method: document.elementFromPoint.

To get the currently touched element just pass the touch event coordinates to document.elementFromPoint:

var touchedElement = document.elementFromPoint(evt.clientX, evt.clientY);

Then, you need to implement a mechanism to map back the DOM element touched to the emulated key (I personally use CSS class name and a map object).

Also, you'll want to iterate over the changedTouches array to properly activate all the buttons pressed on multi-touch devices.

To put it in a nutshell:

function onTouch(evt) {
  emulator.releaseAllKeys();
  evt.changedTouches.forEach(function(touch) {
    var target = document.elementFromPoint(touch.clientX, touch.clientY);
    var key = getKeyFromElement(target);
    emulator.pressKey(key);
  });

  evt.preventDefault();
}

function onTouchEnd(evt) {
  emulator.releaseAllKeys();
}

padElement.addEventListener('touchstart', onTouch);
padElement.addEventListener('touchmove', onTouch);
padElement.addEventListener('touchend', onTouchEnd);

Obviously, you'll want to code your own getKeyFromElement and emulator communicating functions.

How optimised?

The result is a nicely working touch optimised controller in HTML5. The finger can swipe from one key to another and the emulator reacts responsively.

I don't think you could do simpler and according to my coworker, the brilliant Chris Lord, if your DOM tree is simple you shouldn't get any performance penalty from using document.elementFromPoint.

Happy gaming!

Comments