Archive for the ‘Client Side’ Category

Why you shouldn’t return false in MooTools event handlers

Saturday, July 25th, 2009 by Sebastian Markbåge

Let’s say I have a link (anchor tag with href), and I wish to attach an event listener to it.

<ul>
  <li><a id="mylink" href="http://...">my link</a></li>
</ul>
document.id('mylink').addEvent('click', function(){
  console.log('hello world');
});

Now, if I click the link it will log the message but the browser window will also visit the location of the link. There are a bunch of such default behaviors to pretty much every event in the DOM. If we’re implementing custom behavior, we typically want to prevent this default behavior. A common practise is to have the method return false as such:

document.id('mylink').addEvent('click', function(){
  console.log('hello world');
  return false;
});

THIS IS BAD! Don’t. To understand the reason for this, you need to understand event bubbling and the difference between preventDefault and stopPropagation.

Event bubbling and stopPropagation

When an event is dispatched, it first fires the listeners of the ‘mylink’ element (not quite true, but we don’t use capture). But then it propagates (bubbles) up to the LI-element, UL-element, BODY-element etc.  So for every click on any element, the ‘click’ event is triggered on the BODY-element. After all of that, the default behavior of the browser is triggered.

In most browsers bubbling continues to the document and window objects, but that’s not always true for IE.

This is a powerful model. It allows us to do things like Event delegation. You can place a listener on the UL-element to catch any events triggered on the LI-elements without adding listeners to all the existing or any new LI-elements.

Sometimes we don’t want bubbling to occur. Let’s say for example that I wanted to have a ‘click’ event handler on the UL-element that handles clicks on the UL area outside of any A-element. Then I could accept the Event object as the first parameter, use stopPropagation during the click event on the A-element to stop the event before it reaches the UL.

document.getElements('ul').addEvent('click', function(){
  console.log('You clicked within the UL but outside of any link.');
});
 
document.getElements('a').addEvent('click', function(event){
  console.log('You clicked a link.');
  event.stopPropagation();
});

preventDefault

In my example above the browser would still visit the href of the link. Stopping propagation (bubbling) doesn’t actually prevent the default browser action. So we also need to call preventDefault during the click event to prevent the default operation of clicking a link.

document.getElements('a').addEvent('click', function(event){
  console.log('You clicked a link.');
  event.stopPropagation();
  event.preventDefault();
});

Now since this is fairly common MooTools has a shortcut for doing both stopPropagation AND preventDefault. Namely the stop() method:

document.getElements('a').addEvent('click', function(event){
  console.log('You clicked a link.');
  event.stop();
});

So, why is return false bad?

In the standard browser DOM model it’s equivalent to calling event.preventDefault(); but in MooTools it’s equivalent to calling event.stop(); i.e. it also calls stopPropagation.

This is a problem. If you use this model routinely you may not notice that you actually prevent plugins attached to elements higher up in the bubbling chain.

Let’s say I want to use the ‘mouseleave’ event to hide the UL-element when the mouse leaves. If I also return false on the ‘mouseout’ event on the A-element, I may not get the ‘mouseleave’ event because the A-element stops it. OR maybe I have a plugin higher up that requires that my events bubble. It’ll be even more prevalent as more plugins makes use of Event delegation.

Therefore you need to be very explicit about when you stop propagation and not.

Second of all, the “return false” API doesn’t make sense. The function isn’t failing. It isn’t canceled. In fact, it’s canceling a DIFFERENT function.

Therefore you should ALWAYS be explicit by calling either event.preventDefault(), event.stopPropagation() or event.stop(); instead of relying on an implicit convention that differs between frameworks.

Returning a false value is a relic from the old days when we only had a single listener per event.

Binding Parameters

Sometimes you need to bind parameters that you wish to pass to an event listener. A common practise is to use bind.

document.getElements('a').addEvent('click', function(paramA, paramB){
  // do something with this, paramA and paramB
  return false;
}.bind(someObj, [objA, objB]));

In this case you can’t accept an Event object since you’ve bound your parameters to other objects. In this case you can use bindWithEvent to let the first parameter (the event object) get through, while binding the remaining parameters.

document.getElements('a').addEvent('click', function(event, paramA, paramB){
  // do something with this, paramA and paramB
  event.stop();
}.bindWithEvent(someObj, [objA, objB]));

$lambda(false)

“But I don’t want to type out all of that just to stop an event. I like $lambda(false) to easily block events.”

People sometimes use the $lambda method to create a function that returns false to easily stop an event without doing anything else: el.addEvent(‘click’, $lambda(false));

So you need a method that does nothing other than accepts an Event object and calls preventDefault, stopPropagation or stop? Thanks to MooTools generics you can easily do that like this:

