Been a while since I’ve been mean­ing to check out Reac­tive Exten­sions for Javascript, I have been using the .Net ver­sion for a while now and blogged about some of the cool things you can do with the .Net version.

Although there are many sources where you can learn about it from they don’t have a struc­tured guide to help you get started and some of the info/demo I found were pretty out-dated and not in line with the lat­est API. Among the arti­cles I’ve read so far I found the series of arti­cles by Matthew Pod­wysocki on codebetter.com to be most use­ful and def­i­nitely worth read­ing through if you want get up to speed with RXJS.

Any­ways, back to the point of the post, I just wanted to show you how to achieve a sim­ple drag and drop effect using RX, with sup­port for clip­ping so you can’t drag the out­side the par­ent container.

Here’s the basic HTML for the demo and with some sim­ple CSS how it looks:

   1: <body>

   2:     <div id="wrapper">

   3:         <div id="draggable"></div>

   4:     </div>

   5: </body>

image

The javascript code for the demo is simple:

   1: <script type="text/javascript" src="js/jquery/jquery-1.4.4.min.js"></script>

   2: <script type="text/javascript" src="js/rxjs/rx.js"></script>

   3: <script type="text/javascript" src="js/rxjs/rx.jQuery.js"></script>

   4:  

   5: <script type="text/javascript">

   6:     $(function () {

   7:         var _doc = $(document), _draggable = $("#draggable"), _wrapper = $("#wrapper");

   8:  

   9:         // boundary so that the draggable content can't move outside of the its container

  10:         var _parent = _draggable.parent(),

  11:             _parentOffset = _parent.offset(),

  12:             _minLeft = _parentOffset.left,

  13:             _minTop = _parentOffset.top,

  14:             _maxLeft = _minLeft + _parent.width() - _draggable.outerWidth(),

  15:             _maxTop = _minTop + _parent.height() - _draggable.outerHeight();

  16:  

  17:         // get the stream of events from the mousedown, mousemove and mouseup events

  18:         var mouseDown = _draggable.toObservable("mousedown"),

  19:             mouseMove = _doc.toObservable("mousemove"),

  20:             mouseUp = _doc.toObservable("mouseup");

  21:  

  22:         // get the changes in the X and Y direction as the mouse moves

  23:         var mouseMoves = mouseMove.Skip(1).Zip(mouseMove, function (left, right) {

  24:             return {

  25:                 xChange: left.clientX - right.clientX,

  26:                 yChange: left.clientY - right.clientY

  27:             };

  28:         });

  29:  

  30:         // for each mouse down event, get all the subsequent changes in the clientX and

  31:         // clientY values resulting from the mouse move events until mouse up event occurs

  32:         var mouseDrags = mouseDown.SelectMany(function (md) {

  33:             return mouseMoves.TakeUntil(mouseUp);

  34:         });

  35:  

  36:         mouseDrags.Subscribe(function (mouseEvent) {

  37:             var oldOffset = _draggable.offset();

  38:  

  39:             // change the left and top

  40:             _draggable.css({

  41:                 left: Math.min(Math.max(oldOffset.left + mouseEvent.xChange, _minLeft), _maxLeft),

  42:                 top: Math.min(Math.max(oldOffset.top + mouseEvent.yChange, _minTop), _maxTop)

  43:             });

  44:         });

  45:     });

  46: </script>

As you can see, this exam­ple uses jQuery and included in the RXJS pack­age is a rx.jQuery.js file which con­tains a num­ber of exten­sion meth­ods to use with jQuery such as the toOb­serv­able func­tion which returns a stream of observ­able val­ues. If this sounds unfa­mil­iar to you I strongly rec­om­mend you to read through the first cou­ple of arti­cles by Matthew Pod­wysocki I linked above.

On line 23, I’m cre­ated a com­pos­ite event stream by pair­ing mouse move events with their sub­se­quent mouse move event to work out the change in the X and Y direc­tion. To visu­alise the two event streams and the resul­tant mouse­Moves stream:

image

Then on line 36, I’ve sub­scribed an event han­dler for val­ues pushed down to us through the mouse­Moves stream. This event han­dler applies the X and Y changes to the <div> element’s left and top off­set but caps them so that it can only be moved inside its container.

Demo

Part­ing thoughts

I used jQuery to demon­strate RXJS’s exten­sion func­tions for jQuery but you don’t need jQuery in order to use RXJS, in the rx.html.js script file there is a very use­ful Rx.Observable.FromHTMLEvent func­tion which lets you get an observ­able stream of events from any HTML events includ­ing IE-specific events such as ‘oncut’.

Share

One Response to “Drag and Drop using Reactive Extensions for Javascript”

  1. spartaco says:

    its amaz­ing… but it does not work very well, at least on FF7 it seems like you have to click twice to let it see the mouseup event.

Leave a Reply