Posts in Category "HTML"

Mouse Pointer Lock in HTML: Video and Sample Code

After watching Paul Irish’s talk at SXSW, I got curious about the new pointer lock APIs in Chrome and decided to create a sample application. Pointer lock refers to the ability to lock the mouse pointer in one location and hide it while still getting certain mouse events like mousemove. This is critical for immersive experiences like games since the user wants to be able to use the mouse to navigate, but doesn’t want the pointer to move off the screen or get in the way.

It’s still early days for navigator.webkitPointer, but I was able to get something to work with Chrome Canary. First, a video demo, then some explanation:


If you want to try the demo firsthand (and maybe experiment with the pointer lock APIs yourself), you can find the code here. But first, you need to follow these steps:

  1. Download Chrome Canary.
  2. Enable pointer lock:
    1. Type "chrome://flags" into your location bar.
    2. Scroll down to "Enable Pointer Lock Mac, Windows, Linux, Chrome OS".
    3. Click "Enable".
    4. Click the "Relaunch Now" button at the bottom to relaunch Chrome Canary.
  3. Allow sites to disable the mouse cursor:
    1. Open settings.
    2. Click "Show advanced settings…".
    3. Click "Content settings…"
    4. Scroll down to the "Mouse Cursor" section.
    5. Select "Allow all sites to disable the mouse cursor". In theory, you should be able to select "Ask me when a site tries to disable the mouse cursor (recommended)," however this isn’t working for me right now (remember: still early days).

Currently, you can only lock the mouse pointer in Chrome while in fullscreen mode. Here’s the relevant code:

document.addEventListener('webkitfullscreenchange', onFullscreenChange, false);

function onFullscreen(event) {
    document.body.webkitRequestFullScreen();
}

function onFullscreenChange(event) {
    if (document.webkitIsFullScreen) {
        id('fullscreenButton').style.display = 'none';
        if (navigator.webkitPointer) navigator.webkitPointer.lock(document.body);
    } else {
        if (navigator.webkitPointer) navigator.webkitPointer.unlock();
        id('fullscreenButton').style.display = 'block';
    }
}

Remember that you have to handle mouse coordinates differently when the pointer is locked versus when the pointer is visible. When it’s locked, even though you can’t see the mouse cursor, it’s actually frozen in one location so it can’t be moved out of the window or onto another monitor which means properties on MouseEvent like clientX and clientY are useless. Instead, you can use properties like webkitMovementX and webkitMovementY that tell you how much (and in which direction) the mouse is being moved as opposed to the location of the mouse cursor. Below is the relevant code from the sample application:

var lastMouse;

function onMouseMove(event) {
    var xDelta, yDelta;
    if (navigator.webkitPointer && navigator.webkitPointer.isLocked) {
        xDelta = event.webkitMovementX;
        yDelta = event.webkitMovementY;
    } else {
        if (lastMouse == null) lastMouse = {x:event.clientX, y:event.clientY};
        xDelta = event.clientX - lastMouse.x;
        yDelta = event.clientY - lastMouse.y;
    }
}

The Next Step in Mobile HTML Workflows

If you do a lot of mobile web development, you might want to check out Adobe Shadow. Shadow is a way to easily push web content from your desktop to any number of mobile devices while you’re developing or testing so you don’t have to type in cumbersome URLs and keep refreshing. Additionally, Shadow allows you to debug and inspect web content running in your iOS or Android browser.

Adobe Shadow is now available on Labs. If you want to see it in action, check out the “sneak peek” below:


Also, you can check out an early prototype I built around these concepts after doing enough mobile web development that the workflow was really starting to bug me. In my opinion, the Shadow team really nailed it.

Prototyping in HTML


Over the weekend, I built an e-book reader prototype to demonstrate some ideas that might make the digital book reading experience better. Although I was prototyping a native application, I found the easiest way to build it was with HTML, JavaScript, and CSS. Once CSS regions make it into all mobile browsers, I might put some more time into this concept and build a real browser-based e-book reader (that works exactly how I want it to).

CSS Regions Support in Google Chrome for Android

I’ve been working on some CSS Regions prototypes recently (if you’re new to CSS Regions, check out this post), so when the Chrome for Android beta came out the other day, I decided to see how some of my samples looked on mobile. It turns out, they work perfectly:

The CSS Regions capabilities currently in Chrome are pretty rudimentary, but I’m also working with some nightly WebKit builds which definitely take the feature to the next level (they include CSS Object Model support which enables the scripting of CSS Regions — that’s when they get really interesting). I’ll have plenty more samples and prototypes in the near future.

