Changing the mouse cursor when hovering over a connection

If you’re making use of the mouse events on jsPlumb connections, sooner or later you might find yourself wanting to change the mouse cursor, to indicate to the user that a particular connection is clickable or whatever.  This post gives you a quick run down on how to do so - there’s a little trick you need to be aware of.

jsPlumb can render connections with one of three technologies: Canvas or SVG in modern browsers, and VML in IE < 9. For each connection, jsPlumb draws a rectangular element to the DOM, in which it paints the line inscribed by the connection - most of these elements are unused, transparent, space.  The elements are added in arbitrary order, according to the order in which your UI makes connections.  When using the Canvas renderer, jsPlumb runs a few mouse listeners in the background, registered on the document, that calculate which, if any, connection the mouse is currently over.  The actual Canvas element for the connection over which the mouse is hovering might be buried under a whole bunch of others, but that’s not your concern…until you want to change the mouse cursor.

The solution is to change the mouse cursor on the body element whenever you get a mouseenter event from a connection:

var aConnection = jsPlumb.connect({source:"someDiv", target:"someOtherDiv"});

aConnection.bind("mouseenter", function(c) {
  $("body").css("cursor", "pointer");
});

aConnection.bind("mouseexit", function(c) {
  $("body").css("cursor", "default");
});

This example is of course for jQuery; your Library of Choice (TM) might use a different command to set a style.

In theory, this is not so much of a problem when using the SVG or VML renderers: they are supposed to pass mouse events through the transparent areas of their drawing surfaces. In practise, however, I have found that this is not always the case (SVG implementations vary in their adherence to this policy; but the VML engine in IE seems to be pretty reliable).

It’s a moot point anyway, as jsPlumb does not expose the actual shapes it has drawn - it exposes only the drawing surface on which they are drawn. This is for a couple of reasons:

In short - just use the same technique for all renderers.

Passing extra parameters to a jsPlumb connection

This is a question I’ve had a few times now - if I have several different source Endpoints that can all connect to some target Endpoint, how do I know which one has made a Connection?

The concept of ‘scope’ does not, unfortunately, provide the answer, as it is limited to a single scope per Endpoint (having said that, one open issue on the Google Code site for jsPlumb is to introduce support for multiple scopes).

One suggestion has been to support a “parameters” member in the parameters to a jsPlumb.addEndpoint call, like this:

var ep = jsPlumb.addEndpoint("someDiv", {   
  endpoint:"Rectangle",   
  isSource:true,   
  parameters: {     
    "myParam":"myParamValue",     
    "myOtherParam":789   
  } 
}); 

Whenever a Connection is made with the given Endpoint as source, all of the parameters in that block would be copied into the Connection and made available via a couple of new methods:

var myParam = connection.getParameter("myParam");  

or  

var allParams = connection.getParameters(); 

This hasn’t yet been fully thought through - all comments/suggestions welcome. If it gets enough support it might make it into a future release.

For now, though, there is a simple hack you can do to achieve the same functionality - just set values directly on your source Endpoint after having created it:

var ep = jsPlumb.addEndpoint("someDiv", {   
  endpoint:"Rectangle",   
  isSource:true 
}); 
ep["myParam"] = "myParamValue"; 
ep["myOtherParam"] = 789; 

Then to access these through a Connection, you would do this:

var myParam = connection.endpoints[0]["myParam"]; 

where endpoints[0] references the first element in the Connection’s ‘endpoints’ array - the source Endpoint. Note that you can also access the target Endpoint with endpoints[1].

Why doesn’t jsPlumb offer a save function?

I’ve been asked this question enough times now that it warrants its own blog post.

The simple answer is that jsPlumb is a “view” technology.  It’s the V in MVC.  You use jsPlumb to assist in the visualisation of some arbitrary data model: it’s not the data model itself, it’s a part of the picture of the data model.

A well designed UI maintains a clear separation between the data that is being represented and the representation itself.  It’s the same principle you should use everywhere when designing software: loose coupling.  Never make the mistake of tying the UI up with the data model.  What if you wanted to replace the jsPlumb interface with some kind of fancy chart, or a tabular dump, or whatever else you might think of?  You need the model - the M in MVC - to be independent of the view.