element.addEvent('click', Event.preventDefault); // OR...
element.addEvent('click', Event.stopPropagation); // OR...
element.addEvent('click', Event.stop);

For you that think “return false;” saves bandwidth… “e,” and “e.stop();” is two bytes shorter.

Additional Event Listeners on the same Element

Neither preventDefault or stopPropagation or even an error prevents any additional handlers/listeners on the same element. So if you have two handlers listening to the same event, then both will be triggered regardless of the result of either function.

That should be true for all Events, even Class events. More on that in MooTools 2.0…

Parsing Base64 Encoded Binary PNG Images in JavaScript

Wednesday, May 20th, 2009 by Sebastian Markbåge

The other day David Walsh was experimenting with rendering images in the browser using regular tags as pixels. Valerio picked up the idea and made some enhancements. A server-side script transformed PNG files into a JSON image format for easy parsing on the client. That raised the question… How difficult would it be to do that parsing on the client instead?

Why PNG? Well, other than becoming the new defacto standard for graphics it’s a very simple format. It’s also free of patents and uses only simple well known techniques. It makes it very easy to work with. This post is about parsing raw PNG image data in pure JavaScript. It has nothing to do with built in browser support for the format.

Base64 Encoding

JavaScript doesn’t allow us to work with binary data directly. Even with XHR we can’t work with the raw binary data because JavaScript doesn’t currently have a concept of raw bytes. Instead we have to get the bytes from a character representation of the data.

Luckily there’s already a standard transfer encoding already heavily in use in various places of the W3C standards… Base64! You can use the data: URI scheme to embed image data in your HTML or CSS documents. It’s also heavily used for binary data in e-mails.

We can get the data either from an XHR request, from a src attribute or just statically embedded in your JavaScript file. So, now we have our data as Base64 encoded string.

To work with the raw data we need a way to represent bytes. If you’re working with ASCII data you can just stick to string representations. But since we’re going to be working binary data the most useful way seems to be simple Numbers. That allows you to do bitwise operations and easily convert them to and from ASCII. It’s also provides better performance than representing the bytes as Objects.

Now we need a parser. I went with a sample parser by some guy named notmasteryet. There are others but this seems like a pretty solid implementation and allows us to work with bytes as Numbers. It also works as a reader that lets us read our data piece by piece instead of filling our memory.

DEFLATE

The current PNG standard only uses the DEFLATE algorithm for compression. It’s the same algorithm used in ZIP, GZIP, zlib, etc. So it’s a very common format.

Luckily for us, notmasteryet’s sample also includes a DEFLATE decompressor. It also works as a piece by piece reader which makes it more memory efficient to work with. The reader pattern is a great way to read data in nested formats.

PNG

The PNG format consists of a set of named chunks. A set of “IDAT” chunks makes up the main image data. The total data stream is compressed using DEFLATE. The uncompressed data is filtered using one of 5 simple delta compression filters for each line of pixels.

Notice that we haven’t yet touched any image-processing specific logic. DEFLATE and delta compression is used for text and other data as much as anything else.

The raw data consists of a color for each pixel. This can be either grayscale, RGB or a reference to a palette color. This is what we really want.

The PNG format is open and well documented. So I’m not going to cover it in any more detail.

Proof of Concept

Since we’re doing a lightweight JavaScript parser and probably have some control over the image data, we can skip some of the more outlandish features of the specification. We can also skip the verification parts. We’ll just skip the file headers and CRC checks.

I decided on an a simple API that reads each line of pixels as an array of RGB colors represented as a number.

var image = new PNG(base64data);
image.width; // Image width in pixels
image.height; // Image height in pixels
var line;
while(line = image.readLine()){
  for(var x = 0;x < line.length;x++){
    var px = line[x]; // Pixel RGB color as a single numeric value
    // white pixel == 0xFFFFFF
  }
}

I then took that RGB data and inserted the pixels into my document as DIV tags with a background-color.

Proof of Concept

In less than 3 hours I had a working Proof of Concept of a format I had never worked with before.

I skipped interlacing, alpha and some of the filters for the demo. It’s not meant to be a fully working prototype nor a reference library in any way.

Now What?

You could…

  • Display the image using a regular rendering method but use the PNG parser to extract colors using a Color Picker.
  • Add obfuscation or cryptographic layers to render images that can’t be easily ripped by bots or downloaded by users.
  • Render embedded PNG images using VML in Internet Explorer (which lacks data: URI support) with full alpha support.

Don’t expect this method to become the new hack for PNG or embedded images in Internet Explorer. The rendering methods here are probably too slow for that. You could do some nice stuff with CANVAS though.