Setting Text Selection Colors in JavaScript

If you’re building any kind of a text editor in JavaScript, you might want to be able to dynamically set or change the text selection color. I discovered it wasn’t as easy to do as I expected it to be, so I thought I’d share the code. I created this extreme (and admittedly, somewhat obnoxious) example showing the selection color changing as the mouse moves, but the core of the code is something like this:

var ss = document.styleSheets[0];
ss.insertRule('#content::selection {color: #'+
              newForegroundColor+'; background: #'+
              newBackgroundColor+';}', 0);

Note that I only tested this code in Chrome and Safari (I’m only targeting WebKit browsers for now), however it can work with Firefox with the correct "moz" prefixed style name. I haven’t yet tested or investigated IE.

If you know another way of changing text selection properties in JavaScript, let me know.

How to Create a Custom File Input (For Use With the HTML5 File APIs)

I’m working on an HTML/JS application that lets users work with local files directly in the browser, and I’m using some new HTML5 APIs to access local files. It works great (in Chrome and Firefox, anyway — see note below), however my UI calls for a custom file input rather than the default (and usually pretty ugly) button-and-path input. Fortunately, customization is easy in this case. The trick is to create your own UI treatment (in my case, just a link), then use the click() function on a hidden file input to bring up the file dialog.

In Firefox, you can use the display:none style as noted in this Mozilla Developer Network documentation, however this won’t work in Chrome or Safari (although FileReader is currently not supported in Safari, you might as well think ahead for when it is). A better way of doing it, therefore, is to use visibility:hidden.

The only problem is that when something is hidden using its visibility property, it’s still actually in the DOM, and space is therefore allocated for it even though you can’t see it. If you want to get your file input completely out of the way, therefore, you can use something like this:

<input type="file" id="fileInput" onchange="handleFiles(this.files)" style="visibility:hidden;position:absolute;top:-50;left:-50"/>

Your file input will still be in the DOM (even though it’s hidden and off-screen), however it won’t take up any visual space.

Here’s the full HTML code:

<a href="javascript:onLoad();">Load a File!</a>
<input type="file" id="fileInput" onchange="handleFiles(this.files)" style="visibility:hidden;position:absolute;top:-50;left:-50"/>

And here’s the JavaScript code:

function onLoad() {
    id('fileInput').click();
}

function handleFiles(files) {
    var file = files[0];
    var reader = new FileReader();
    reader.onload = onFileReadComplete;
    reader.readAsText(file);
}
  
function onFileReadComplete(event) { 
  // Do something fun with your file contents.
}

Note that this code is only going to work in current versions of Chrome and Firefox, but is expected to work in future versions of IE (10) and Safari (6).

How to Download Data as a File From JavaScript

I’m currently working on an HTML/JavaScript application that allows you to author content entirely on the client. I want to let users download that content and save it locally, but without bouncing it off a server. After some trial and error, I have it working fairly well using a data URI. Rather than explain it, it’s probably easiest just to show the code:

HTML:

<a href="javascript:onDownload();">Download</a>

JavaScript code:

function onDownload() {
    document.location = 'data:Application/octet-stream,' +
                         encodeURIComponent(dataToDownload);
}

The only limitation is that I can’t figure out a way to give the downloaded file a name (and have concluded that it’s not currently possible, though I’m happy to be proven wrong). I’ve only tested the code in Safari and Chrome, and in both cases, the file name defaults to "download" (with no extension). All the data is in the file, but it’s not a very intuitive experience for the end user.

I’ll be releasing the application shortly which should demonstrate why downloading data directly from the client can be useful. In the meantime, I’m curious if this is something any of you might use, and if so, if you think the file name issue should be fixed.

Let me know in the comments.

A Summary of the WebKit Developer Tools

I use the WebKit developer tools extensively in both Chrome and Safari, but it occurred to me the other day that I was probably only using a fraction of their capabilities. After researching them more fully, I was pleasantly surprised by how comprehensive they are, so I decided to make a quick list of all the major developer-oriented features of WebKit that I know of. Even if you use the Chrome/WebKit dev tools regularly, there’s a good chance you’ll find one or two things below you haven’t been leveraging.

Continue reading…

Creating a Loading Spinner Animation in CSS and JavaScript

Update (12/20/2011): Now works in Firefox as well as Chrome and Safari.

Since I’m not a very good designer, I usually try to do as much styling, design, and graphics in code as I can. For instance, when I wrote this mobile compass application in HTML, I did all the graphics programmatically using Canvas and CSS.

