- Drag Options
- Drag Options in Defaults
- Drag Containment
- Dragging on a Grid
- Required CSS
- Text Selection while dragging
- Community Edition
- Dragging nested elements
- Drag Options in the Toolkit Edition
- Dragging Multiple Elements
- The Drag Selection
- Posses
- Posses in the Community Edition
- Posses in the Toolkit Edition
Overview Options CSS Disable Text Selection Setup Dragging Community Edition Setup Dragging Toolkit Edition
A common feature of interfaces using jsPlumb is that the elements are draggable. In the Community Edition you should use the draggable
method on a jsPlumbInstance
to configure this:
...because if you don't, jsPlumb won't know that an element has been dragged, and it won't repaint the element.
In the Toolkit edition, nodes/groups are automatically made draggable by the render
call. You can disable that behaviour though, with the elementsDraggable
parameter:
The options given here are common between the Community and Toolkit editions, but the means of supplying them differs.
You can use the DragOptions
default to set default behaviour for dragging. See this page for information about this.
A common request is for the ability to contain the area within which an element may be dragged. jsPlumb offers support for containing a dragged element within the bounds of its parent:
To constrain elements to a grid, supply the size of the grid in the draggable
call:
You must set position:absolute
on elements that you intend to be draggable. The reasoning for this is that all libraries implement dragging by manipulating the left and top properties of an element's style attribute.
When you position an element absolute, these left
/top
values are taken to mean with respect to the origin of this element's offsetParent
, and the offsetParent
is the first ancestor of the element that has position:relative
set on it, or the body if no such ancestor is found.
When you position an element relative, the left
/top
values are taken to mean move the element from its normal position by these amounts, where "normal position" is dependent on document flow. You might have a test case or two in which relative positioning appears to work; for each of these you could create several others where it does not.
You cannot trust relative positioning with dragging.
The jsplumbtoolkit.css
file that ships with both the Community and Toolkit editions provides a .jtk-node
class which sets position:absolute
. The Toolkit edition adds this class to any node it renders; the Community edition will, from 4.x, be adding this class to any element it is requested to manage.
The default browser behaviour on mouse drag is to select elements in the DOM. To assist with handling this, this class is attached to the body at drag start:
jtk-drag-select
The class is removed at drag end.
A suitable value for this class (this is from the jsPlumb demo pages) is:
.jtk-drag-select * {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
This CSS rule is provided in the jsplumbtoolkit.css
file that is shipped with both the Community and Toolkit editions.
In the Community Edition you supply drag options to the draggable
call:
jsPlumb.draggable("someElement", {
grid:[50,50]
});
Allowed argument types
draggable
supports several types as argument:
- a String representing an element ID
- an Element
- any "list-like" object whose contents are Strings or Elements.
A "list-like" element is one that exposes a length
property and which can be indexed with brackets ([ ]
). Some examples are:
- arrays
- jQuery selectors
- NodeLists
Manually configuring dragging
If you absolutely cannot use jsPlumb.draggable
, you will have to arrange to repaint the drag element manually, via jsPlumb.revalidate
.
jsPlumb takes nesting into account when handling draggable elements. For example, say you have this markup:
...and then you connect one of those child divs to something, and make container draggable:
jsPlumb.connect({
source:$("#container .childType1"),
target:"somewhere else"
});
jsPlumb.draggable("container");
Now when you drag container
, jsPlumb will have noticed that there are nested elements that have Connections, and they will be updated. Note that the order of operations is not important here: if container was already draggable when you connected one of its children to something, you would get the same result.
Nested Element offsets
For performance reasons, jsPlumb caches the offset of each nested element relative to its draggable ancestor. If you make changes to the draggable ancestor that will have resulted in the offset of one or more nested elements changing, you need to tell jsPlumb about it, using the revalidate
function.
Consider the example from before, but with a change to the markup after initializing everything:
<div id="container">
<div class="header" style="height:20px;background-color:blue;">header</div>
<div class="childType1"></div>
<div class="childType2"></div>
<div>
Connect a child div to some element and make it draggable:
jsPlumb.connect({
source:$("#container .childType1"),
target:"somewhere else"
});
jsPlumb.draggable($(".childType1"));
Now if you manipulate the DOM in such a way that the internal layout of the container node is changed, you need to tell jsPlumb:
$("#container .header").hide(); // hide the header bar. this will alter the offset of the other child elements...
jsPlumb.revalidate("container"); // tell jsPlumb that the internal dimensions have changed.
// you can also use a selector, eg $("#container")
Note in this example, one of the child divs was made draggable. jsPlumb automatically recalculates internal offsets after dragging stops in that case - you do not need to call it yourself.
The equivalent way to provide drag options in the Toolkit edition is to supply them to the render
call:
var surface = toolkitInstance.render({
...
dragOptions:{
grid:[50,50]
}
...
});
jsPlumb supports dragging multiple elements at once. There are two ways to do this - one via the "drag selection" and the other with a concept called "posses".
Note For users of the Toolkit, the drag selection discussed here maps to the concept of the "current selection" in the Toolkit: when you add a node/group to the current selection, its corresponding DOM element is added to the renderer's drag selection. When you remove a node/group from the current selection, or from the Toolkit itself, its DOM node is removed from the renderer's drag selection. You will not need to know about the methods discussed here.
Every jsPlumb instance has an associated dragSelection
- a set of elements that are considered to be "selected" (and which have a CSS class assigned to them to indicate this fact). Any drag event occurring in the instance will cause the currently selected elements to be dragged too.
There are three methods provided to work with the drag selection:
- addToDragSelection: function (elements) Add the specified elements to the drag selection.
- removeFromDragSelection: function (elements) Remove the specified elements from the drag selection
- clearDragSelection Clear the drag selection.
Here, elements
can be a single element Id or element, or an array-like object of either of these.
A posse
is a group of elements that should all move whenever one of them is dragged. This mechanism is intended to be used for more "permanent" multiple drag arrangements - you may have a set of nodes that should always move together and they do not need to be considered to be "selected".
You can define multiple posses in an instance but each element can belong to one posse only.
Any given element in a Posse has one of two types of membership: either active
, in which dragging that element causes the rest of the elements in the Posse to also be dragged, maintaining their positions relative to each other at the start of the drag; or passive
, in which the given element can be dragged independently without affecting the other elements in the Posse, but if an active
element in that Posse is dragged, the element will be dragged along with it.
Working with Posses is slightly different depending on whether you're using the Toolkit or the Community edition.
Three methods are provided for working wth Posses:
- addToPosse(elements, posse...)
Here, elements
can be a single element Id or element, or an array of either of these (or, in fact, a jQuery selector, since jsPlumb knows that selectors are "list-like"). posse
is required, and is a varargs parameter: you can provide more than one. Each posse
parameter can either be a String, indicating a Posse to which the element should be added as an active drag participant, or an object containing the Posse's ID and also whether or not the element should be an active drag participant.
For example, in the Flowchart demo, we could make these couple of calls:
jsPlumb.addToPosse(["flowchartWindow1", "flowchartWindow2"], "posse");
jsPlumb.addToPosse("flowchartWindow3", {id:"posse",active:false})
And the result would be that whenever window 1 or 2 was dragged, all of windows 1, 2 and 3 would be dragged. But if window 3 was dragged, it would be dragged alone; windows 1 and 2 would not move.
- removeFromPosse(elements, posseId)
Remove the given element(s) from Posse(s). As with addToPosse
, the second parameter is a varargs parameter: you can supply a number of posse IDs at once:
- removeFromAllPosses
Remove the given element(s) from all of the Posses to which it/they belong.
jsPlumb.removeFromAllPosses("flowchartWindow1");
jsPlumb.removeFromAllPosses(["flowchartWindow2", "flowchartWindow3"]);
Since rendering in the Toolkit is handled by the Surface, mapping node/group types to templates and behaviour, the way elements are assigned to a Posse is different to the manual way it is done in the Community Edition. In the Toolkit Edition, assignment to a Posse is handled by an optional assignPosse
function passed to a render
call:
var surface = toolkitInstance.render({
...
assignPosse:function(obj:Node|Group) {
//
// return null, or a string, or a [ name, isActive ] array
//
},
...
})
The assignPosse
function is defined as:
It is applied to both Nodes and Groups, and takes as argument the Node/Group. Its return value can be one of 3 things:
- null, indicating the Node/Group does not belong to a Posse
- a string, providing the name of the Posse to which the Node/Group should belong
- an array consisting of [ "posseName", isActive ], providing both the name of the Posse to which the Node/Group should belong and also whether or not it is an
active
member of the Posse. The default behaviour is for all members to beactive
.