Posts Tagged ‘Events’

The Reactive Extensions for JavaScript – MooTools Integration

Sunday, March 7th, 2010 by Sebastian Markbåge

This is a follow up to my earlier post about the Reactive Extensions (Rx) for JavaScript by Microsoft’s DevLabs. This is also in response to Matthew Podwysocki’s post on jQuery integration (which deserves some credit for putting it out there).

I will assume some familiarity with Rx.

Just like any other DOM library, MooTools has a way of working with native and custom passive DOM events. We can easily give Element object and the Elements collection a method to provide these events as “Observables”. In the jQuery example the method name “ToObservable” was added to the jQuery object, accepting an event type parameter, which was my initial reaction as well. But I’m going to call mine getEvent as in “getting a stream of events given the event type“.

var observableFromEvent = function(type){
  var self = this;
  return Rx.Observable.Create(function(observer){
      var fn = function(event){
          observer.OnNext(event);
      };
      self.addEvent(type, fn);
      return function(){
        self.removeEvent(type, fn);
      };
  });
};
 
Window.implement('getEvent', observableFromEvent);
Document.implement('getEvent', observableFromEvent);
Element.implement('getEvent', observableFromEvent);
Elements.implement('getEvent', observableFromEvent);

These are infinite Observables but we could also make .destroy() trigger onComplete to make them finite as well.

Flickables Example

Instead of the canonical Drag and Drop example I thought I show a twist. Let’s say we want to listen to a mouse flick. The mouse position have to move over 100px in 200ms. Then we want the angle of the flick.

var angleFromPosition = function(position, center){
    var diffX = position.x - center.x, diffY = position.y - center.y;
    var distance = Math.sqrt(diffX * diffX + diffY * diffY);
    var angle = Math.atan2(diffY + distance, diffX) * 360 / Math.PI;
    return { distance: distance, angle: angle };
};
 
var distanceReached = function(angle){ return angle.distance > 100; };
 
var timeLimit = Rx.Observable.Timer(200);
 
var mousePositions = document.getEvent('mousemove')
                     .Select(function(event){ return event.page; });
 
var flicks = document.getElements('.flickable')
             .getEvent('mousedown')
             .SelectMany(function(event){
                 return mousePositions
                     .Select(angleFromPosition.bindWithEvent(null, event.page))
                     .TakeUntil(document.getEvent('mouseup'))
                     .TakeUntil(timeLimit)
                     .Where(distanceReached)
                     .Take(1);
             });
 
// ...
 
flicks.Subscribe(function(current){
    console.log('Flicked in direction: ' + current.angle + '°');
});

Events Mixin

MooTools has a very strong benefit compared to many other libraries. The publish/subscribe pattern is made explicit even for custom classes, using the Events mixin. By implement our “getEvent” method on this class we can use Rx on all custom MooTools classes that provide passive events.

Events.implement('getEvent', observableFromEvent);

Side-effects

Rx allows for the act of subscribing to an event to trigger an action/side-effect. Think of the Request object for example. You can use the act of subscribing to it, to issue a HTTP request. Then we can turn the subsequent events like success and failure into the Observable interface. This means that Request is a complete Observable in it self. This is what I was saving the conversion name toObservable for.

Request.implement({
 
  toObservable: function(){
    var self = this;
    return Rx.Observable.create(function(observer){
 
      var listeners = {
 
        success: function(result){
          self.removeEvents(listeners);
          observer.OnNext(result);
          observer.OnCompleted();
        },
 
        cancel: function(){
          self.removeEvents(listeners);
          observer.OnCompleted();
        },
 
        failure: function(xhr){
          self.removeEvents(listeners);
          observer.OnError(xhr);
        }
 
      };
 
      if (!self.running || self.options.link == 'cancel'){
        self.addEvents(listeners).send();
        return function(){
          self.removeEvents(listeners).cancel();
        };
      }
 
      if (self.options.link == 'chain'){
        var disposed, running;
        self.chain(function(){
          running = true;
          if (!disposed) self.addEvents(listeners).send();
        });
        return function(){
          if (running) self.removeEvents(listeners).cancel();
          disposed = true;
        };
      }
 
      observer.OnComplete();
      return function(){};
 
    });
  }
 
});