However, I have demonstrated that it is possible to work with binary formats in JavaScript. We shouldn’t be afraid of utilizing existing binary standards (PNG, GZIP, SVGZ, SWF, TTF…). We shouldn’t always fallback to our comfortable old JSON format and reinvent the wheel for every client-side need.

Relevant Projects

The MooTools team is working on a tool set for vector graphics in the web browser, A.R.T. You could use binary formats to embed your vector based graphics in formats like… TrueType!

The APE (Ajax Push Engine) project brings socket programming to the JavaScript platform.

Digg’s MXHR stream parses multipart encoded data and extracts the parts for various uses. This could provide a packaging model for various widgets or data packets.

Client Side Dependency Strategy

Sunday, May 17th, 2009 by Sebastian Markbåge

This post is in response to an off-site discussion about modular dependency strategies. But I figured I’d post it here for future reference.

The Calyptus Web Resource Manager is a project that can on compile-time or on runtime handle your JavaScript, CSS, and other client-side dependencies. You can keep source code as separate files on the server or pre-compile packages (such as .ZIP, .DLL or .JAR). Currently source is available only on the .NET/Mono platforms but the concept is valid for all platforms.

Syntax

The syntax is largely inspired to be compatible with ScriptDoc and ECMAScript 4 Draft import statement. In the top of your file you add the dependencies that your file relies on:

/*
@import [package, ]filename
@include [package, ]filename
@build [package, ]filename
@compress [always|release|never]
*/

Don’t worry, we’re not going to ruin your precious open-source project with inline docs. Read on.

@import – Indicates that this file has a dependency on the referenced file and that it needs to be included in the final document (implicitly before this one). The other file may be a JavaScript file, CSS, image, Flash or something else. The project is fully extensible.

@include – Same as @import but also indicates that the referenced file should be merged into this one on compile or runtime.

@build – Same as @include but also merges any nested @import statements. Allowing you to create a single packaged file.

@compress – Indicate whether the document should use a compression tool (such as YUI compressor) or not. Defaults to “release”, which means that it won’t compress during the debug stage.

You can reference a file by either filename/namespace or package + filename/namespace. You may include wild cards to reference an entire path or namespace. If you’re referencing another file in the same package, you can exclude the package name.

If you are running ASP.NET you can exclude the package if you’re referencing an assembly that is already referenced in your Web.config.

If you’re in a .js file, the filename will automatically look for files ending in .js.

This allows you to do a namespace like syntax on prepackaged files:

/*
@import MooTools.Core.*
@import MooTools.More.URI
*/

If you want to use the runtime view generating tools the syntax depends on what View Engine you’re running. For ASP.NET WebForms you can use the following controls:

<c:Import src="filename" runat="server" />
<c:Import assembly="package" name="namespace/filename" runat="server" />
<c:Include ... />
<c:Build ... />

In the future this will be integrated into the ASP.NET ScriptManager as well. For other view engines the syntax would be much prettier.

Example

MyBaseStyle.css

div.BaseClassItem {
  background-image: url(MyBaseImage.png);
}

MyBaseClass.js

// @import MyTheme.css
var MyBaseClass = new Class({
  initialize: function(){
    this.element = new Element('div', { className: 'BaseClassItem' });
  }
});

MyChildClass.js

// @import MyBaseClass.js
var MyChildClass = new Class({
  Extends: MyBaseClass,
  ...
});

MyView.aspx

<c:Include src="MyChildClass.js" />

OUTPUT:

<link href="MyBaseStyle.css" rel="stylesheet" type="text/css" />
<script src="MyBaseClass.js" type="text/javascript"></script>
<script type="text/javascript">
var MyChildClass=new Class({Extends:MyBaseClass,...});
</script>

Since I used the included command the file is included in the output document. All it’s dependencies are automatically added to the document through links.

Any referenced file is only added once to the output. So it’s no problem adding multiple references to the same resource in partial views or by indirect dependencies.

MyOtherView.aspx

<c:Import src="MyChildClass.js" />
<c:Import src="MyBaseClass.js" />

OUTPUT:

<link href="MyBaseStyle.css" rel="stylesheet" type="text/css" />
<script src="MyBaseClass.js" type="text/javascript"></script>
<script src="MyChildClass.js" type="text/javascript"></script>

In the sample above, I import the base class after the child class. Since the child is dependent on the base, it will be included first. Therefore the second reference to MyBaseClass.js is excluded.

Typical Work Flow – Late Optimization

Typically you would only use the @import statement in all your resources. You should only reference any direct resources that your code or style sheet uses. Indirect files are referenced by the referenced resources so that if a dependency changes, you don’t have to update all your reliers. Your views will only reference the direct resources that it is using by import statements as well.

