The most simple connection you can make with jsPlumb looks like this:
jsPlumb.connect({source:"element1", target:"element2"});
In this example we have created a Connection from 'element1' to 'element2'. Remember that a Connection in jsPlumb consists of two Endpoints, a Connector, and zero or more Overlays. But this call to 'connect' supplied none of those things, so jsPlumb uses the default values wherever it needs to. In this case, default values have been used for the following:
Bottom
.So this call will result in an 8px Bezier, colored "#456", from the bottom center of 'element1' to the bottom center of 'element2', and each Endpoint will be a 10px radius Dot Endpoint, colored "#456".
Let's beef up this call a little and tell jsPlumb what sort of Endpoints we want, and where we want them:
jsPlumb.connect({
source:"element1",
target:"element2",
anchors:["Right", "Left" ],
endpoint:"Rectangle",
endpointStyle:{ fillStyle: "yellow" }
});
This is what we have told jsPlumb we want this time:
anchors
you can use anchor
, if you want the source and target Endpoints to be located at the same place on their parent elements.Rectangle
Endpoint for both the source and target of the Connection. As with anchors, endpoint
has a plural version that allows you to specify a different Endpoint for each end of the Connection.A fairly common situation you will find yourself in is wanting to create a bunch of Connections that have only minor differences between them. To support that, jsPlumb.connect
takes an optional second argument. For example:
var common = {
anchors:[ "BottomCenter", "TopCenter" ],
endpoints:["Dot", "Blank" ]
};
jsPlumb.connect({ source:"someElement", target:"someOtherElement" }, common);
jsPlumb.connect({ source:"aThirdElement", target:"yetAnotherElement" }, common);
If you supply an element id or selector for either the source or target, jsPlumb.connect will automatically create an Endpoint on the given element. These automatically created Endpoints are not marked as drag source or targets, and cannot be interacted with. For some situations this behaviour is perfectly fine, but for more interactive UIs you should set things up using one of the drag and drop methods discussed below.
Note: given that jsPlumb.connect
creates its own Endpoints in some circumstances, in order to avoid leaving orphaned Endpoints around the place, if the Connection is subsequently deleted, these automatically created Endpoints are deleted too. Should you want to, you can override this behaviour by setting deleteEndpointsOnDetach
to false in the connect call:
jsPlumb.connect({
source:"aThirdElement",
target:"yetAnotherElement",
deleteEndpointsOnDetach:false
});
By default, connections made with jsPlumb.connect
will be detachable via the mouse. You can prevent this by either setting an appropriate default value:
jsPlumb.importDefaults({
...
ConnectionsDetachable:false
...
});
...or by specifying it on the connect call like this:
jsPlumb.connect({
source:"aThirdElement",
target:"yetAnotherElement",
detachable:false
});
To support drag and drop connections, you first need to set a few things up. Every drag and drop connection needs at least a source Endpoint that the user can drag a connection from. Here's a simple example of how to create an Endpoint:
var endpointOptions = { isSource:true };
var endpoint = jsPlumb.addEndpoint('elementId', endpointOptions);
This Endpoint will act as a source for new Connections, and will use the jsPlumb defaults for its own appearance and that of any Connections that are drawn from it.
One thing that happens quite often is that you have an Endpoint whose appearance and behaviour is largely the same between usages on different elements, with just a few differences.
var exampleGreyEndpointOptions = {
endpoint:"Rectangle",
paintStyle:{ width:25, height:21, fillStyle:'#666' },
isSource:true,
connectorStyle : { strokeStyle:"#666" },
isTarget:true
};
Notice there is no anchor
set. Here we apply it to two elements, at a different location in each:
jsPlumb.addEndpoint("element1", {
anchor:"Bottom"
}, exampleGreyEndpointOptions));
jsPlumb.addEndpoint("element2", {
anchor:"Top"
}, exampleGreyEndpointOptions));
Now that you have a source Endpoint, you need to either create a target Endpoint on some element, or notify jsPlumb that you wish to make an entire element a drop target. Let's look at how to attach a target Endpoint first:
var endpointOptions = { isTarget:true, endpoint:"Rectangle", paintStyle:{ fillStyle:"gray" } };
var endpoint = jsPlumb.addEndpoint("otherElementId", endpointOptions);
This Endpoint, a gray rectangle, has declared that it can act as a drop target for Connections.
jsPlumb also supports turning entire elements into Connection sources and targets, using the methods makeSource
and makeTarget
. With these methods you mark an element as a source or target, and provide an Endpoint specification for jsPlumb to use when a Connection is established. makeSource
also gives you the ability to mark some child element as the place from which you wish to drag Connections, but still have the Connection on the main element after it has been established.
These methods honour the jsPlumb defaults - if, for example you set up the default Anchors to be this:
jsPlumb.importDefaults({
Anchors : [ "Left", "BottomRight" ]
});
... and then used makeSource
and makeTarget
without specifying Anchor locations, jsPlumb would use Left
for the makeSource
element and BottomRight
for the makeTarget
element.
A further thing to note about makeSource
and makeTarget
is that any prior calls to one of these methods is honoured by subsequent calls to jsPlumb.connect
. This helps when you're building a UI that uses this functionality at runtime but which loads some initial data, and you want the statically loaded data to have the same appearance and behaviour as dynamically created Connections (obviously quite a common use case).
For example:
jsPlumb.makeSource("el1", {
anchor:"Continuous",
endpoint:["Rectangle", { width:40, height:20 }],
maxConnections:3
});
...
jsPlumb.connect({source:"el1", target:"el2"});
In this example, the source of the connection will be a Rectangle of size 40x20, having a Continuous Anchor. The source is also configured to allow a maximum of three Connections.
You can override this behaviour by setting a newConnection
parameter on the connect call:
jsPlumb.makeSource("el1", {
anchor:"Continuous",
endpoint:["Rectangle", { width:40, height:20 }],
maxConnections:1,
onMaxConnections:function(info, originalEvent) {
console.log("element is ", info.element, "maxConnections is", info.maxConnections);
}
});
...
jsPlumb.connect({
source:"el1",
target:"el2",
newConnection:true
});
Note the onMaxConnections
parameter to this call - it allows you to supply a function to call if the user tries to drag a new Connection when the source has already reached capacity.
As with jsPlumb.connect
, makeSource
can take an optional third argument consisting of parameters that may be common across several different calls:
var common = {
anchor:"Continuous",
endpoint:["Rectangle", { width:40, height:20 }],
};
jsPlumb.makeSource("el1", {
maxConnections:1,
onMaxConnections:function(info, originalEvent) {
console.log("element is ", info.element, "maxConnections is", info.maxConnections);
}
}, common);
This method takes two arguments, the first of which specifies some element (or list of elements); the second specifies the Endpoint you wish to create on that element whenever a Connection is established on it. In this example we will use the exact same target Endpoint we used before - the gray rectangle - but we will tell jsPlumb that the element aTargetDiv
will be the drop target:
var endpointOptions = {
isTarget:true,
maxConnections:5,
endpoint:"Rectangle",
paintStyle:{ fillStyle:"gray" }
};
jsPlumb.makeTarget("aTargetDiv", endpointOptions);
The allowed values in 'endpointOptions' are identical for both the jsPlumb.addEndpoint
and jsPlumb.makeTarget
methods, but makeTarget
supports an extended Anchor syntax that allows you more control over the location of the target endpoint. This is discussed below.
Notice in the endpointOptions
object above there is a parameted called isTarget
- this may seem incongruous, since you know you're going to make some element a target. Remember that the endpointOptions
object is the information jsPlumb will use to create an Endpoint on the given target element each time a Connection is established to it. It takes the exact same format as you would pass to addEndpoint
; makeTarget
is essentially a deferred addEndpoint
call followed by a connect
call. So in this case, we're telling jsPlumb that any Endpoints it happens to create on some element that was configured by the makeTarget
call are themselves Connection targets. You may or may not want this behaviour in your application - just control it by setting the approriate value for that parameter (it defaults to false).
makeTarget
also supports the maxConnections
and onMaxConnections
parameters, as makeSource
does, but note that onMaxConnections
is passed one extra parameter than its corresponding callback from makeSource
- the Connection the user tried to drop:
jsPlumb.makeTarget("aTargetDiv", {
isTarget:true,
maxConnections:5,
endpoint:"Rectangle",
paintStyle:{ fillStyle:"gray" },
maxConnections:3,
onMaxConnections:function(info, originalEvent) {
console.log("user tried to drop connection", info.connection, "on element", info.element, "with max connections", info.maxConnections);
}
};
As with jsPlumb.connect
and jsPlumb.makeSource
, jsPlumb.makeTarget
can take an optional third argument consisting of parameters that may be common across several different calls:
var common = {
anchor:"Continuous",
endpoint:["Rectangle", { width:40, height:20 }],
};
jsPlumb.makeTarget("el1", {
maxConnections:1,
onMaxConnections:function(info, originalEvent) {
console.log("element is ", info.element, "maxConnections is", info.maxConnections);
}
}, common);
In vanilla jsPlumb only, you can instruct jsPlumb to prevent loopback connections without having to resort to a beforeDrop interceptor. You do this by setting allowLoopback:false
on the parameters passed to the makeTarget
method:
jsPlumb.makeTarget("foo", {
allowLoopback:false
});
jsPlumb will create a new Endpoint using the supplied information every time a new Connection is established on the target element, by default, but you can override this behaviour and tell jsPlumb that it should create at most one Endpoint, which it should attempt to use for subsequent Connections:
var endpointOptions = {
isTarget:true,
uniqueEndpoint:true,
endpoint:"Rectangle",
paintStyle:{ fillStyle:"gray" }
};
jsPlumb.makeTarget("aTargetDiv", endpointOptions);
Here, the uniqueEndpoint
parameter tells jsPlumb that there should be at most one Endpoint on this element. Notice that maxConnections
is not set: the default is 1, so in this setup we have told jsPlumb that aTargetDiv
can receive one Connection and no more.
By default, any Endpoints created using makeTarget have deleteEndpointsOnDetach
set to true, which means that once all Connections to that Endpoint are removed, the Endpoint is deleted. You can override this by setting the flag to true on the makeTarget
call:
var endpointOptions = {
isTarget:true,
maxConnections:5,
uniqueEndpoint:true,
deleteEndpointsOnDetach:false,
endpoint:"Rectangle",
paintStyle:{ fillStyle:"gray" }
};
jsPlumb.makeTarget("aTargetDiv", endpointOptions);
In this setup we have told jsPlumb to create an Endpoint the first time aTargetElement
receives a Connection, and to not delete it even if there are no longer any Connections to it. The created Endpoint will be reused for subsequent Connections, and can support a maximum of 5.
As with jsPlumb.connect
, Connections made with the mouse after setting up Endpoints with one of the functions we've just covered will be, by default, detachable. You can prevent this in the jsPlumb defaults, as previously mentioned:
jsPlumb.importDefaults({
...
ConnectionsDetachable:false,
...
});
And you can also set this on a per-endpoint (or source/target) level, like in these examples:
jsPlumb.addEndpoint("someElementId", {
connectionsDetachable:false
});
jsPlumb.makeSource("someOtherElement", {
...
connectionsDetachable:false,
...
});
jsPlumb.makeTarget("yetAnotherElement", {
...
connectionsDetachable:false,
...
});
Note that in the jsPlumb defaults, by convention each word in a parameter is capitalised ("ConnectionsDetachable"), whereas for a call to one of these methods, we use camel case ("connectionsDetachable").
When using the makeTarget
method, jsPlumb allows you to provide a callback function to be used to determine
the appropriate location of a target Anchor for every new Connection dropped on the given target. It may be the case that you want to take some special action rather than just relying on one of the standard Anchor mechanisms.
This is achieved through an extended Anchor syntax (note that this syntax is not supported in the jsPlumb.addEndpoint
method) that supplies a "positionFinder" to the anchor specification. jsPlumb provides two of these by default; you can register your own on jsPlumb and refer to them by name, or just supply a function. Here's a few examples:
jsPlumb.makeTarget("someElement", {
anchor:[ "Assign", {
position:"Fixed"
}]
});
Fixed
is one of the two default position finders provided by jsPlumb. The other is Grid
:
jsPlumb.makeTarget("someElement", {
anchor:[ "Assign", {
position:"Grid",
grid:[3,3]
}]
});
The Grid position finder takes a grid
parameter that defines the size of the grid required. [3,3] means 3 rows and 3 columns.
To supply your own position finder to jsPlumb you first need to create the callback function. First let's take a look at what the source code for the Grid
position finder looks like:
function(eventOffset, elementOffset, elementSize, constructorParams) {
var dx = eventOffset.left - elementOffset.left, dy = eventOffset.top - elementOffset.top,
gx = elementSize[0] / (constructorParams.grid[0]),
gy = elementSize[1] / (constructorParams.grid[1]),
mx = Math.floor(dx / gx), my = Math.floor(dy / gy);
return [ ((mx * gx) + (gx / 2)) / elementSize[0], ((my * gy) + (gy / 2)) / elementSize[1] ];
}
The four arguments are: - eventOffset - Page left/top where the mouse button was released (a JS object containing left/top members like you get from a jQuery offset call) - elementOffset - JS offset object containing offsets for the element on which the Connection is to be created - elementSize - [width, height] array of the dimensions of the element on which the Connection is to be created - constructorParams - the parameters that were passed to the Anchor's constructor. In the example given above, those parameters are 'position' and 'grid'; you can pass arbitrary parameters.
The return value of this function is an array of [x, y] - proportional values between 0 and 1 inclusive, such as you can pass to a static Anchor.
To make your own position finder you need to create a function that takes those four arguments and returns an [x, y] position for the anchor, for example:
jsPlumb.AnchorPositionFinders.MyFinder = function(dp, ep, es, params) {
... do some maths ...
console.log("my custom parameter is ", params.myCustomParameter);
return [ x, y ];
};
Then refer to it in a makeTarget call:
jsPlumb.makeTarget("someElement", {
anchor:[ "Assign", {
position:"MyFinder",
myCustomParameter:"foo",
anInteger:5
}]
});
There are two use cases supported by this method. The first is the case that you want to drag a Connection from the element itself and have an Endpoint attached to the element when a Connection is established. The second is a more specialised case: you want to drag a Connection from the element, but once the Connection is established you want jsPlumb to move it so that its source is on some other element.
Here's an example code snippet for the basic use case of makeSource:
jsPlumb.makeSource(someDiv, {
paintStyle:{ fillStyle:"yellow" },
endpoint:"Blank",
anchor:"BottomCenter"
});
Notice again that the second argument is the same as the second argument to an addEndpoint
call. makeSource
is, essentially, a type of addEndpoint
call. In this example we have told jsPlumb that we will support dragging Connections directly from someDiv
. Whenever a Connection is established between someDiv
and some other element, jsPlumb assigns an Endpoint at BottomCenter of someDiv
, fills it yellow, and sets that Endpoint as the newly created Connection's source.
Configuring an element to be an entire Connection source using makeSource
means that the element cannot itself be draggable. There would be no way for jsPlumb to distinguish between the user attempting to drag the element and attempting to drag a Connection from the element. To handle this there is the filter
parameter.
filter
parameterYou can supply a filter
parameter to the makeSource call, which can be either a function or a selector. Consider this markup:
<div id="foo">
<span>FOO</span>
<button>click me</button>
<div>
Let's suppose we do not want to interfere with the operation of the "click me" button.
We can supply a selector filter to the makeSource
call to do so:
jsPlumb.makeSource("foo", {
filter:":not(button)"
});
Valid values for a filter selector are as per the spec.
One of the limitations of CSS3 selectors is that the contents of a :not
selector are restricted to what are called simple selectors. What this means is that only one item may appear inside a :not
, eg. :not(.someClass)
, :not(button)
etc. You cannot, however, do something like this: :not(.myClass button)
.
To allow for these sorts of negation selectors, you can set the filterExclude:true
flag and re-write your selector:
jsPlumb.makeSource("foo", {
filter:"span",
filterExclude:true
});
Alternatively, you can supply a function as the filter:
jsPlumb.makeSource("foo", {
filter:function(event, element) {
return event.target.tagName !== "BUTTON";
}
});
In this example, if the filter returns anything other than a boolean false, the drag will begin. It's important to note that only boolean false will prevent a drag. False-y values will not.
There are many things you can set in an Endpoint options object; for a thorough list see the API documentation for Endpoint.
Here's an example of specifying that you want an Arrow overlay halfway along any Connection dragged from this Endpoint:
var exampleGreyEndpointOptions = {
endpoint:"Rectangle",
paintStyle:{ width:25, height:21, fillStyle:'#666' },
isSource:true,
connectorStyle : { strokeStyle:"#666" },
isTarget:true,
connectorOverlays: [ [ "Arrow", { location:0.5 } ] ]
};
This is an Endpoint that moves around the element it is attached to dependent on the location of other elements in the connections it is attached to (a 'dynamic' anchor):
var exampleGreyEndpointOptions = {
endpoint:"Rectangle",
paintStyle:{ width:25, height:21, fillStyle:'#666' },
isSource:true,
connectorStyle : { strokeStyle:"#666" },
isTarget:true,
connectorOverlays: [ [ "Arrow", { location:0.5 } ] ]
anchor:[ "TopCenter","RightMiddle","BottomCenter","LeftMiddle" ]
};
You can test if some element has been made a connection source or target using these methods:
isTarget(some id, selector or DOM element)
isSource(some id, selector or DOM element)
The return value is a boolean.
You can toggle the state of some source or target using these methods:
setTargetEnabled(some id, selector or DOM element)
setSourceEnabled(some id, selector or DOM element)
The return value from these methods is the current jsPlumb instance, allowing you to chain them:
jsPlumb.setTargetEnabled("aDivId").setSourceEnabled($(".aSelector"));
Note that if you call either of these methods on an element that was not originally configured as a target or source, nothing will happen.
You can check the enabled state of some target or source using these methods:
isTargetEnabled(some id, selector or DOM element)
isSourceEnabled(some id, selector or DOM element)
makeTarget
and/or makeSource
callsjsPlumb offers four methods to let you cancel previous makeTarget
or makeSource
calls. Each of these methods returns the current jsPlumb instance, and so can be chained:
unmakeTarget(some id, selector or DOM element)
unmakeSource(some id, selector or DOM element)
unmakeEveryTarget
unmakeEverySource
These last two are analogous to the removeEveryConnection
and deleteEveryEndpoint
methods that have been in jsPlumb for a while now.
unmakeTarget
and unmakeSource
both take as argument the same sorts of values that makeTarget
and makeSource
accept - a string id, or a selector, or an array of either of these:
jsPlumb.unmakeTarget("aDivId").unmakeSource($(".aSelector"));
These are options that will be passed through to the supporting library's drag API. jsPlumb passes everything you supply here through, inserting wrapping functions if necessary for the various lifecycle events that jsPlumb needs to know about. So if, for example, you pass a function to be called when dragging starts, jsPlumb will wrap that function with a function that does what jsPlumb needs to do, then call yours.
At the time of writing, jsPlumb supports jQuery, as well as having its own built-in support (actually supplied by a bundled library called Katavorio. If you're using jQuery you can supply jQuery drag option as values on the dragOptions; the best drag library to use, though, is Katavorio, which is bundled in vanilla jsPlumb from 1.6.0 onwards: it supports everything jQuery drag supports, as well as multiple scopes and multiple element dragging.
Given that the options here are library-specific, and they are all well-documented, we're going to discuss just the three drag options that behave the same way in all (see below for hoverClass):
For more information about drag options, take a look at the jQuery or Katavorio docs.
Drop options are treated by jsPlumb in the same way as drag options - they are passed through to the underlying library.
Here are three common jQuery/Katavorio droppable options that you might want to consider using:
For more information about drop options when using jQuery, see here
jsPlumb borrowed the concept of 'scope' from jQuery's drag/drop implementation: the notion of which draggables can be dropped on which droppables. In jsPlumb you can provide a 'scope' entry when creating an Endpoint. Here's the example grey Endpoint example with 'scope' added:
var exampleGreyEndpointOptions = {
endpoint:"Rectangle",
paintStyle:{ width:25, height:21, fillStyle:"#666" },
isSource:true,
connectionStyle : { strokeStyle:"#666" },
isTarget:true,
scope:"exampleGreyConnection"
};
If you do not provide a 'scope' entry, jsPlumb uses a default scope. Its value is accessible through this method:
jsPlumb.getDefaultScope();
If you want to change it for some reason you can do so with this method:
jsPlumb.setDefaultScope("mySpecialDefaultScope");
You can also, should you want to, provide the scope value separately on the drag/drop options, like this:
var exampleGreyEndpointOptions = {
endpoint:"Rectangle",
paintStyle:{ width:25, height:21, fillStyle:'#666' },
isSource:true,
connectionStyle : { strokeStyle:"#666" },
isTarget:true,
dragOptions:{ scope:"dragScope" },
dropOptions:{ scope:"dropScope" }
};
If you're using vanilla jsPlumb you can assign multiple scopes to some Endpoint, or element configured via makeSource
or makeTarget
. To do so, provide the scopes as a space-separated list, as you would with CSS classes:
var exampleGreyEndpointOptions = {
endpoint:"Rectangle",
paintStyle:{ width:25, height:21, fillStyle:"#666" },
isSource:true,
connectionStyle : { strokeStyle:"#666" },
isTarget:true,
scope:"foo bar baz"
};
jsPlumb offers a few methods for changing the scope(s) of some element configured via makeSource
or makeTarget
:
setScope(el, scopeString)
Sets both the source and target scope(s) for the given element.setSourceScope(el, scopeString)
Sets the source scope(s) for the given element.setTargetScope(el, scopeString)
Sets both the target scope(s) for the given element.By default, jsPlumb will allow you to detach connections from either Endpoint by dragging (assuming the Endpoint allows it; Blank Endpoints, for example, have nothing you can grab with the mouse). If you then drop a Connection you have dragged off an Endpoint, the Connection will be detached. This behaviour can be controlled using the detachable
and reattach
parameters, or their equivalents in the jsPlumb Defaults.
Some examples should help explain:
jsPlumb.connect({ source:"someElement", target:"someOtherElement" });
jsPlumb.connect({ source:"someElement", target:"someOtherElement", detachable:false });
jsPlumb.connect({ source:"someElement", target:"someOtherElement", reattach:true });
This behaviour can also be controlled using jsPlumb defaults:
jsPlumb.importDefaults({
ConnectionsDetachable:true,
ReattachConnections:true
});
The default value of ConnectionsDetachable is true, and the default value of ReattachConnections is false, so in actual fact those defaults are kind of pointless. But you probably get the idea.
Endpoints support the detachable
and reattach
parameters too. If you create an Endpoint and mark detachable:false
, then all Connections made from that Endpoint will not be detachable. However, since there are two Endpoints involved in any Connection, jsPlumb takes into account the detachable
and reattach
parameters from both Endpoints when establishing a Connection. If either Endpoint declares either of these values as true, jsPlumb assumes the value to be true. It is possible that in a future version of jsPlumb the concepts of detachable and reattach could be made more granular, through the
introduction of parameters like sourceDetachable
/targetReattach
etc.
If you drag a Connection from its target Endpoint you can then drop it on another suitable target Endpoint - suitable meaning that it is of the correct scope and it is not full. If you try to drop a Connection on another target that is full, the drop will be aborted and then the same rules will apply as if you had dropped the Connection in whitespace: if reattach
is set, the Connection will reattach, otherwise it will be removed.
You can drag a Connection from its source Endpoint, but you can only drop it back on its source Endpoint - if you try to drop it on any other source or target Endpoint jsPlumb will treat the drop as if it happened in whitespace. Note that there is an issue with the hoverClass
parameter when dragging a source Endpoint: target Endpoints are assigned the hover class, as if you could drop there. But you cannot; this is caused by how jsPlumb uses the underlying library's drag and drop, and is something that will be addressed in a future release.