This creates a finite stream of events – only one response to be exact. However, since the act of subscribing to it causes it to occur we can have it trigger repeatedly as part of a composite stream of events.

MooTools’ Fx provides a similar concept but slightly different. Even though we don’t get an event for each tick, we still get an asynchronous complete event. This means we can insert Fx as part of a composite stream of events.

Fx also requires from/to arguments to be passed at the start. So we add the option “defaultArgs” to allow us to pass those at initialization.

Fx.implement({
 
  toObservable: function(){
    var self = this;
    return Rx.Observable.create(function(observer){
 
      var listeners = {
 
        complete: function(){
          self.removeEvents(listeners);
          observer.OnCompleted();
        },
 
        cancel: function(){
          self.removeEvents(listeners);
          observer.OnCompleted();
        }
 
      };
 
      if (!self.running || self.options.link == 'cancel'){
        self.addEvents(listeners).start.run(self.options.defaultArgs, self);
        return function(){
          self.removeEvents(listeners).cancel();
        };
      }
 
      if (self.options.link == 'chain'){
        var disposed, running;
        self.chain(function(){
          running = true;
          if (!disposed)
            self.addEvents(listeners).start.run(self.options.defaultArgs, self);
        });
        return function(){
          if (running) self.removeEvents(listeners).cancel();
          disposed = true;
        };
      }
 
      observer.OnComplete();
      return function(){};
 
    });
  }
 
});

Of course since there are a lot of other classes extending the Request and Fx classes, you get the same benefits on them. This is one of the true benefits of MooTools’ modular extensibility.

That is one of the benefits of using the class(ical) pattern in JavaScript. More on that next time…

Side-effects Example

var popup = document.id('popup');
var showPopup = new Fx.Morph(popup, { property: 'opacity', defaultArgs: 1 });
var feed = new Request.JSON({ url: 'mydata.json', method: 'get' });
 
var showFeed = feed.toObservable()
               .Do(function(data){ popup.set('text', data); })
               .Concat(showPopup.toObservable());
 
// ...
 
showFeed.Subscribe(); // loads mydata.json into #popup and displays it

Using Arrays in Unit Tests

Since natives are allowed to be extended within the MooTools theorem, we can add a convenience method to turn an Array into an observable stream of content.

Array.implement('toObservable', function(){return Rx.Observable.FromArray(this);});

We can use this to fake the “flicks” event stream in our earlier example. We avoid having to include complex asynchronous tests or user action tests.

var flicks = [
               { angle: 0, distance: 100 },
               { angle: 45, distance: 100 },
               { angle: 90, distance: 100 }
             ]
             .toObservable();
 
// Unit tests
// Synchronously testing code that's depending on a flick event stream

Web Sockets and Web Workers

Now imagine this on a stream of events coming in from Web Sockets or Web Workers.

You could set up a web socket to asynchronously feed you JSON objects, and easily hook that up to the rest of you UI just as easily as the Request example above.

The Reactive Extensions for JavaScript – Event Composition

Saturday, March 6th, 2010 by Sebastian Markbåge

I’ve been following the work on the Reactive Extensions for .NET (Rx) by Eric Meijer and others over at Microsoft. At first look I was intrigued but didn’t really understand the purpose of it. However, at a second look, I realized that it had the potential to solve every major problem I’ve had with advanced UI development in JavaScript.

Asynchronous Programming – Composable Events

Modern UI development forces us to use asynchronous patterns for user actions, animations and data load. But to make UI development easy you can think of each operation as sequential. You can even artificially lock down the user interface – by disabling or hiding UI elements – while an operation is occurring.