I’m now working on a project that requires one of those loading spinner animations that Apple seems to have made famous, so naturally, I started looking into ways to create one purely in code and/or CSS (no external assets). Fortunately, I found a great post on the Signal vs. Noise blog demonstrating exactly what I wanted to do. However, I decided to take it one step further, and write some JavaScript to generate both the required CSS and the HTML. The advantage of generating everything dynamically is that you can configure things like the size, color, and the number of bars in the animation at runtime.

Here’s an example of some randomly generated loading spinner animations which are 100% CSS and HTML generated by JavaScript (Chrome and Safari only for now). If you’re interested in the code, here’s the original CSS from Signal vs. Noise, and below is my port to JavaScript (view the source of the demo to see how to use it). It only works in WebKit-based browsers for the time being, but I’ll update it as browser capabilities get better. It works in WebKit-based browser, and in Firefox.

Check out the example.

var Spinner = {
  SPINNER_ID: '_spinner',
  prefix: (navigator.userAgent.indexOf('WebKit') != -1) ? 'webkit' : 'moz',
  getSpinner: function(size, numberOfBars, color) {
    if (document.getElementById(this.SPINNER_ID) == null) {
      var style = document.createElement('style');
      style.setAttribute('id', this.SPINNER_ID);
      style.innerHTML = '@-'+this.prefix+'-keyframes fade {from {opacity: 1;} to {opacity: 0.25;}}';
      document.getElementsByTagName('head')[0].appendChild(style);
    }
    var spinner = document.createElement('div');
    spinner.style.width = size;
    spinner.style.height = size;
    spinner.style.position = 'relative';
    var rotation = 0;
    var rotateBy = 360 / numberOfBars;
    var animationDelay = 0;
    var frameRate = 1 / numberOfBars;
    for (var i = 0; i < numberOfBars; ++i) {
      var bar = document.createElement('div');
      spinner.appendChild(bar);
      bar.style.width = '12%';
      bar.style.height = '26%';
      bar.style.background = color;
      bar.style.position = 'absolute';
      bar.style.left = '44.5%';
      bar.style.top = '37%';
      bar.style.opacity = '1';
      bar.style.setProperty('-'+this.prefix+'-border-radius', '50px', null);
      bar.style.setProperty('-'+this.prefix+'-box-shadow', '0 0 3px rgba(0,0,0,0.2)', null);
      bar.style.setProperty('-'+this.prefix+'-animation', 'fade 1s linear infinite', null);
      bar.style.setProperty('-'+this.prefix+'-transform', 'rotate('+rotation+'deg) translate(0, -142%)', null);
      bar.style.setProperty('-'+this.prefix+'-animation-delay', animationDelay + 's', null);
      rotation += rotateBy;
      animationDelay -= frameRate;
    }
    return spinner;
  }
}

Loading Data Across Domains with JavaScript

I’m working on a project now that makes heavy use of XHR (XMLHttpRequest) to load data from a server. I’m writing the HTML/JS portion of the application on my local machine, but my server development environment is on a remote server under a different domain. Typically cross-domain XHR request aren’t allowed due to browser security restrictions, however there are two easy work-arounds:

  1. JSONP
  2. Cross-Origin Resource Sharing

JSONP

JSONP gets around the same origin policy by loading JavaScript from another domain into a dynamically generated <script> tag (script tags are not subject to the same origin policy which is what enables things like ads to be served from different domains). In your request (formed as the src attribute of the script tag), you typically specify a callback function that you have already defined in your application. The JavaScript that gets loaded into the dynamically generated script tag will call the specified function, passing in the requested JSON data.

(Note that JSONP requests are probably best handled by robust frameworks like the jQuery.getJSON() function.)

I’ve used JSONP in the past for things like this prototype client-side news reader, but the project I’m working on now uses XML as well as JSON, so I decided to use a different approach.

Cross-Origin Resource Sharing

Cross-origin resource sharing is similar to cross-domain policy files in the Flash word, but they are done through HTTP headers. The specification defines several different headers, however the only one I’ve needed so far is the response header Access-Control-Allow-Origin. Allowing my server to share resources with my local machine was as easy as adding the following line of PHP code before writing to the output buffer:

header('Access-Control-Allow-Origin: http://seeker.home');

(Seeker is the name of my development machine, named after the excellent Jack McDevitt novel.)

Of course, once I move the client-side portion of my application (the HTML, JS, and CSS files) to my server, I can delete or comment this line out since the application will be served from the same domain from which it needs to request data. In the meantime, however, it’s made local development much easier.

Cross-origin resource sharing is part of the XMLHttpRequest Level 2 specification, and is supported in all modern browsers.