This will generate a lot of <script> and <link> tags in your documents. This is not good for production where you want to minimize the overhead of multiple requests. That’s when you start building clusters.

Common.css

/*
@build Headers.css
@build Footers.css
@build MyBaseStyle.css
*/

Common.js

/*
@build MooTools.Core.Fx.Tween
@build MyChildClass.js
*/

Now I can include the cluster Common.js in my view:

<c:Import src="Common.css" />
<c:Import src="Common.js" />
...
<c:Include src="MyChildClass.js" />

OUTPUT:

<link href="Common.css" rel="stylesheet" type="text/css" />
<script src="Common.js" type="text/javascript"></script>

The MyChildClass.js reference and all it’s dependencies are ignored since those file has already been included in the document by Common.css and Common.js. You can for example add these clusters to your Master view to automatically optimize all your partial views. If you remove a reference from your cluster it won’t break any of your code, since those files are individually added by your partial views to your document.

This pattern will allow you to do late optimization of your load-time by grouping only the files that are commonly used in to clusters. Leaving edge-case files into the outer branches of your site. To accomplish this I recommend that you use a modular framework such as MooTools.

Your clusters should be named and composed in relevant packages for your site, not in packages of JavaScript frameworks. For example, DON’T create a MooTools.js cluster that includes all MooTools files.

By default, @include and @build commands are evaluated as @import during the debug stage. That makes it easy to find the references to your source code with debugging tools such as FireBug.

Messing Up Your Beautiful Source? Use Place Holders

If you’re working with a consultant project you can just put all your references in the source file. That makes it very easy to work with. But if you have an open-source project you may not want to mess up the source with dependency references. Instead, use place holder files that @include the original source and references the dependency place holders using @import.

Fx.js

/*
@import Class.Extras.js
@include Real/Source/Fx/Fx.js
*/

Fx.CSS.js

/*
@import Fx.js
@import Element.Style.js
@include Real/Source/Fx/Fx.CSS.js
*/

Now you can reference your place holders to get dependencies instead of the original source files.

What about my CDN?

You can use a CDN to store your clusters. Just reference the full URIs in your import statements. There is a pre-built class that does this with MooTools on Google. Just @import GoogleAPIs.MooTools.

I will add an @embedded syntax to reference other files that have already been included. That way you could write your own like this:

MooTools-Cluster-Google.js

/*
@import http://ajax.googleapis.com/ajax/libs/mootools/1.2.2/mootools-yui-compressed.js
@embedded MooTools.Core.*
*/

If you reference this cluster in your view, all references to your local MooTools files will be ignored since it they are already included in the Google cluster.

@include on Images

If @include filename.png is used in a style-sheet, every instance of url(filename.png) will automatically be replaced with base64 embedded data at runtime. This is only used on the runtime version since this content can’t be sent to IE browsers. IE browsers will get the url(filename.png) reference intact.

This also works with view/document Include commands. In that case an <img> tag is rendered with a link or embedded content depending on the browser capabilities.

This pattern allows you to do late load time optimization of image dependencies.

Getting Started

As always, begin by checking out the source.

HTML 5 Current Browser Support – Part 1 – Introduction

Tuesday, March 24th, 2009 by Sebastian Markbåge

The HTML 5 working draft is continuing it’s development of the future support for HTML 5. This includes new tags, attributes and a strong specification of how clients should interact with old and new elements. What I find even more intriguing, is the standardization of many advanced JavaScript DOM features (such as editable content, drag and drop). Most of which has been available to IE users for more than a decade. This is one area that standards has been particularly slow to adopt. With the current beta versions of Safari, Chrome and Firefox these new browsers are finally ready to leave IE behind (yes, even IE 8).

Many people are still frightened of implementing code according to a working draft. Especially since it’s not scheduled to be complete until 2012. In my opinion, those fears are largely unfounded at this point. The primary reason for this is that many of the features have been available in IE for many years and the HTML 5 specification centers around keeping some historical compliance. So the primary threat for lagging cross browser functionality has already been eliminated. It is also the WHATWG’s estimate that browsers will have full compliance and people will have started utilizing this new standard long before it is finalized. For these reasons, by the time you read this, you may already be a late adopter.

However, there are still some quirks that you need to be aware of. I’ve been working on cross browser layers of the HTML 5 specifications since 2007 including backwards compatible code for older browsers. This code has been used in production and little of it has changed since mid-2008. Therefore I’ve started work on introducing these features to my JavaScript framework of choice, MooTools. While I refactor my code for this purpose I thought I might introduce some of the quirks that you might come across in your own endeavors.

Coming up

Part 2 – Drag and Drop, Copy and Paste

Part 3 – Range and Selection

Part 4 – ContentEditable and ExecCommand