If you want to optimize your user experience, you will need to start dabble in the complicated art of event composition. The problem occurs when you have complex interactions that depend upon other interaction or state.

You can solve this using various state machine patterns. However, I think you will find it quite difficult at times. Even if you do solve it, it’s probably going to be for a specific purpose which is not easily generalizable nor extensible.

Various tools have tried using patterns like Futures and Promises. I think those patterns need to be applied at the language level to be really useful though.

Reactive Programming in an Object Oriented World

JavaScript has introduced the map/filter/reduce methods on arrays to allow collection operations using a sequenced composition of functions.

There’s one minor thing that JavaScript developers should note. In LINQ these operations are lazy iterables. The map/filter operations aren’t actually executed until an .each() starts iterating over them. This avoids having to create duplicates of the result in memory. It also means that the underlying array can change after we call filter and map. This is similar to “live” collections in the DOM. But they can also be infinite in length just like Mozilla’s Iterators. The .each() call is still essentially a synchronous operation though.

Erik Meijer and team simply decided to make that iteration execution asynchronous.

This means that the source data can be asynchronous. So instead of thinking of Events as independent, think of them as a stream of data with an unknown length… (or an asynchronous list/array).

This means that you can now apply the same type of function composition to streams of events. Enter the Reactive Extensions for .NET.

Supposedly this could solve the problem of Event composition in the UI space.

The Reactive Extensions for JavaScript

To my surprise, Matthew Podwysocki recently started a blog series about the Reactive Extensions for JavaScript (also Microsoft to be clear). Apparently the benefits of this tool in the JS world has not gone unnoticed.

There are no bits officially released yet. However, considering Matthew Podwysocki’s recent posts and Eric Meijer’s upcoming talk at the Mix conference… I wouldn’t be surprised if something was released at Mix on March 17th.

Learning More

I have since been interested in learning more about various alternative models. There’s a research project called Arrows which provides a different model that’s more purely functional. There’s also a framework called Flapjax which is more of a DOM library aiming to provide reactive concepts to JavaScript.

To learn more about the Reactive Extensions, take a look at the videos about the .NET version posted by the team over at Channel 9.

Concerns

I’m not sure the first implementation of the Reactive Extensions is going to be the one to solve all these problems.

I think that many developers will have a difficult time thinking about these concepts in the terms of event streams. That could make it difficult to use the current method naming. In this sense, I think Arrows might be easier to get started with. It will allow you to think about events as sequential operations. However, I also see benefit in the model employed by the Reactive Extensions, IF we can all wrap our heads around it.

Another issue is the “Let” method. This may be difficult to know when to use for many developers. That’s true even for LINQ. However, in the Reactive Extensions I have a feeling those issues will become even more prevalent. Hopefully there will be better syntactical sugar to solve this issue.

Rx has yet to prove itself in real world complex applications that goes well beyond single subscription examples. I may try to extend the canonical drag and drop examples to my own HTML5 based Drag and Drop model and plugins to stress test it.

Naming Conventions

There’s also the issue of upper camel case in method names. LINQ for JavaScript is also using this convention. I’m guessing they’re trying to be compatible. However, the JavaScript convention is to use lower camel case method names, which also the new ASP.NET AJAX library is doing. So I don’t understand why.

In dynamic languages with limited auto-completion (IntelliSense) support, naming conventions are very important to follow.

Although, I do like the names Select/Where/OrderBy better than map/filter/sort since given the arguments, that tends to read better as a grammatical sentence.

UPDATE: Event DSLs

I should mention that the MooTools 2.0 team has been working on a DSL based on the CSS selector syntax. This is an extension of the Element.Delegation plugin.

The idea is to use event names and pseudos in combination to create custom composite event listeners. This example would listen to the first click event:

element.addEvent('click:flash', firstClick);

This could enable a lot more powerful combinations of custom events. However, it doesn’t enable passing of parameters and the composability of Rx and Arrows.

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…