Of course, jsPlumb offers a means for your users to establish connections themselves, without your intervention, by drag and drop, so you need a means to keep the data model up to date with the UI.  For this, you should use jsPlumb’s bind method, and register for connection established/connection detached events.  Like this:

jsPlumb.bind("jsPlumbConnection", function(connectionInfo) {
  ...update your data model here.  The contents of the 'connectionInfo' are described below.
});

jsPlumb.bind("jsPlumbConnectionDetached", function(connectionInfo) {
   ...update your data model here.  The contents of the 'connectionInfo' are described below.
});

connectionInfo

This is a JavaScript object containing the following data:

What’s new in jsPlumb 1.2.6

jsPlumb 1.2.6 was released today, with several bugfixes, an improved syntax for specifying Endpoints, Connectors and Overlays, mouse events when using Canvas, Flowchart Connectors, and a couple of other new pieces of functionality.  Here’s a run-down.

Shorthand Syntax for Endpoints, Connectors and Overlays

In previous versions of jsPlumb you may have got used to writing code like this:

jsPlumb.connect({
  source:"someDiv",
  target:"someOtherDiv",
  connector:new jsPlumb.Connectors.Bezier(68),
  endpoints:[ new jsPlumb.Endpoints.Dot({radius:12}),
              new jsPlumb.Endpoints.Rectangle({width:20, height:30}) 
  ]
});

That’s a lot of typing. 1.2.6 introduces a shorthand syntax designed to reduce the incidence of RSI amongst jsPlumb users. The equivalent code now looks like this:

jsPlumb.connect({
  source:"someDiv",
  target:"someOtherDiv",
  connector:["Bezier",68],
  endpoints:[["Dot",{radius:12}], ["Rectangle",{width:20,height:30}]]
});

If you are happy using the default parameter values for some component, you can use an even terser syntax:

jsPlumb.connect({
  source:"someDiv",
  target:"someOtherDiv",
  connector:"Bezier",
  endpoints:[ "Dot", "Rectangle" ]
});

The same syntax can be used for Overlays. For instance, you used to write something like:

jsPlumb.connect({
  source:"someDiv",
  target:"someOtherDiv",
  overlays:[
   new jsPlumb.Overlays.Arrow({location:0.1, width:10}),
   new jsPlumb.Overlays.Label({label:"foo", location:0.6})
  ]
});

which now would look like this:

jsPlumb.connect({
  source:"someDiv",
  target:"someOtherDiv",
  overlays:[
   [ "Arrow", {location:0.1, width:10} ],
   [ "Label", {label:"foo", location:0.6} ]
  ]
});

The same terser syntax applies to Overlays, if you are happy to be using the defaults, as it does for Endpoints and Connectors.

Flowchart Connectors

These are what the name suggests: connectors that consist of a series of horizontal or vertical segments, as you’d see on a flowchart. They support one parameter, minStubLength, which tells jsPlumb what the shortest acceptable value is for the stub that begins each connection.

To use these, taking the example code from above, you’d do this:

jsPlumb.connect({
  source:"someDiv",
  target:"someOtherDiv",
  connector:[ "Flowchart", {minStubLength:40} ],
  endpoints:[ "Dot", "Rectangle" ]
});

Note that here I have set the ‘minStubLength’ argument to tell jsPlumb the stubs need to be at least 40px long. This will make more sense if you checkout the demo.

Mouse Events

1.2.6 introduces support for the following mouse events, but only when you’re using Canvas, meaning not in IE prior to version 9.  The next release of jsPlumb will support mouse events for all browsers.

Handlers for these events can be registered on Endpoints and Connectors, using the bind method:

var connection = jsPlumb.connect(...some options...);
connection.bind("click", function(c) {
  console.log("c is a Connection object.  it connects " + c.sourceId + " to " + c.targetId);
});

As I was about to release 1.2.6 I realised, from using the API myself, that it would probably be more convenient to be able to bind a click handler on jsPlumb itself, to be notified of click events on any Endpoint or Connector. This functionality will be included in the next jsPlumb release.

This new functionality allows jsPlumb to support ‘hover’ styles for Endpoints and Connections - just like the CSS :hover pseudo class. For example:

jsPlumb.connect({
  source:"someDiv", 
  target:"someOtherDiv",
  paintStyle:{strokeStyle:"blue", lineWidth:5},
  hoverPaintStyle:{strokeStyle:"red", lineWidth:7}
});

Acceptable arguments for ‘hoverPaintStyle’ are whatever is acceptable for ‘paintStyle’.

Miscellaneous bits & pieces

jsPlumb basics

In this post I’m going to attempt to provide you with a basic overview of the concepts used by the jsPlumb plugin.  If jsPlumb is new to you then maybe you might want to take a look here:

http://jplumb.org

In a nutshell, jsPlumb offers you the ability to visually link elements of your HTML pages together, using what we call Connections.  A Connection appears as a line of some description - the path that the line follows depends on what type of Connection you use.  jsPlumb ships with two Connections - a straight line, and a Bezier curve - but Connections are pluggable and it is relatively easy to create your own.

Each Connection is established between two Endpoints.  An Endpoint is the point on some element at which the Connection joins, and it has some visual representation of its own.  You can see various different types of Endpoints on the demonstration pages.  jsPlumb ships with three different types of Endpoints: a Dot, a Rectangle, and an Image, but as with Connections, Endpoint implementations are pluggable.

Each Endpoint is associated with an Anchor.  Anchors define where on an element an Endpoint should be located.  Anchors do not have any visual representation of their own; they are simply used for positioning.  jsPlumb ships with several default Anchors - TopLeft, TopCenter, TopRight, RightMiddle, BottomRight, BottomCenter, BottomLeft, LeftMiddle and Center.  Hopefully their names are self explanatory.  Once again, it is easy to create your own Anchor implementation and plug it in to jsPlumb, but there is also a helper method that should cover most of your needs (see the last example in this post).

Under the hood, jsPlumb uses the Canvas element to draw Connections and Endpoints.  This is supported in most browsers but not IE; fortunately, though, Google created ExplorerCanvas.js, which simulates the Canvas element in IE.

So now we’ve got those few concepts out there, let’s take a look at a simple connection:

jsPlumb.connect({source:'element1', target:'element2'});

This is the most basic connection you can make in jsPlumb - all you have to provide is the ID of the two elements to connect.  Everything that you can specify yourself in jsPlumb also has a default setting, so in this case jsPlumb will use defaults for all of these things:

So let’s look at providing values for some of those defaults.  In this next example, we’re going to tell jsPlumb that the Connection should be black and 10 pixels wide, the Endpoints should be rectangles of size 10x15, and the connection should go from the RightMiddle of one element to the BottomLeft of the other:

jsPlumb.connect( { 
source:'element1',
target:'element2',
paintStyle:{ lineWidth:10, strokeStyle:'black' },
endpoint:new jsPlumb.Endpoints.Rectangle({width:10, height:15}),
anchors:["RightMiddle", "BottomLeft"]

});

There are a few things to note about this:

The last example I’ll give you today will expand on the previous one a little.  Here we’re going to specify different Endpoints for each end of the Connection, each with its own paint style, and we’ll also specify a gradient for the Connection:

jsPlumb.connect( { 
source:'element1',
target:'element2',
paintStyle:{
lineWidth:10,
gradient:{ stops:[[0, 'blue'],[1, 'red']] }
},
endpoints:[ new jsPlumb.Endpoints.Dot({radius:10}), new jsPlumb.Endpoints.Dot({radius:25}) ],
endpointStyles:[{fillStyle:'blue'}, {fillStyle:'red'}],
anchors:[[0.333, 1, 0, 1], [0.333, 0, 0,-1] ]
});

The jsPlumb documentation can be found here: 

http://jsPlumb.org/doc/usage.html

Introducing jsPlumb

jsPlumb is a jQuery plugin that enables you to connect elements on an HTML page with visual links, both statically, and, from version 1.1.0, by clicking and dragging with the mouse.

there are several demonstration pages available at http://morrisonpitt.com/jsPlumb/html/demo.html

the code is open sourced and MIT-licensed, and is hosted on Google Code at http://code.google.com/p/jsplumb/

this blog will keep you up to date with enhancements and whatever else goes on in jsPlumb land.