🎉 JointJS has new documentation! 🥳
This is the API reference to the open source JointJS core library. If you're looking for the Rappid diagramming toolkit documentation, you can find that here.
JointJS library exports three global variables: joint
, V
and g
.
The joint
namespace contains all the objects that you will use to build your diagrams.
Additionally, joint.version
property tells you which version of JointJS you're using.
The V global is lightweight SVG library that we call Vectorizer
. This tiny library makes manipulation with SVG documents much easier. JointJS uses this library internally. Normally, you don't have to get in touch with this library at all but for advanced uses, it can be handy.
The g global is another lighweight library used internally by JointJS that provides many useful geometry operations. Again, you might not get in touch with this library but when you do have the need to perform geometric operations in your applications, you'll certainly find it helpful.
An anchor of a link is a point in the reference element that this link wants to reach as its endpoint. (In reality, the reference element is probably in the way - then, it is the job of the connection point function to determine the actual location of the route endpoint with the obstructing reference element taken into account.) Anchors are set via an anchor
property provided within link end definitions (i.e. the objects provided to link.source()
and link.target()
functions). (If the reference object is a Link, JointJS looks at linkAnchor
property instead.)
There are many built-in anchor functions in JointJS:
'center'
- default anchor at center of view bbox'modelCenter'
- anchor at center of model bbox'perpendicular'
- anchor that ensures an orthogonal route to the other endpoint'midSide'
- anchor in the middle of the side of view bbox closest to the other endpoint'bottom'
- anchor in the middle of the bottom side of view bbox'left'
- anchor in the middle of the left side of view bbox'right'
- anchor in the middle of the right side of view bbox'top'
- anchor in the middle of the top side of view bbox'bottomLeft'
- anchor at the bottom-left corner of view bbox'bottomRight'
- anchor at the bottom-right corner of view bbox'topLeft'
- anchor at the top-left corner of view bbox'topRight'
- anchor at the top-right corner of view bboxExample:
link.source(model, {
anchor: {
name: 'midSide',
args: {
rotate: true,
padding: 20
}
}
});
The default anchor function is 'center'
; this can be changed with the defaultAnchor
paper option. Example:
paper.options.defaultAnchor = {
name: 'midSide',
args: {
rotate: true,
padding: 20
}
};
JointJS also contains mechanisms to define one's own custom anchor functions.
The 'bottom'
anchor function places the anchor of the link in the middle of the bottom side of the view bbox. It accepts three arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
dx | number | Offset the anchor by dx . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
dy | number | Offset the anchor by dy . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
Example:
link.source(model, {
anchor: {
name: 'bottom',
args: {
rotate: true,
dx: 10,
dy: '40%'
}
}
});
The 'bottomLeft'
anchor function places the anchor of the link at the bottom-left corner of the view bbox. It accepts three arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
dx | number | Offset the anchor by dx . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
dy | number | Offset the anchor by dy . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
Example:
link.source(model, {
anchor: {
name: 'bottomLeft',
args: {
rotate: true,
dx: 10,
dy: '40%'
}
}
});
The 'bottomRight'
anchor function places the anchor of the link at the bottom-left corner of the view bbox. It accepts three arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
dx | number | Offset the anchor by dx . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
dy | number | Offset the anchor by dy . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
Example:
link.source(model, {
anchor: {
name: 'bottomRight',
args: {
rotate: true,
dx: 10,
dy: '40%'
}
}
});
The 'center'
anchor function is the default anchor function. It places the anchor of the link at center of the view bbox. It accepts three arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
dx | number | Offset the anchor by dx . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
dy | number | Offset the anchor by dy . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
Example:
link.source(model, {
anchor: {
name: 'center',
args: {
rotate: true,
dx: 10,
dy: '40%'
}
}
});
New anchor functions can be defined in the joint.anchors
namespace (e.g. joint.anchors.myAnchor
) or passed directly as a function to the anchor
property of link source/target (or to the defaultAnchor
option of a paper).
In either case, the anchor function must return the anchor as a Point. The function is expected to have the form function(endView, endMagnet, anchorReference, args)
:
endView | dia.ElementView | The ElementView to which we are connecting. The Element model can be accessed as endView.model ; this may be useful for writing conditional logic based on element attributes. |
---|---|---|
endMagnet | SVGElement | The SVGElement in our page that contains the magnet (element/subelement/port) to which we are connecting. |
anchorReference | g.Point | A reference to another component of the link path that may be necessary to find this anchor point. If we are calling this method for a source anchor, it is the first vertex, or if there are no vertices the target anchor. If we are calling this method for a target anchor, it is the last vertex, or if there are no vertices the source anchor... |
SVGElement | ...if the anchor in question does not exist (yet), it is that link end's magnet. (The built-in methods usually use this element's center point as reference.) | |
args | object | An object with additional optional arguments passed to the anchor method by the user when it was called (the args property). |
The 'left'
anchor function places the anchor of the link in the middle of the left side of the view bbox. It accepts three arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
dx | number | Offset the anchor by dx . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
dy | number | Offset the anchor by dy . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
Example:
link.source(model, {
anchor: {
name: 'left',
args: {
rotate: true,
dx: 10,
dy: '40%'
}
}
});
The 'midSide'
anchor function places the anchor of the link in the middle of the side of view bbox closest to the other endpoint. It accepts two arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
padding | number | Offset the anchor by padding away from view bbox. Default is 0 . |
Example:
link.source(model, {
anchor: {
name: 'midSide',
args: {
rotate: true,
padding: 20
}
}
});
The 'modelCenter'
anchor function places the anchor of the link at center of the model bbox.
Example:
link.source(model, {
anchor: {
name: 'modelCenter'
}
});
The 'perpendicular'
anchor function tries to place the anchor of the link inside the view bbox so that the link is made orthogonal. The anchor is placed along two line segments inside the view bbox (between the centers of the top and bottom side and between the centers of the left and right sides). If it is not possible to place the anchor so that the link would be orthogonal, the anchor is placed at the center of the view bbox instead. The function accepts one argument, which can be passed within the anchor.args
property:
padding | number | Limit the area inside the view bbox available for placing the anchor by padding . Default is 0 . |
---|
Example:
link.source(model, {
anchor: {
name: 'perpendicular',
args: {
padding: 10
}
}
});
When the link has no vertices, the other end cell's center is used as a reference point. By default, this means that a link using the 'perpendicular'
anchor slides
alongside the source element's edge while pointing to target element's center. To invert this behavior, and have the anchor slide
alongside the target element's edge while pointing to source element's center, pass a priority
option to the target function:
link.target(model, {
priority: true,
anchor: {
name: 'perpendicular',
args: {
padding: 10
}
}
});
The 'right'
anchor function places the anchor of the link in the middle of the right side of the view bbox. It accepts three arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
dx | number | Offset the anchor by dx . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
dy | number | Offset the anchor by dy . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
Example:
link.source(model, {
anchor: {
name: 'right',
args: {
rotate: true,
dx: 10,
dy: '40%'
}
}
});
The 'top'
anchor function places the anchor of the link in the middle of the top side of the view bbox. It accepts three arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
dx | number | Offset the anchor by dx . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
dy | number | Offset the anchor by dy . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
Example:
link.source(model, {
anchor: {
name: 'top',
args: {
rotate: true,
dx: 10,
dy: '40%'
}
}
});
The 'topLeft'
anchor function places the anchor of the link at the top-left corner of the view bbox. It accepts three arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
dx | number | Offset the anchor by dx . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
dy | number | Offset the anchor by dy . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
Example:
link.source(model, {
anchor: {
name: 'topLeft',
args: {
rotate: true,
dx: 10,
dy: '40%'
}
}
});
The 'topRight'
anchor function places the anchor of the link at the top-right corner of the view bbox. It accepts three arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
dx | number | Offset the anchor by dx . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
dy | number | Offset the anchor by dy . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
Example:
link.source(model, {
anchor: {
name: 'topRight',
args: {
rotate: true,
dx: 10,
dy: '40%'
}
}
});
A link connection point is an endpoint of the link route. This point is (usually) different from the link anchor point, as it takes into account the presence of the end element. Connection points are set via an connectionPoint
property provided within link end definitions (i.e. the objects provided to link.source()
and link.target()
functions).
The built-in functions work by finding an intersection between the link path (the path from the link's source anchor, through its vertices, to its target anchor). However, the functions always only have access to a single path segment; the source connectionPoint is found by investigating the first segment (i.e. source anchor - first vertex, or source anchor - target anchor if there are no vertices), while the target connectionPoint is found by investigating the last segment (i.e. last vertex - target anchor, or source anchor - target anchor). This has consequences if the investigated path segment is entirely contained within the end element.
There are four built-in connection point functions in JointJS:
'anchor'
- connection point at anchor'bbox'
- default connection point at bbox boundary'boundary'
- connection point at actual shape boundary'rectangle'
- connection point at unrotated bbox boundaryExample:
link.source(model, {
connectionPoint: {
name: 'boundary',
args: {
sticky: true
}
}
});
The default connection point is 'bbox'
; this can be changed with the defaultConnectionPoint
paper option. Example:
paper.options.defaultConnectionPoint = {
name: 'boundary',
args: {
sticky: true
}
};
All four of the built-in connection point functions accept the following optional argument, in addition to their own arguments:
offset | number | Offset the connection point from the anchor by the specified distance along the end link path segment. Default is 0 . |
---|
Example:
link.source(model, {
connectionPoint: {
name: 'bbox',
args: {
offset: 10
}
}
});
JointJS also contains mechanisms to define one's own custom connection point functions.
The 'anchor'
places the connection point so that it coincides with the link end's anchor point (determined either by the anchor
function or by the defaultAnchor
paper option). The position of the connection point may be modified by several additional arguments, which may be passed within the connectionPoint.args
property:
offset | number | object | An object with x and y properties. The connection point will be moved:
x offset.
|
---|---|---|
align | 'top' | 'left' | 'bottom' | 'right' | Offset the connection point to the point given by projecting the first vertex onto the vector which points from the anchor point in the direction specified. (If there are no vertices, use the projection of the other anchor point instead.) Notably, if the reference point is not the direction-most point of the two, the connection point is set to be the same as the anchor point. Let us illustrate that outcome and the other possible outcome on the `'top'` direction:
|
alignOffset | number | After having determined the position of the connection point according to the `align` algorithm (see above), additionally offset the connection point by the specified amount in the direction specified by `align`. |
Example:
link.source(model, {
connectionPoint: {
name: 'anchor',
args: {
offset: 10
}
}
});
The 'bbox'
connection point function is the default connection point function. It places the connection point at the intersection between the link path end segment and the end element bbox. The position of the connection point may be modified by several additional arguments, which may be passed within the connectionPoint.args
property:
offset | number | object | An object with x and y properties. The connection point will be moved:
x offset.
|
---|---|---|
stroke | boolean | Should the stroke width be included when calculating the connection point? Default is false . |
Example:
link.source(model, {
connectionPoint: {
name: 'bbox',
args: {
offset: 10,
stroke: true
}
}
});
The 'boundary'
connection point function places the connection point at the intersection between the link path end segment and the actual shape of the end element. (If JointJS is unable to determine the actual shape - e.g. for text - the element bbox is used instead, just as in the 'bbox'
connection point function.) The position of the connection point may be modified by several additional arguments, which may be passed within the connectionPoint.args
property:
offset | number | object | An object with x and y properties. The connection point will be moved:
x offset.
|
---|---|---|
insideout | boolean | What happens if the link path never leaves the interior area of the end element (e.g. because the other end anchor lies within the first end element)? Should the path line be extended until an intersection with the boundary is found? Default is true . |
extrapolate | boolean | What happens if the link path never enters the interior area of the end element (e.g. because the anchor lies outside the end element)? Should the path line be extended to try and find the boundary? Default is false . Note that even if this option is true , an intersection is still not guaranteed. This option takes precedence over connectionPoint.args.sticky . |
sticky | boolean | What happens if the link path never enters the interior area of the end element (e.g. because the anchor lies outside the end element)? Should the closest point on the end element boundary be used instead? Default is false . Note that setting this option to true guarantees that a connection point will be found on the shape boundary. |
precision | number | The precision of the path intersection algorithm. Uses a logarithmic scale; increasing the number by 1 reduces the maximum observed error by a factor of ten. Default is 2 , corresponding to 1% error. |
selector | string | A selector to identify subelement/magnet of the end element at whose boundary we want the connection point to be found. Default is undefined , meaning that the first non-group descendant of the end element's node will be considered. (An example of another setting that may be useful is 'root' , which forces the usage of the root group bbox instead.) |
stroke | boolean | Should the stroke width be included when calculating the connection point? Default is false . |
Example:
link.source(model, {
connectionPoint: {
name: 'boundary',
args: {
offset: 10,
insideout: false,
extrapolate: true,
sticky: true,
precision: 3,
stroke: true
}
}
});
New connection point function can be defined in the joint.connectionPoints
namespace (e.g. joint.connectionPoints.myConnectionPoint
) or passed directly as a function to the connectionPoint
property of link source/target (or to the defaultConnectionPoint
option of a paper).
In either case, the connection point function must return the connection point as a Point. The function is expected to have the form function(endPathSegmentLine, endView, endMagnet, args)
:
endPathSegmentLine | g.Line | The link path segment at which we are finding the connection point. If we are calling this method for a source connection point, it is the first segment (source anchor - first vertex, or source anchor - target anchor). If we are calling this method for a target connection point, it is the last segment (last vertex - target anchor, or source anchor - target anchor). |
---|---|---|
endView | dia.ElementView | The ElementView to which we are connecting. The Element model can be accessed as endView.model ; this may be useful for writing conditional logic based on element attributes. |
endMagnet | SVGElement | The SVGElement in our page that contains the magnet (element/subelement/port) to which we are connecting. |
args | object | An object with additional optional arguments passed to the connection point method by the user when it was called (the args property). |
The 'rectangle'
connection point function places the connection point at the intersection between the link path end segment and the element's unrotated bbox. The position of the connection point may be modified by several additional arguments, which may be passed within the connectionPoint.args
property:
offset | number | object | An object with x and y properties. The connection point will be moved:
x offset.
|
---|---|---|
stroke | boolean | Should the stroke width be included when calculating the connection point? Default is false . |
Example:
link.source(model, {
connectionPoint: {
name: 'rectangle',
args: {
offset: 10,
stroke: true
}
}
});
Connection strategies come into play when the user modifies the position of link endpoints. There are two situations in which this is relevant:
anchor
property that might have been assigned on the dragged link endpoint will be overridden by the connection strategy. If necessary, have a look at the custom connectionStrategy documentation for information on replicating the functionality of anchor functions.)Both the anchor
and connectionPoint
properties are rewritten in response to user interaction. None of the built-in connection strategies preserve the originally assigned anchor and connection point functions. To assign precisely what you need as the anchor and connection point functions, you may need to define your own custom connection strategy.
Connection strategies are not assigned on a link-by-link basis. They are set with the connectionStrategy
option on a paper.
There are three built-in connection strategies in JointJS:
useDefaults
- default strategy; ignore user pointer and use default anchor and connectionPoint functionspinAbsolute
- use user pointer coordinates to position anchor absolutely within end element; use default connectionPointpinRelative
- use user pointer coordinates to position anchor relatively within end element; use default connectionPointThe default connection strategy is specified as null
in paper settings, which is equivalent to joint.connectionStrategies.useDefaults
.
joint.connectionStrategies
namespace. Example:
paper.options.connectionStrategy = joint.connectionStrategies.pinAbsolute;
New connection strategies can be defined in the joint.connectionStrategies
namespace (e.g. joint.connectionStrategies.myConnectionStrategy
) or passed directly as a function to the connectionStrategy
option of a paper.
In either case, the connection strategy function must return an end definition (i.e. an object in the format supplied to the link.source()
and link.target()
functions). The function is expected to have the form function(endDefinition, endView, endMagnet, coords)
:
endDefinition | object | An end definition; the output of the appropriate end function (link.source() or link.target() ). An object containing at least an id of the Element to which we are connecting. This object is expected to be changed by this function and then sent as the return value. |
---|---|---|
endView | dia.ElementView | The ElementView to which we are connecting. The Element model can be accessed as endView.model ; this may be useful for writing conditional logic based on element attributes. |
endMagnet | SVGElement | The SVGElement in our page that contains the magnet (element/subelement/port) to which we are connecting. |
coords | g.Point | A Point object recording the x-y coordinates of the user pointer when the connection strategy was invoked. |
Custom connection strategies may be enormously useful for your users. Here we provide some examples of custom functionality.
If your diagram makes heavy use of nested elements, it may be useful to always connect links to a top-level ancestor element (instead of the element on which the arrowhead was actually dropped by user interaction):
joint.connectionStrategies.topAncestor = function(end, endView) {
var ancestors = endView.model.getAncestors();
var numAncestors = ancestors.length;
var end = numAncestors ? ancestors[numAncestors - 1] : end;
return end;
}
paper.options.connectionStrategy = joint.connectionStrategies.topAncestor;
If your diagram uses ports, you usually do not want links to be able to connect anywhere else. The solution is similar to the one above:
joint.connectionStrategies.firstPort = function(end, endView) {
var ports = endView.model.getPorts();
var numPorts = ports.length;
var end = numPorts ? { id: end.id, port: ports[0].id } : end;
return end;
}
paper.options.connectionStrategy = joint.connectionStrategies.firstPort;
Furthermore, it is very easy to replicate the built-in anchor functions for connection strategy scenarios - simply apply the anchor function to the received end
parameter:
joint.connectionStrategy.midSide = function(end) {
end.anchor = {
name: 'midSide',
args: {
rotate: true
}
};
return end;
}
paper.options.connectionStrategy = joint.connectionStrategy.midSide;
What if we needed to replicate a built-in connection point function instead? We use the same idea as in the previous example:
joint.connectionStrategy.boundary = function(end) {
end.connectionPoint = {
name: 'boundary',
args: {
offset: 5
}
};
return end;
}
paper.options.connectionStrategy = joint.connectionStrategy.boundary;
Of course, it is also possible to combine both of the examples and assign an anchor
as well as connectionPoint
to the end
parameter of the modified link.
The pinAbsolute
connection strategy records the coordinates of user pointer and assigns the end anchor absolutely, by reference to the top-left corner of the view bbox of the element above which the endpoint was dropped. Absolute positioning ensures that if the element is subsequently resized, the anchor stays at the same absolute distance from the edges (e.g. staying 10 pixels away from the left side and 20 pixels away from the top side).
The end connection point is assigned according to defaultConnectionPoint
paper option.
Example:
paper.options.connectionStrategy = joint.connectionStrategies.pinAbsolute;
The end (source or target) that is being modified gets the 'topLeft'
anchor assigned by this connection strategy:
end.anchor = {
name: 'topLeft',
args: {
rotate: true
dx: (coords.x - bbox.x),
dy: (coords.y - bbox.y)
}
};
The pinRelative
connection strategy records the coordinates of user pointer and assigns the end anchor relatively, by reference to the top-left corner of the view bbox of the element above which the endpoint was dropped. Relative positioning ensures that if the element is subsequently resized, the anchor stays at the same relative distance from the edges (e.g. staying 25% of the way from the left side and 75% of the way from the top side).
The end connection point is assigned according to defaultConnectionPoint
paper option.
Example:
paper.options.connectionStrategy = joint.connectionStrategies.pinRelative;
The end (source or target) that is being modified gets the 'topLeft'
anchor assigned by this connection strategy:
end.anchor = {
name: 'topLeft',
args: {
rotate: true
dx: percentageString(coords.x - bbox.x),
dy: percentageString(coords.y - bbox.y)
}
};
The useDefaults
connection strategy is the simplest connection strategy. It ignores the coordinates of user pointer and assigns the end anchor according to the defaultAnchor
paper option and the end connection point according to the defaultConnectionPoint
paper option.
Thus, this connection strategy is equivalent to a connection strategy of null
.
Example:
paper.options.connectionStrategy = joint.connectionStrategies.useDefaults;
Connectors take an array of link route points and generate SVG path commands so that the link can be rendered. The connector
property of a link can be accessed with the link.connector()
function.
There are four built-in connectors in JointJS:
'jumpover'
- connector with bridges over link intersections'normal'
- default simple connector'rounded'
- connector with rounded edges'smooth'
- connector interpolated as a bezier curve'curve'
- connector interpolating as a curve defined by ending tangentsExample:
link.connector('rounded', {
radius: 20
});
The default connector is 'normal'
; this can be changed with the defaultConnector
paper option. Example:
paper.options.defaultConnector = {
name: 'rounded',
args: {
radius: 20
}
}
All four of the built-in connectors accept the following optional argument, in addition to their own arguments:
raw | boolean | Should the router return the connection path as a g.Path rather than as a string? Default is false . |
---|
Example:
link.connector('normal', {
raw: true
});
JointJS also contains mechanisms to define one's own custom connectors.
Note that the modular architecture of JointJS allows mixing-and-matching connectors with routers as desired; for example, a link may be specified to use the 'jumpover'
connector on top of the 'manhattan'
router:
var link = new joint.shapes.standard.Link();
link.source(rect);
link.target(rect2);
link.router('manhattan');
link.connector('jumpover');
New connectors can be defined in the joint.connectors
namespace (e.g. joint.connectors.myConnector
) or passed directly as a function to the connector
property of a link (or to the defaultConnector
paper option).
In either case, the connector function must return a g.Path representing the SVG path data that will be used to render the link. The function is expected to have the form function(sourcePoint, targetPoint, routePoints, args)
:
sourcePoint | g.Point | The source connection point. |
---|---|---|
targetPoint | g.Point | The target connection point. |
routePoints | Array<g.Point> | The points of the route, as returned by the router in use. |
args | object | An object with additional optional arguments passed to the connector method by the user when it was called (the args property). |
Example of a connector defined in the joint.connectors
namespace:
joint.connectors.wobble = function(sourcePoint, targetPoint, vertices, args) {
var SPREAD = args.spread || 20;
var points = vertices.concat(targetPoint)
var prev = sourcePoint;
var path = new g.Path(g.Path.createSegment('M', prev));
var n = points.length;
for (var i = 0; i < n; i++) {
var next = points[i];
var distance = prev.distance(next);
var d = SPREAD;
while (d < distance) {
var current = prev.clone().move(next, -d);
current.offset(
Math.floor(7 * Math.random()) - 3,
Math.floor(7 * Math.random()) - 3
);
path.appendSegment(g.Path.createSegment('L', current));
d += SPREAD;
}
path.appendSegment(g.Path.createSegment('L', next));
prev = next;
}
return path;
}
var link = new joint.shapes.standard.Link();
link.source(source);
link.target(target);
link.connector('wobble', {
spread: 10
});
An example of a connector passed as a function is provided below. Notice that this approach does not enable passing custom args
nor can it be serialized with the graph.toJSON()
function.
var link = new joint.shapes.standard.Link();
link.source(source);
link.target(target);
link.connector(function(sourcePoint, targetPoint, vertices, args) {
var SPREAD = 20;
var points = vertices.concat(targetPoint)
var prev = sourcePoint;
var path = new g.Path(g.Path.createSegment('M', prev));
var n = points.length;
for (var i = 0; i < n; i++) {
var next = points[i];
var distance = prev.distance(next);
var d = SPREAD;
while (d < distance) {
var current = prev.clone().move(next, -d);
current.offset(
Math.floor(7 * Math.random()) - 3,
Math.floor(7 * Math.random()) - 3
);
path.appendSegment(g.Path.createSegment('L', current));
d += SPREAD;
}
path.appendSegment(g.Path.createSegment('L', next));
prev = next;
}
return path;
});
The 'jumpover'
connector draws straight lines with small arcs in place of link-link intersections. (For the time being, it cannot detect intersections with 'smooth'
router links). It accepts the following additional arguments, which can be passed as the connector.args
property:
size | number | The size of the jump (the diameter of the arc, or the length of the empty spot on the line). Defaults to 5 . |
---|---|---|
jump | string | The style of the jump. Either 'arc' (using an Arcto SVG path command), 'cubic' (using a Curveto path command as a normalized approximation to Arcto), or 'gap' (leaving a blank space). Defaults to 'arc' . |
radius | number | The curve radius of the rounded corners. Default is 0 . |
Example:
link.connector('jumpover', {
type: 'gap'
});
The 'normal'
connector is the default connector for links and it is the simplest connector. It simply connects provided route points with straight-line segments.
Example:
link.connector('normal');
The 'rounded'
connector connects provided route points with straight lines while smoothing all corners on the route. It accepts one additional argument, which can be passed within the connector.args
property:
radius | number | The curve radius of the rounded corners. Default is 10 . |
---|
Example:
link.connector('rounded', {
radius: 20
});
The 'smooth'
connector interpolates route points using a cubic bezier curve.
Example:
link.connector('smooth');
(Deprecated) For the purposes of backwards compatibility, the 'smooth'
connector may also be enabled by setting the link.smooth
property to true
.
// deprecated
link.set('smooth', true)
The 'curve'
connector interpolates route points using a catmull-rom curve converted into cubic bezier curve. Tangents at endings of a curve can be defined using several options.
Available options:
direction | curve.Directions | The overall direction of the curve. The default is curve.Directions.AUTO . |
---|---|---|
sourceDirection | curve.TangentDirections|object|number | The unit vector direction of the tangent at the source point. If the option is an object it must have x and y properties. If the option is a number, it represents an angle relative to the x-axis in the positive direction (counterclockwise). |
targetDirection | curve.TangentDirections|object|number | The unit vector direction of the tangent at the target point. If the option is an object it must have x and y properties. If the option is a number, it represents an angle relative to the x-axis in the positive direction (counterclockwise). |
sourceTangent | object | Vector of the tangent to the curve at the source point. Has priority over the sourceDirection and direction . Object must have x and y properties. |
targetTangent | object | Vector of the tangent to the curve at the target point. Has priority over the targetDirection and direction . Object must have x and y properties. |
distanceCoefficient | number | Coefficient of the tangent vector length relative to the distance between points. The default is 0.6 . |
The example of how to make the source point tangent always horizontal and target point tangent always go upwards:
link.connector('curve', {
direction: curve.Directions.HORIZONTAL,
targetDirection: curve.TangentDirections.UP
});
Available values for the direction
option.
curve.Directions.AUTO | Determines the tangent direction depending on the side of the element where the end point is located. |
---|---|
curve.Directions.HORIZONTAL | Limits tangents to horizontal directions (left and right) only. |
curve.Directions.VERTICAL | Limits tangents to vertical directions (up and down) only. |
curve.Directions.CLOSEST_POINT | Defines the direction tangent as a vector in the direction of the closest point. |
curve.Directions.OUTWARDS | Defines the direction tangent as a vector in from the center of the element to the end point. |
Available values for sourceDirection
and targetDirection
options.
curve.TangentDirections.AUTO | Determines the tangent direction depending on the side of the element where the end point is located. |
---|---|
curve.TangentDirections.UP | Sets the direction of the tangent upwards. |
curve.TangentDirections.DOWN | Sets the direction of the tangent downwards. |
curve.TangentDirections.LEFT | Sets the direction of the tangent to the left. |
curve.TangentDirections.RIGHT | Sets the direction of the tangent to the right. |
curve.TangentDirections.CLOSEST_POINT | Defines the direction tangent as a vector in the direction of the closest point. |
curve.TangentDirections.OUTWARDS | Defines the direction tangent as a vector from the center of the element to the corresponding end point. |
The attributes in JointJS define how the graphics elements are to be rendered inside of the element and link views.
All the standard SVG styling properties are available (both kebab-case
and camelCase
styles).
In addition JointJS modifies the behavior of existing attributes (the use of calc()
for specifying attribute values) and defines new so-called "special" attributes and allows programmers to define their own.
The calc()
function lets you perform calculations when specifying SVG attributes values.
The calc()
function takes a simple expression as its parameter, with the expression's result used as the value. The expression can be any simple expression in one of the following forms:
'calc(w)'
'calc(w + 5)'
'calc(h - 10)'
'calc(2 * w)'
'calc(0.5 * h)'
'calc(2 * w + 5)'
'calc(0.5 * h - 10)'
Where:
Variable is a symbol representing a value that can change, when the model attributes change (size, attrs).
variable | name | description |
---|---|---|
w |
width | The current width of the model (model.prop('size/width') ). The value can be bound to an SVGElement's size instead by using ref attribute. |
h |
height | The current height of the model (model.prop('size/height') ). The value can be bound to an SVGElement's size instead by using ref attribute. |
x |
x | The current x coordinate of the SVGElement in the element's coordinate system. If the attribute is not bound to a specific SVGElement with ref attribute, the value of x is always zero. |
y |
y | The current y coordinate of the SVGElement in the element's coordinate system. If the attribute is not bound to a specific SVGElement with ref attribute, the value of y is always zero. |
s |
shortest | The shortest side of the rectangle. The minimum of width and height. |
l |
longest | The longest side of the rectangle. The maximum of width and height. |
d |
diagonal | The length of the diagonal of the rectangle of size width and height. |
*
symbol.
1.5 *
+
or -
symbol followed by a number.
+ 5
+
, -
and *
operators do not require whitespace.calc()
functions, in which case the inner ones are evaluated first.
e.g 'M 0 0 H calc(w - calc(h))'
.
It can be used with the following attributes:
el.resize(200, 100); // dia.Element
// <rect joint-selector="myRect" width="200" height="100" rx="20" ry="10" />
el.attr('myRect', {
width: 'calc(w)',
height: 'calc(h)',
rx: 'calc(0.1*w)',
ry: 'calc(0.1*h)'
});
// <image joint-selector="myImage" x="105" y="55" />
el.attr('myImage/x', 'calc(0.5*w+5)');
el.attr('myImage/y', 'calc(0.5*h+5)');
// <path joint-selector="myPath" d="M 10 50 190 50" />
el.attr('myPath/d', 'M 10 calc(0.5*h) calc(w-10) calc(0.5*h)')
// <polygon joint-selector="myPolygon" points="0,0 200,0 200,100 0,100" />
el.attr('myPolygon/d', '0,0 calc(w),0 calc(w),calc(h) 0,calc(h)');
// Resize the rectangle to match the text size with extra 5 pixels of padding
// <rect joint-selector="myTextBackground" />
// <text joint-selector="myText" >Some text</text>
el.attr('myTextBackground', {
ref: 'myText',
x: 'calc(x - 5)',
y: 'calc(y - 5)'
width: 'calc(w + 10)',
height: 'calc(h + 10)',
});
Here is the list of all built-in attributes.
Move the subelement to the point at a given distance along the connection path but do not auto-orient it according to the path's gradient. Use a positive number to define the distance from the source of the link and a negative number from the target of the link. It is valid only within the LinkView context.
link.attr('rectSelector', { atConnectionLengthIgnoreGradient: 30, width: 10, height: 10, fill: 'red' });
alias: atConnectionLength
Move and auto-orient the subelement to the point at a given distance along the connection path. Use a positive number to define the distance from the source of the link and a negative number from the target of the link. It is valid only within the LinkView context.
link.attr('rectSelector', { atConnectionLength: 30, width: 10, height: 10, fill: 'red' });
link.attr('rectSelector', { atConnectionLengthKeepGradient: 30, width: 10, height: 10, fill: 'red' });
Move the subelement to the point at a given ratio of the connection total length but do not auto-orient it according to the path's gradient. It accepts a number in the [0,1]
range. It is valid only within the LinkView context.
link.attr('rectSelector', { atConnectionRatioKeepGradient: .5, width: 10, height: 10, fill: 'red' });
alias: atConnectionRatio
Move and auto-orient the subelement to the point at a given ratio of the connection total length. It accepts a number in the [0,1]
range. It is valid only within the LinkView context.
link.attr('rectSelector', { atConnectionRatio: .5, width: 10, height: 10, fill: 'red' });
link.attr('rectSelector', { atConnectionRatioKeepGradient: .5, width: 10, height: 10, fill: 'red' });
If true
, set the shape of the subelement to match the shape of the LinkView (set the 'd'
attribute to the result of the link connector). It's valid only for SVGPathElement
within the LinkView context.
You can also provide an object with the following options:
Name | Type | Description |
---|---|---|
stubs | Number | If provided, display only the beginning and end stubs of the connection and hide the remaining central section. A positive value determines the length of each stub. If the value is negative, it determines the length of the hidden section of the connection between the two stubs. |
link1.attr('pathSelector', { connection: true, stroke: 'red', fill: 'none' });
link2.attr('pathSelector', { connection: { stubs: -20 }});
Designate another subelement of the cell as a proxy target for the cell's container, whenever this subelement (the one on which this attribute is defined) becomes the target of an embedding. (Usually, this attribute needs to be defined on the `'root'` subelement of the cell, since that is the default target for the cell's container.) Expects a selector.
A change in the cell's container affects the following characteristics of the cell view:
// `standard.Rectangle` has `root`, `body` and `label` subelements
const rect = new joint.shapes.standard.Rectangle();
// we want to have the `label` on the outside and under the `body` of the rectangle:
rect.attr(['label'], { refY: '100%', textVerticalAnchor: 'top' });
// normally, when a user tries to embed a child to the rectangle, the `root` subelement would be highlighted (the wrapper of `body` and `label`)
// however, we want only the `body` to be highlighted, so we need to specify it as a proxy for `root`:
rect.attr(['root', 'containerSelector'], 'body');
Valid only for <text>
subelements.
If set to true
, the SVGTextElement with empty content will be rendered. It's useful when the text needs to be editable.
The event
attribute makes the selected node and its descendants trigger an arbitrary event when clicked (mousedown/touchstart).
This event is triggered on the view itself and the paper.
The paper handler is called with the signature cellView
, evt
, x
, y
,
while the cell view handler is called only with evt
, x
, y
.
element.attr({
image: {
// pointerdown on the image SVG node will trigger the `element:delete` event
event: 'element:delete',
xlinkHref: 'trash.png'
width: 20,
height: 20
}
});
// Binding handler to the event
paper.on('element:delete', function(elementView, evt) {
// Stop any further actions with the element view e.g. dragging
evt.stopPropagation();
if (confirm('Are you sure you want to delete this element?')) {
elementView.model.remove();
}
});
The fill
attribute becomes a special attribute only in case it's defined as an object, instead of the usual SVG syntax (e.g. "#ffaabb"
).
If it's defined as an object, it is assumed to be either a gradient or pattern definition. It must have the form defined by the defineGradient() (resp. definePattern()) paper method.
element.attr('rect/fill', {
type: 'linearGradient',
stops: [
{ offset: '0%', color: '#E67E22' },
{ offset: '20%', color: '#D35400' },
{ offset: '40%', color: '#E74C3C' },
{ offset: '60%', color: '#C0392B' },
{ offset: '80%', color: '#F39C12' }
]
});
element.attr('rect/fill', {
type: 'pattern',
attrs: {
width: 10,
height: 10
},
markup: [{
tagName: 'polygon',
attributes: {
points: '0,0 2,5 0,10 5,8 10,10 8,5 10,0 5,2'
}
}]
});
The filter
attribute becomes a special attribute only in case it's defined as an object, instead of the usual SVG syntax (e.g. "url(#myfilter)"
).
If it's defined as an object, it must have the form defined by by the filterGradient() paper method.
element.attr('rect/filter', {
name: 'dropShadow',
args: {
dx: 2,
dy: 2,
blur: 3
}
});
Designate another subelement of the cell as a proxy target for the cell's magnet, whenever this subelement (the one on which this attribute is defined) becomes the source/target of a link. (Usually, this attribute needs to be defined on the `'root'` subelement of the cell, since that is the default target for the cell's magnet.) Expects a selector.
A change in the cell's magnet affects the following characteristics of the cell view:
It has no effect on the connection validation and link model attributes.
// `standard.Rectangle` has `root`, `body` and `label` subelements
const rect = new joint.shapes.standard.Rectangle();
// we want to have the `label` on the outside and under the `body` of the rectangle:
rect.attr(['label'], { refY: '100%', textVerticalAnchor: 'top' });
// normally, highlighters would appear around the `root` subelement (the wrapper of `body` and `label`)
// however, we want highlighters to highlight only to the `body` subelement, so we need to specify it as a proxy for `root`:
rect.attr(['root','highlighterSelector'], 'body');
When set to true
, the subelement can become the source/target of a link during link reconnection. Useful for
so called 'ports'. When set to magnet: 'passive'
, a new link won't be added to the graph when a user clicks
on the respective magnet. This use case is common for the creation of 'input' ports.
const paper = new joint.dia.Paper({
// Other Paper options
// The default behaviour of magnet validation
validateMagnet: function(_cellView, magnet, _evt) {
return magnet.getAttribute('magnet') !== 'passive';
}
});
const portIn = {
label: {
// Label position and markup
},
attrs: {
portBody: {
// Other portBody attributes
magnet: 'passive' // No link added upon user click
},
label: { text: 'port' }
},
markup: [{
tagName: 'rect',
selector: 'portBody'
}]
};
Designate another subelement of the cell as a proxy target for the cell's magnet, whenever this subelement (the one on which this attribute is defined) becomes the source/target of a link. (Usually, this attribute needs to be defined on the `'root'` subelement of the cell, since that is the default target for the cell's magnet.) Expects a selector.
A change in the cell's magnet affects the following characteristics of the cell view:
It has no effect on the connection validation, magnet highlighter and the source
and target
link model attributes.
// `standard.Rectangle` has `root`, `body` and `label` subelements
const rect = new joint.shapes.standard.Rectangle();
// we want to have the `label` on the outside and under the `body` of the rectangle:
rect.attr(['label'], { refY: '100%', textVerticalAnchor: 'top' });
// normally, links would connect to the `root` subelement (the wrapper of `body` and `label`)
// however, we want links to connect only to the `body` subelement, so we need to specify it as a proxy for `root`:
rect.attr(['root','magnetSelector'], 'body');
An object containing at least an id
property. This property uniquely identifies the port.
If a link gets connected to a magnet that has also a port
object defined, the id
property of the port object will be copied to the port
property
of the source/target of the link.
Markup or CSS selector pointing to an element that is used as a reference for relative positioning attributes.
Set cx
attribute of the subelement relatively to the width of the element referenced to by the selector in ref
attribute.
If the value is in the [0, 1]
interval (or expressed in percentages, e.g. '80%'
), the cx
of the subelement will be set as a percentage
of the width of the referenced element. If the value is <0
or >1
, the height
of the subelement will be smaller/bigger than the width of the referenced element by the amount specified.
Note that this makes sense only for SVG elements that support rx
and ry
attributes, such
as <ellipse>
.
Set cy
attribute of the subelement relatively to the height of the element referenced to by the selector in ref
attribute.
If the value is in the [0, 1]
interval (or expressed in percentages, e.g. '80%'
), the cy
of the subelement will be set as a percentage
of the height of the referenced element. If the value is <0
or >1
, the height
of the subelement will be smaller/bigger than the height of the referenced element by the amount specified.
Note that this makes sense only for SVG elements that support rx
and ry
attributes, such
as <ellipse>
.
Set d
attribute of the <path>
subelement relatively to the dimensions and position of the element referenced by the selector in the ref
attribute. The refD
path data is scaled so that the path's dimensions match the reference element's dimensions, and translated so that the path's origin matches the origin of the reference element.
The original path data offset is preserved. This means that if the top-left corner of the refD
bounding box does not lie at 0,0
, the gap between the path and origin is preserved in the rendered shape, as well.
var Path = joint.dia.Element.define('examples.Path', {
attrs: {
path: {
refDKeepOffset: 'M 10 10 30 10 30 30 z', // path offset is 10,10
fill: 'red',
stroke: 'black'
}
}
}, {
markup: 'path'
});
var p = (new Path()).resize(40, 40).addTo(graph);
// the rendered path's `d` attribute will be 'M 10 10 50 10 50 50 z'
// can be obtained by `p.findView(paper).vel.findOne('path').attr('d');`
alias: refD
Set d
attribute of the <path>
subelement relatively to the dimensions and position of the element referenced by the selector in the ref
attribute. The refD
path data is scaled so that the path's dimensions match the reference element's dimensions, and translated so that the path's origin matches the origin of the reference element.
The original path data offset is not preserved. This means that if the top-left corner of the refD
bounding box does not lie at 0,0
, the path is translated so that this gap disappears. The rendered path then fits perfectly into the reference element's bounding box.
var Path = joint.dia.Element.define('examples.Path', {
attrs: {
path: {
refDResetOffset: 'M 10 10 30 10 30 30 z', // path offset of 10,10 will be discarded
fill: 'red',
stroke: 'black'
}
}
}, {
markup: 'path'
});
var p = (new Path()).resize(40, 40).addTo(graph);
// the rendered path's `d` attribute will be 'M 0 0 40 0 40 40 z'
// can be obtained by `p.findView(paper).vel.findOne('path').attr('d');`
alias: ref-dx
Make x-coordinate of the subelement relative to the right edge of the element referenced to by the selector in ref
attribute.
Make y-coordinate of the subelement relative to the bottom edge of the element referenced to by the selector in ref
attribute.
alias: ref-height
Set height of the subelement relatively to the height of the element referenced to by the selector in ref
attribute.
If the value is in the [0, 1]
interval (or expressed in percentages, e.g. '80%'
), the height of the subelement will be set as a percentage
of the height of the referenced element. If the value is <0
or >1
, the height
of the subelement will be smaller/bigger than the height of the referenced element by the amount specified.
Note that this makes sense only for SVG elements that support width
and height
attributes, such
as <rect>
.
Same as refHeight
. Useful when one needs to resize an element absolutely and relatively at the same time. Note that all percentages are relative to 100% of the referenced height.
{ refHeight: 20, refHeight2: '-75%' } // resizes the element to 25% of the reference height (25% = 100% - 75%) plus extra 20 pixels
Set the points
attribute of the <polygon>
or <polyline>
subelement relatively to the dimensions and position of the element referenced by the selector in the ref
attribute. The refPoints
are scaled so that the subelement's dimensions match the reference element's dimensions, and translated so that the their origin matches the origin of the reference element.
The original points offset is preserved. This means that if the top-left corner of the refPoints
bounding box does not lie at 0,0
, the gap between the points and origin is preserved in the rendered shape, as well.
var Polygon = joint.dia.Element.define('examples.Polygon', {
attrs: {
polygon: {
refPoints: '10,10 30,10 30,30', // points offset is 10,10
fill: 'red',
stroke: 'black'
}
}
}, {
markup: 'polygon'
});
var p = (new Polygon()).resize(40, 40).addTo(graph);
// the rendered polygon's `points` attribute will be '10,10 50,10 50,50'
// can be obtained by `p.findView(paper).vel.findOne('polygon').attr('points');`
alias: refPoints
Set the points
attribute of the <polygon>
or <polyline>
subelement relatively to the dimensions and position of the element referenced by the selector in the ref
attribute. The refPoints
are scaled so that the subelement's dimensions match the reference element's dimensions, and translated so that the their origin matches the origin of the reference element.
The original points offset is not preserved. This means that if the top-left corner of the refPoints
bounding box does not lie at 0,0
, the subelement is translated so that this gap disappears. The rendered subelement then fits perfectly into the reference element's bounding box.
var Polygon = joint.dia.Element.define('examples.Polygon', {
attrs: {
polygon: {
refPoints: '10,10 30,10 30,30', // points offset of 10,10 will be discarded
fill: 'red',
stroke: 'black'
}
}
}, {
markup: 'polygon'
});
var p = (new Polygon()).resize(40, 40).addTo(graph);
// the rendered polygon's `points` attribute will be '0,0 40,0 40,40'
// can be obtained by `p.findView(paper).vel.findOne('polygon').attr('d');`
Set the r
attribute of the subelement relatively to the circumscribed size (length of the diagonal of the bounding box) of the element referenced by the selector in the ref
attribute. If the value is in the [0, 1]
interval (or expressed in percentages, e.g. '80%'
), the r
of the subelement will be set as a percentage of the size of the referenced element. If the value is <0
or >1
, the size of the subelement will be smaller/bigger than the size of the referenced element by the amount specified. Note that this makes sense only for SVG elements that support r
attribute, such as <circle>
.
alias: refR
Set the r
attribute of the subelement relatively to the inscribed size (width or height of the bounding box, whichever is smaller) of the element referenced by the selector in the ref
attribute. If the value is in the [0, 1]
interval (or expressed in percentages, e.g. '80%'
), the r
of the subelement will be set as a percentage of the size of the referenced element. If the value is <0
or >1
, the size of the subelement will be smaller/bigger than the size of the referenced element by the amount specified. Note that this makes sense only for SVG elements that support r
attribute, such as <circle>
.
Set rx
attribute of the subelement relatively to the width of the element referenced to by the selector in ref
attribute.
If the value is in the [0, 1]
interval (or expressed in percentages, e.g. '80%'
), the rx
of the subelement will be set as a percentage
of the width of the referenced element. If the value is <0
or >1
, the height
of the subelement will be smaller/bigger than the width of the referenced element by the amount specified.
Note that this makes sense only for SVG elements that support rx
and ry
attributes, such
as <ellipse>
.
Set ry
attribute of the subelement relatively to the height of the element referenced to by the selector in ref
attribute.
If the value is in the [0, 1]
interval (or expressed in percentages, e.g. '80%'
), the ry
of the subelement will be set as a percentage
of the height of the referenced element. If the value is <0
or >1
, the height
of the subelement will be smaller/bigger than the height of the referenced element by the amount specified.
Note that this makes sense only for SVG elements that support rx
and ry
attributes, such
as <ellipse>
.
alias: ref-width
Set width of the subelement relatively to the width of the element referenced to by the selector in ref
attribute.
If the value is in the [0, 1]
interval (or expressed in percentages, e.g. '80%'
), the width of the subelement will be set as a percentage
of the width of the referenced element. If the value is <0
or >1
, the width
of the subelement will be smaller/bigger than the width of the referenced element by the amount specified.
For example, 'ref-width': .75
sets the width of the subelement to 75%
of the width of the referenced element.
'ref-width': 20
makes the width to be 20px
less than the width of the referenced element.
Note that this makes sense only for SVG elements that support width
and height
attributes, such
as <rect>
.
Same as refWidth
. Useful when one needs to resize an element absolutely and relatively at the same time. Note that all percentages are relative to 100% of the referenced width.
{ refWidth: 20, refWidth2: '-75%' } // resizes the element to 25% of the reference width (25% = 100% - 75%) plus extra 20 pixels
alias: ref-x
Make x-coordinate of the subelement relative to the x-coordinate of the element referenced to by the selector in ref
attribute.
If ref-x
is in the (0,1)
interval (or expressed in percentages, e.g. '80%'
), the offset is
calculated from the fraction of the bounding box of the referenced element. Otherwise, it is an absolute value in pixels.
Same as refX
. Useful when one needs to position an element absolutely and relatively at the same time.
{ refX: '50%', refX2: 20 } // moves the element by 50% of the referenced width plus extra 20 pixels.
alias: ref-y
Make y-coordinate of the subelement relative to the y-coordinate of the element referenced to by the selector in ref
attribute.
If ref-y
is in the (0,1)
interval (or expressed in percentages, e.g. '80%'
), the offset is
calculated from the fraction of the bounding box of the referenced element. Otherwise, it is an absolute value in pixels.
Same as refY
. Useful when one needs to position an element absolutely and relatively at the same time.
{ refY: '50%', refY2: 20 } // moves the element by 50% of the referenced height plus extra 20 pixels.
If set to true
, the subelement offset (the distance from x0 y0 to the most top-left point) will be reset to x0 y0.
element.attr({
path: {
// The path bbox for `d="M 10 10 20 20"` is x10 y10 w10 h10.
d: 'M 10 10 20 20',
// The path bbox will be changed to x0 y0 w10 h10.
// This has same effect as passing path with `d="M 0 0 10 10"`
resetOffset: true
}
});
Valid for <path>
subelements. It draws an SVG element at the beginning of a path (the source of the link). The element is automatically rotated based on the path direction.
It must have the form defined by the defineMarker() paper method.
link.attr('line/sourceMarker', {
type: 'circle', // SVG Circle
fill: '#666',
stroke: '#333',
r: 5, // radius of the circle
cx: 5 // move the centre of the circle 5 pixels from the end of the path
});
The stroke
attribute becomes a special attribute only in case it's defined as an object. This has the exact same
behaviour as the fill attribute.
An object containing CSS styles for a subelement.
Valid for <path>
subelements. The same as sourceMarker but draws an SVG element at the end of the path.
Note the coordinate system for drawing is rotated by 180 degrees for convenience (this allows to use the same marker for source and target and have them both face the connected elements).
Valid only for <text>
subelements.
The text
attribute contains the text that should be set on the subelement. If the provided string does not contain any newline characters ('\n'
), the text is set directly as the content of the <text>
subelement. Alternatively, if the provided string has multiple lines, each line becomes the content of a <tspan>
child of the <text>
subelement.
If you need the text to be automatically wrapped inside the <text>
subelement, you should use the textWrap
attribute instead. It can automatically add line breaks into the provided string as necessary, and mark them with newline characters.
Valid only for <text>
subelements.
textPath
can be either a string or an object.
If it is a string, it specifies a path the text will be rendered along.
If it is an object, then it can contain a d
property that specifies the path the text will be rendered along.
Alternatively use selector
property, which is useful for targeting a specific SVGPathElement within the shape. The option expects a string selector (CSS or JSONMarkup selector).
Use startOffset
property to control the text position on the path (e.g. '50%'
, 20
). See the Vectorizer.text
method for more details.
Valid only for <text/>
subelements. The attribute is used to (top-, middle- or bottom-) align a text, relative to a point determined by reference to provided x
, y
, refX
, refY
, and similar attributes. See the Vectorizer.text
method for more details.
Valid only for <text>
subelements.
Similar to the text
attribute, except the provided string is automatically wrapped to fit within the reference bounding box.
Expects an object with a text
property and optional width
and height
, which can adjust the final size of the wrapped text. Negative values decrease the dimension (e.g. to add padding around the wrapped text); positive values increase the dimension. Percentage values are accepted as well.
If the text cannot fit into the bounding box as specified, the overflowing words are cut off. If the ellipsis
option is provided, an ellipsis string is inserted before the cutoff. If no words can fit into the bounding box at all, no text is inserted. See util.breakText for more info.
textWrap: {
text: 'lorem ipsum dolor sit amet consectetur adipiscing elit',
width: -10, // reference width minus 10
height: '50%', // half of the reference height
ellipsis: true // could also be a custom string, e.g. '...!?'
}
Add the text-only description in form of a <title>
element to the associated node. The title is not rendered as part of the graphics, but it's displayed as a tooltip.
element.attr('rect/title', 'Description of the rectangle');
Valid for <path>
subelements. The same as sourceMarker but draws SVG elements at every vertex of the path.
alias: x-alignment
If set to 'middle'
, the subelement will be centered around its new x-coordinate.
alias: y-alignment
If set to 'middle'
, the subelement will be centered around its new y-coordinate.
The basic model for diagram cells. It's a Backbone model with a few additional properties and methods. Most importantly, every cell has a unique ID that is stored in the id
property.
define(type [, defaultAttributes, prototypeProperties, staticProperties])
Helper to define a new Cell class or extend an existing one.
The type
must be a unique identifier of the class, which determines the location of the class definition in the joint.shapes
namespace (type
is the path to the class definition delimited by dots: .
). When creating an instance of the cell, attributes will be set to the value from the defaultAttributes
, unless overridden by subclass or instance attributes.
// Define a new Ellipse class in `joint.shapes.examples` namespace
// Inherits from generic Element class
var Ellipse = joint.dia.Element.define('examples.Ellipse', {
// default attributes
markup: [{
tagName: 'ellipse',
selector: 'ellipse' // not necessary but faster
],
attrs: {
ellipse: {
fill: 'white',
stroke: 'black',
strokeWidth: 4,
refRx: .5,
refRy: .5,
refCx: .5,
refCy: .5
}
}
});
// Instantiate an element
var ellipse = (new Ellipse()).position(100, 100).size(120, 50).addTo(graph);
// Define a new ColoredEllipse class
// Inherits from Ellipse
var ColoredEllipse = Ellipse.define('examples.ColoredEllipse', {
// overridden Ellipse default attributes
// other Ellipse attributes preserved
attrs: {
ellipse: {
fill: 'lightgray'
}
}
}, {
// prototype properties
// accessible on `this.(...)` - as well as, more precisely, `this.prototype.(...)`
// useful for custom methods that need access to this instance
// shared by all instances of the class
randomizeStrokeColor: function() {
var randomColor = '#' + ('000000' + Math.floor(Math.random() * 16777215).toString(16)).slice(-6);
return this.attr('ellipse/stroke', randomColor);
}
}, {
// static properties
// accessible on `this.constructor.(...)`
// useful for custom methods and constants that do not need an instance to operate
// however, a new set of static properties is generated every time the constructor is called
// (try to only use static properties if absolutely necessary)
createRandom: function() {
return (new ColoredEllipse()).randomizeStrokeColor();
}
});
// Instantiate an element
var coloredEllipse = ColoredEllipse.createRandom().position(300, 100).size(120, 50).addTo(graph);
markup
Either an XML string or JSON markup specifying an array of JSON elements. Used as a template to build DOM Elements on the fly when the associated cellView is rendered.
A valid XML string that contains either a single tagName
or XML that can be parsed with DOMParser
.
markup: 'rect'
markup: '<rect class="rectangle"/>'
markup: '<rect><g><circle/><circle/></g>'
Note that defining Cell markup with XML strings is slower than defining it with JSON arrays, precisely because of the need for parsing. We strongly recommend you to use the JSON markup for your Cell definitions.
JSON markup is defined recursively as an array of JSONElement
, where JSONElement
is a plain object with the following properties:
tagName
(string) (required) The type of element to be created.selector
(string) A unique selector for targeting the element within the attr
cell attribute.groupSelector
(string | string[]) A selector for targeting multiple elements within the attr
cell attribute. The group selector name must not be the same as an existing selector name.namespaceURI
(string) The namespace URI of the element. It defaults to SVG namespace "http://www.w3.org/2000/svg"
.attributes
(object with attributes name-value pairs) Attributes of the element.style
(object with CSS property-value pairs) The style
attribute of the element.className
(string) The class
attribute of the element.children
(JSONMarkup) The children of the element.textContent
(string) The text content of the element.// single DOM element
markup: [{ tagName: 'rect' }]
// multiple DOM elements
markup: [{
tagName: 'rect',
selector: 'body'
}, {
tagName: 'text',
selector: 'label',
attributes: {
'stroke': 'none'
}
}]
// nested DOM elements
markup: [{
tagName: 'g',
children: [{
tagName: 'circle',
selector: 'circle1',
groupSelector: 'circles'
}, {
tagName: 'circle',
selector: 'circle2',
groupSelector: 'circles'
}]
}]
Anything you define in markup
is evaluated once at CellView creation (the DOM elements and their attributes).
That means it's important to think about the runtime of your application. If you have SVG attributes that don't change
throughout the runtime, you can add them to the markup
.
As markup
is something all instances of the element type are expected to have in common, inheriting from
the subtype prototype is more efficient. Nevertheless, it is still possible to provide custom markup to individual instances
of your class by providing markup later.
Anything in the attrs
attribute is evaluated on every change of the model (e.g. a resize
or
an attrs
change). As JointJS
special attributes mostly depend on the current state of the model (size
, attrs
, angle
),
they should always be defined inside attrs
.
SVG attributes that are modified at some point in the application should also be added to attrs
(e.g. user
changes the color of the element).
joint.dia.Element.define('standard.Rectangle', {
attrs: {
body: {
width: 'calc(w)',
height: 'calc(h)',
stroke: 'red',
fill: '#333333' // We wan't to modify the fill color of our instances
},
label: {
// attributes
}
}
}, {
markup: [{
tagName: 'rect',
selector: 'body',
attributes: {
'stroke-width': 2 // Use native SVG kebab-case for attributes in markup
}
}, {
tagName: 'text',
selector: 'label',
attributes: {
'stroke': 'none' // We don't need any instances to have a stroke value for text
}
}]
});
selector
and groupSelector
.
selector
is unique i.e. can target a single node only.selector
takes precedence over groupSelector
.groupSelector
targeting n
nodes takes precedence over groupSelector
targeting m
nodes for n
< m
. When n
=== m
the order how the attributes are applied is unspecified.In the example below, the circle with the selector circle1
is filled with "red"
color. All the other circles are filled with "blue"
color.
cell.attr({
circle1: { fill: 'red' },
circles: { fill: 'blue', stroke: 'black' }
});
cell.getParentCell()
Return the parent cell of cell
or null
if there is none.
cell.isElement()
Always returns false
. The reason the method is here is that both joint.dia.Element
and joint.dia.Link
inherit from joint.dia.Cell
. This method is useful if you don't know what the cell is. Calling cell.Element()
is equivalent to cell instanceof joint.dia.Element
. Example:
var cell = graph.getCell(myId)
if (cell.isElement()) {
// Do something if the cell is an element.
}
element.isLink()
Always returns false
. The reason the method is here is that both joint.dia.Element
and joint.dia.Link
inherit from joint.dia.Cell
. This method is useful if you don't know what the cell is. Calling cell.isLink()
is equivalent to cell instanceof joint.dia.Link
. Example:
var cell = graph.getCell(myId)
if (cell.isLink()) {
// Do something if the cell is a link.
}
cell.parent()
Return the parent
property of the cell.
If the cell is a part of an embedding, the id
of the parent cell is returned as a string.
If you need to be sure that a Cell is returned (and not its id
), use the cell.getParentCell
function instead.
cell.parent(parentId [, opt])
Set the parent
property of the cell to parentId
provided.
The view for the joint.dia.Cell model. It inherits from Backbone.View and is responsible for:
To find the view associated with a specific cell (model), use the findViewByModel method of the paper. For example:
var cellView = paper.findViewByModel(cell);
When you define a custom CellView, which listens to the underlying model changes and update itself (modifying its sub-elements directly) you should follow the instructions below in order to make the view work correctly in the async mode.
Note, that when a view needs an update (a model attribute has changed e.g. size
has changed), it requests the update from the paper first. The paper confirms the update immediately (sync
mode) or in the next animation frame (async
mode). The updates may be also held by the paper as long as the paper is frozen and released when the paper changes its state to unfrozen.
The update requests are sent to the paper via flags
and later received back all at once. The paper accumulates flags received and confirms the updates when the right time has come.
flagLabel
- is an arbitrary string or array of strings
flags
- are encoded flag labels
CellView.prototype.presentationAttributes
- is an object that maps model attributes to flag labels.
CellView.prototype.confirmUpdate(flags, opt)
- This method receives all scheduled flags and based on them updates the view
CellView.prototype.hasFlag(flags, flagLabel)
- Checks whether a flagLabel
is present in the current update.
CellView.addPresentationAttributes(presentationAttributes)
- Extends the CellView presentation attributes with another presentationAttributes
. - The method makes sure the original CellView attributes are preserved. It returns a new object with all presentation attributes.
const FadeView = joint.dia.ElementView.extend({
// Make sure that all super class presentation attributes are preserved
presentationAttributes: joint.dia.ElementView.addPresentationAttributes({
// mapping the model attributes to flag labels
faded: 'flag:opacity'
}),
confirmUpdate(flags, ...args) {
joint.dia.ElementView.prototype.confirmUpdate.call(this, flags, ...args);
if (this.hasFlag(flags, 'flag:opacity')) this.toggleFade();
},
toggleFade() {
this.el.style.opacity = this.model.get('faded') ? 0.5 : 1;
}
});
cellView.findAttribute(attribute, node)
Return the value of the specified attribute
of node
.
If node
does not set a value for attribute
, start recursing up the DOM tree from node
to look for attribute
at the ancestors of node
. If the recursion reaches CellView's own node and attribute
is not found even there, return null
.
cellView.findBySelector([selector, root, selectors])
Return an array of subelements of this CellView which are identified by selector
. If no such subelements are found, return an empty array.
This method mimics jQuery element search methods in that it always returns an array. If you are only expecting to find a single subelement (most common scenario), you can get it by accessing the first element in the returned array – e.g. this.findBySelector('body')[0]
.
If you do not specify a selector
, the root
of the CellView is returned as the only element of the return array. For backwards compatibility, the same applies also if you provide the special value selector = '.'
. By default root = this.el
(the wrapping <g>
SVGElement of the CellView), but if you provide a different root
(see below), then that value is returned instead.
By default, JointJS searches for matching subelements among descendants of this.el
SVGElement. As an advanced feature, you may provide a different root
– e.g. an el
SVGElement of another CellView. Then JointJS searches for matching subelements among the descendants of that SVGElement.
By default, JointJS uses this.selectors
array for reference. (The selectors
array is a cached structure of this CellView's SVGElement descendants as defined by the markup
attribute of the Cell model from which the CellView was initialized.) As an advanced feature, you may provide a different selectors
array for the search – e.g. a selectors
array of another CellView.
(Deprecated) For backwards compatibility, if no matching subelement is found among selectors
, then jQuery is used to try and find an element through CSS selectors ($(root).find(selector).toArray()
). If you are using standard shapes, you may disable this functionality for increased performance by setting useCSSSelectors = false
in the global config.
cellView.findPortNode(portId)
Return the port element of this CellView that is identified by portId
(i.e. the SVGElement within this.el
which has 'port': portId
).
If the CellView does not have a port identified by portId
, return null
.
cellView.findPortNode(portId, selector)
If selector
is also specified, return the subelement of the port element that is identified by selector
, according to the rules of the cellView.findBySelector()
function.
If the port element does not have a subelement that matches selector
, return null
.
cellView.highlight([el[, options]])
Add a highlighter to the cell view.
When the method is called, the cell:highlight
paper event is triggered.
Arguments:
el
will be usedname
cellView.unhighlight([el[, options]])
Removes a highlighter added to the cell view.
When the method is called, the cell:unhighlight
paper event is triggered.
The basic model for diagram elements. It inherits from joint.dia.Cell with a few additional properties and methods specific to elements. For a quick introduction to elements, see our tutorial.
Elements' properties may be split into several groups according to their function:
position
- coordinates of the element, provided as an object with x
and y
keys. The property may be accessed or set directly using regular Backbone set('position')
/get('position')
methods or through Element's translate()
method.angle
- angle of rotation of the element in degrees. The rotation origin is always the center of the element. The property may be accessed or set directly using regular Backbone set('angle')
/get('angle')
methods or through Element's rotate()
method.size
- size of the element, provided as an object with width
and height
keys. The property may be accessed or set directly using regular Backbone set('size')
/get('size')
methods or through Element's translate()
method.Each joint.dia.Element
defines its own SVG markup
which is then used by joint.dia.ElementView
to render the element to the paper. For instance, the joint.shapes.standard.Rectangle
element (which inherits from joint.dia.Element
) defines its markup using the JSON array notation as follows:
markup: [{
tagName: 'rect',
selector: 'body',
}, {
tagName: 'text',
selector: 'label'
}]
As we can see, the joint.shapes.standard.Rect
shape consists of two subelements
: one SVGRectElement named 'body'
and one SVGTextElement named 'label'
. The attrs
object refers to the subelements' names (selectors
) to provide SVG attributes to these constituent SVGElements.
The keys of the attrs
object are selectors that match subelements defined in the element's markup
. The values of this object are SVG attributes that will be set on the selected subelements. One can find the full list of SVG attributes and their descriptions online, e.g. on MDN.
For example, in order to set a red fill color on a subelement called 'body'
, the attrs
object would contain:
body: { fill: 'red' }
If you simply need to change a value of an attribute, it is not recommended to modify the attrs
object directly. Instead, use the element.attr()
method.
We can use the joint.shapes.standard.Rectangle
element (which inherits from joint.dia.Element
) as an example. The attrs
object in its definition is provided below:
attrs: {
body: {
refWidth: '100%',
refHeight: '100%',
strokeWidth: 2,
stroke: '#000000',
fill: '#FFFFFF'
},
label: {
textVerticalAnchor: 'middle',
textAnchor: 'middle',
refX: '50%',
refY: '50%',
fontSize: 14,
fill: '#333333'
}
}
Notice that the object makes use of special JointJS attributes (e.g. refWidth
, textVerticalAnchor
) on top of standard SVG attributes (e.g. stroke
, fill
). All of these special attributes
are listed in the attributes section of this documentation. You should also refer to our tutorial on special attributes.
Attributes defined in markup
are evaluated once at CellView creation,
while attributes defined in attrs
are evaluated on every model change. As JointJS special attributes usually depend on the
current state of the model, we should define them in attrs
, along with any other SVG attributes that will be modified
in the runtime of your application.
The z
property specifies the stack order of the element in the SVG DOM. An element with a higher z
value will be rendered in front of an element with a lower z
value. (This also applies to Links.)
You may define and group ports on the Element with the ports
attribute. Each can have its own markup and attrs, and its own label. Grouped ports may share properties and behaviors. See the element ports documentation for more information.
The last two properties of elements are embeds
and parent
. These two are related to elements that contain or are contained within other elements, forming a hierarchical structure.
embeds
- a list of id
's of the Cells that are embedded inside this Element.parent
- the id
of the Element that is the parent of this element. When a parent element is translated, all its children get translated too.Elements trigger several special events, detailed in the element events documentation.
It is possible to extend the joint.dia.Element
class to create a custom element. A custom element may override Element properties to assign its own defaults. These values override builtin defaults, if provided, and are applied to all instances of the new Element type, unless an individual instance overrides them with its own values. The following Element properties are applicable in this context:
markup
- provide default element markup for all instances of this Element type, specified with a JSON array.attrs
- provide default element styling for all instances of this Element type. These allow you to change the style and size of SVG elements, identified by their selectors.Creating custom elements is explained in more detail in our tutorial.
The following list contains events that you can react on:
change
- generic event triggered for any change on the element - fn(element, opt)change:position
- triggered when the element changes its position - fn(element, newPosition, opt)change:angle
- triggered when the element gets rotated - fn(element, newAngle, opt)change:size
- triggered when the element gets resized - fn(element, newSize, opt)change:attrs
- triggered when the element changes its attributes - fn(element, newAttrs, opt)change:embeds
- triggered when other cells were embedded into the element - fn(element, newEmbeds, opt)change:parent
- triggered when the element got embedded into another element - fn(element, newParent, opt)change:z
- triggered when the element is moved in the z-level (toFront and toBack) - fn(element, newZ, opt)transition:start
- triggered when a transition starts. - fn(element, pathToAttribute)transition:end
- triggered when a transiton ends. - fn(element, pathToAttribute)// Listening for changes of the position to a single element
element1.on('change:position', function(element1, position) {
alert('element1 moved to ' + position.x + ',' + position.y);
});
// All elements events are also propagated to the graph.
graph.on('change:position', function(element, position) {
console.log('Element ' + element.id + 'moved to ' + position.x + ',' + position.y);
});
// Using the option parameter and a custom attribute
graph.on('change:custom', function(element, custom, opt) {
if (opt.consoleOutput) {
console.log('Custom attribute value changed to "' + custom + '"');
}
});
element2.prop('custom', 'myValue', { consoleOutput: true });
Many diagramming applications deal with elements with ports. Ports are usually displayed as circles inside diagram elements and are used not only as "sticky" points for connected links, but they also further structure the linking information. It is common that certain elements have lists of input and output ports. A link might not then point to the element as a whole, but to a certain port instead.
It's easy to add ports to arbitrary shapes in JointJS. This can be done either by passing a ports definition as an option
in the constructor, or using the ports API to get/add/remove single or multiple ports. For more information on how to define ports please see the Port configuration section.
joint.dia.Element
hasPort
/ hasPorts
addPort
/ addPorts
removePort
/
removePorts
getPort
/ getPorts
portProp
getPortsPositions
// Single port definition
const port = {
// id: 'abc', // Generated if `id` value is not present
group: 'a',
args: {}, // Extra arguments for the port layout function, see `layout.Port` section
label: {
position: {
name: 'left',
args: { y: 6 } // Extra arguments for the label layout function, see `layout.PortLabel` section
},
markup: [{
tagName: 'text',
selector: 'label'
}]
},
attrs: {
body: { magnet: true, width: 16, height: 16, x: -8, y: -4, stroke: 'red', fill: 'gray'},
label: { text: 'port', fill: 'blue' }
},
markup: [{
tagName: 'rect',
selector: 'body'
}]
};
// a.) Add ports in constructor.
const rect = new joint.shapes.standard.Rectangle({
position: { x: 50, y: 50 },
size: { width: 90, height: 90 },
ports: {
groups: {
'a': {}
},
items: [
{ group: 'a' },
port
]
}
});
// b.) Or add a single port using API
rect.addPort(port);
rect.getGroupPorts('a');
/*
[
{ * Default port settings * },
{ * Follows port definition * },
{ * Follows port definition * }
]
*/
id | string |
It is automatically generated if no id is provided. IDs must be unique in the context of a single shape - two ports with the same port id are therefore not allowed (Element: found id duplicities in ports. error is thrown).
|
group | string | Group name, more info in the groups section. |
args | object |
Arguments for the port layout function. Available properties depend on the type of layout. More information can be found in layout.Port .
|
attrs | object |
JointJS style attribute definition. The same notation as the attrs property on Element .
|
markup | MarkupJSON | string |
Custom port markup. Multiple roots are not allowed. Valid notation would be:
The default port looks like the following: |
label | object |
Port label layout configuration. E.g. label position, label markup. More information about port label layouts can be found in layout.PortLabel .
|
|
string | object |
Port label position configuration. It can be a
|
|
string |
It states the layout type. It matches the layout method name defined in the joint.layout.PortLabel namespace: name: 'left' is implemented as joint.layout.PortLabel.left .
|
|
object |
Additional arguments for the layout method. Available properties depend on the layout type. More information can be found in the layout.PortLabel section.
|
|
MarkupJSON | string |
Custom port label markup. Multiple roots are not allowed. The default port label looks like the following: <text class="joint-port-label" fill="#000000"/> .
|
z | number | string |
An alternative to HTML
Shapes most likely consist of 1 or more DOM elements, For instance, the first shape from the demo above with the following markup...
...will be rendered like this:
Ports will be placed in the
It will be rendered like this:
|
All properties described above are optional, and everything has its own default. E.g. element.addPorts([{}, {}])
will add 2 ports with default settings.
While single port definitions are useful, what if we want more control over our ports? This is where a port group can come into play. A group allows us to define multiple ports with similar properties, and influence the default port alignment. Any individual port can override a property in a port group definition except the type of layout(E.g. position: 'left'
). The group definition defines the layout, and the individual port args
are the only way a port can affect it.
// Port definition for input ports group
const portsIn = {
position: {
name: 'left', // Layout name
args: {}, // Arguments for port layout function, properties depend on type of layout
},
label: {
position: {
name: 'left',
args: { y: 6 }
},
markup: [{
tagName: 'text',
selector: 'label',
}]
},
attrs: {
body: { magnet: 'passive', width: 15, height: 15, stroke: 'red', x: -8, y: -8 },
label: { text: 'in1', fill: 'black' }
},
markup: [{
tagName: 'rect',
selector: 'body'
}]
};
// Define port groups in element constructor
const rect = new joint.shapes.basic.Rect({
// ...
ports: {
groups: {
'group1': portsIn,
// 'group2': ...,
// 'group3': ...,
},
items: [
// Initialize 'rect' with port in group 'group1'
{
group: 'group1',
args: { y: 40 } // Overrides `args` from the group level definition for first port
}
]
}
});
// Add another port using Port API
rect.addPort(
{ group: 'group1', attrs: { label: { text: 'in2' }}}
);
position | string | object |
Port position configuration. It can be a string to set the port layout type directly with default
settings, or an object where it's possible to set the layout type and options.
|
|
string |
It states the layout type. Match the layout method name defined in the joint.layout.Port namespace: name: 'left' is implemented as joint.layout.Port.left .
|
|
object |
Arguments for the port layout function. Available properties depend on the type of layout. More information can be found in layout.Port .
|
attrs | object |
JointJS style attribute definition. The same notation as the attrs property on Element .
|
markup | MarkupJSON | string |
Custom port markup. Multiple roots are not allowed. Valid notation would be:
The default port looks like the following: |
label | object |
Port label layout configuration. E.g. label position, label markup. More information about port label layouts can be found in the layout.PortLabel section.
|
|
string | object |
Port label position configuration. It can be a
|
|
string |
It states the layout type. It matches the layout method name defined in the joint.layout.PortLabel namespace: name: 'left' is implemented as joint.layout.PortLabel.left .
|
|
object |
Additional arguments for the layout method. Available properties depend on the layout type. More information can be found in the layout.PortLabel section.
|
|
MarkupJSON | string |
Custom port label markup. Multiple roots are not allowed. The default port label looks like the following: <text class="joint-port-label" fill="#000000"/> .
|
Both port and port label can have custom markup...
var rect = new joint.shapes.basic.Rect({
// ...
});
rect.addPort({
markup: [{
tagName: 'rect', selector: 'body', attributes: { 'width': 20, 'height': 20, 'fill': 'blue' }
}]
});
rect.addPort({
markup: [{
tagName: 'rect', selector: 'body', attributes: { 'width': 16, 'height': 16, 'fill': 'red' }
}],
label: {
markup: [{ tagName: 'text', selector: 'label', attributes: { 'fill': '#000000' }}]
},
attrs: { label: { text: 'port' }}
});
...or, it can be set as an default port markup/port label markup on an element model:
var rect = new joint.shapes.basic.Rect({
portMarkup: [{
tagName: 'rect',
selector: 'body',
attributes: {
'width': 20,
'height': 20,
'fill': 'blue',
'stroke': 'black',
'stroke-width': 5 // Use native SVG kebab-case for attributes in markup
}
}],
portLabelMarkup: [{
tagName: 'text',
selector: 'label',
attributes: {
'fill': 'yellow'
}
}]
// ...
});
element.addPort(port, [opt])
Add a single port, where port
could be defined as described in section Port interface
element.addPorts(ports, [opt])
Add array of ports
. Ports are validated for id
duplicities, if there is a id collision, no new ports are added, where port
could be defined as describe in section Port interface
element.addTo(graph)
Add the element to the graph
(an instance of joint.dia.Graph
). This is equivalent to calling graph.addCell(element)
.
element.angle()
Return the rotation of the element in degrees (0° - 360°).
element.attr(attrs [, opt])
Set presentation attributes (SVG and JointJS attributes) on view subelements. attrs
can either be an object or string representing a path to a nested attribute. If it is an object, the keys of the attrs
object are selectors (JSON Markup Selector or CSS Selector) matching the subelements. The values are objects containing SVG attributes and their values. attrs
object will be mixed with attrs
property of the element
model. This is a convenient way of rewriting only some of the attributes of the subelements. For overwriting all attributes of all subelements, use element.set('attrs', attrs)
.
element.attr({
// selectors as defined in the JSON markup
body: { refWidth: '100%', refHeight: '100%' },
label: { text: 'My Label' },
// using CSS selectors is significantly slower
rect: { fill: 'blue' },
text: { fill: 'white', fontSize: 15 },
'.myrect2': { fill: 'red' }
});
prop()
or set()
methods.
element.set('confirmed', true);
element.prop('data/count', 10);
element.attr(path, value [, opt])
An alternative call using a string/array path and a value:
element.attr('body/fill', 'red');
element.attr(['label', 'fontSize'], 12);
// Note: an equivalent expression is also
element.prop('attrs/label/fontSize', 12);
element.attr([path])
Get attribute value defined by a path. If no path provided the whole attrs
object is returned.
element.attr('body/fill') === 'red';
element.attr(['label', 'fontSize']) === 12;
element.clone(options)
Returns a new instance of the element with identical attributes. If options.deep === true
, then
all the embedded cells (elements, links) of the element are cloned as well. In this case, the return value is an array of
instances rather then a single instance.
element.embed(cell, [opt])
element.embed(cells, [opt])
Embed a cell (element or link) or an array of cells into the element. The element then becomes a parent of the embedded cell. When a parent is moved (translated), all cells embedded into that parent will move as well. If links are embedded, their vertices move with the parent. This way both options are available: if a link is not embedded but its source/target elements are and their parent moves, the embedded elements move with the parent but the link vertices stay at the same position. If the link is embedded with its source/target elements, its vertices move as the parent moves.
element.findView(paper)
Find view (joint.dia.ElementView
) for the element model in the paper
.
This is a shortcut to the equivalent call paper.findViewByModel(element)
element.fitEmbeds([opt])
Resize the element so that it fits all the embedded elements inside it. If opt.deep
is true
,
the resizing will be done recursively for any embedded elements that contain embedded elements themselves.
Set opt.padding
if you want certain padding on all the parent elements.
element.getAbsolutePointFromRelative(x, y)
element.getAbsolutePointFromRelative(relativePoint)
Accept a point in the coordinate system of the element and return the point in the graph coordinate system, represented as a g.Point
.
The element coordinate system has its origin [0,0]
at the element.position()
rotated by the element.angle()
. Each axis x
and y
are rotated by the same angle.
element.getAncestors()
Return an array of all the ancestors of this element starting from the immediate parent all the way up to the most distant ancestor.
element.getBBox([opt])
Return the bounding box of the element model, represented as a g.Rect
.
Keep in mind that this function reports the dimensions of the element's model, not the dimensions of the element's view. (For example, the model bounding box does not include any associated ports). See the documentation of the elementView.getBBox
function for details about the differences.
if (element1.getBBox().intersect(element2.getBBox())) {
// intersection of the two elements
}
opt | description |
---|---|
deep | Return the union of the element's bounding box and all the elements embedded into it. |
rotate | Return the bounding box after the element's rotation. |
element.getEmbeddedCells([opt])
Return an array of all the embedded cells of an element. If all you need is id
's of all the embedded cells,
use element.get('embeds')
. If opt.deep
is true
, all the deeply embedded
cells will be returned. The order in which the cells are returned depends on the search algorithm used. By
default, Depth-first search (DFS) algorithm is used. If
opt.breadthFirst
is true
, the Breadth-first search algorithm will be used instead.
element.getGroupPorts(groupName)
Returns an array of all ports on the element with the given groupName
. If there is no such a port an empty array is returned.
element.getPort(id)
Returns the port specified by an id
. If such a port does not exist the method returns undefined
.
element.getPortIndex(portId)
Returns the port index in the array of port items.
element.getPorts()
Returns an array of all ports on the element. If there is no port an empty array is returned.
element.getPortsPositions(groupName)
Returns the positions and the angle of all ports in the group, relatively to the element position.
element.getRelativePointFromAbsolute(x, y)
element.getRelativePointFromAbsolute(absolutePoint)
Accept a point in the graph coordinate system and return the point in the coordinate system of the element, represented as a g.Point
.
The element coordinate system has its origin [0,0]
at the element.position()
rotated by the element.angle()
. Each axis x
and y
are rotated by the same angle.
element.getTransitions()
Return an array of all active transitions (their paths).
element.hasPort(id)
Check if an element contains a port with id
. Returns boolean
.
element.hasPorts()
Check if an element has any ports defined. Returns boolean
.
element.insertPort(before, port, [opt])
Insert a new port before another port, where port
could be defined as described in section Port interface and before
is either an index, port id or port itself.
element.isElement()
Always returns true
. The reason the method is here is that both joint.dia.Element
and joint.dia.Link
inherit from joint.dia.Cell
. This method is useful if you don't know what the cell is. Calling cell.isElement()
is equivalent to cell instanceof joint.dia.Element
. Example:
var cell = graph.getCell(myId)
if (cell.isElement()) {
// Do something if the cell is an element.
}
element.isEmbeddedIn(element [, opt])
Return true
if the element is embedded in another element element
. If opt.deep
is false
, only direct parentage will be checked. opt.deep
is true
by default.
element.isLink()
Always returns false
. The reason the method is here is that both joint.dia.Element
and joint.dia.Link
inherit from joint.dia.Cell
. This method is useful if you don't know what the cell is. Calling cell.isLink()
is equivalent to cell instanceof joint.dia.Link
. Example:
var cell = graph.getCell(myId)
if (cell.isLink()) {
// Do something if the cell is a link.
}
element.portProp(portId, path, [value])
Set properties, possibly nested, on the element port. This is an equivalent of the attr() method but this time for custom data properties.
element.portProp('port-id', 'attrs/circle/fill', 'red');
element.portProp('port-id', 'attrs/circle/fill'); // 'red'
element.portProp('port-id', 'attrs/circle', { r: 10, stroke: 'green' });
element.portProp('port-id', 'attrs/circle'); // { r: 10, stroke: 'green', fill: 'red' }
element.position([opt])
If position()
is called without arguments, it returns the current position.
Option | Default | Description |
---|---|---|
parentRelative | false | The method returns the current position of the element relative to its parent. |
element.position(x, y, [opt])
Set the element position to x
and y
coordinates. This is almost equivalent to
element.set('position', { x, y }, opt)
. However, this method provides some additional functionality.
Option | Default | Description |
---|---|---|
parentRelative | false | If set to true the x and y
coordinates will be treated relatively to the parent element of this element. If the element has no parent or the parent is a link, the option is ignored.
|
deep | false | If set to true it will position the element and its descendants while keeping the original distances of the descendants to/from the element origin. The relative distances of the descendants are maintained. |
restrictedArea | null |
If set to a rectangle, the translation of the element will be restricted to that rectangle only.
The restrictedArea is an object of the form { x: Number, y: Number, width: Number, height: Number } .
This is useful, e.g. if you want to restrict the translation of an embedded element within its parent. The only thing
you have to do in this case is to pass the bounding box of the parent element to the restrictedArea option:
The code above makes sure that the element |
el1.position(100, 100);
el1.embed(el2);
el2.position(10, 10, { parentRelative: true });
el2.position() // --> 110@110
el1.position(200,200, { deep: true });
el2.position() // --> 210@210
const restrictedArea = paper.getArea();
el.position(x, y, { restrictedArea }); // --> makes sure x and y is within the area provided
element.prop(properties)
Set properties, possibly nested, on the element model. This is an equivalent of the attr() method but this time for custom data properties.
element.prop('name/first', 'John')
element.prop('name/first') // 'John'
element.prop({ name: { first: 'John' } })
// Nested arrays are supported too:
element.prop('mylist/0/data/0/value', 50)
element.prop({ mylist: [ { data: [ { value: 50 } ] } ] })
To overwrite attributes, enable rewrite mode by adding { rewrite: true }
as the 3rd argument. This differs from the default behaviour which is to merge our properties.
element.prop('custom/state/isCollapsed', true);
element.prop('custom/state', { isActive: false }, { rewrite: true });
// Output from element.toJSON();
// We can see our attributes have been overwritten
{
"type": "standard.Rectangle",
"position": { "x": 0, "y": 0 },
"size": { "width": 1, "height": 1 },
"angle": 0,
"id": "b1c02090-e46a-4d90-a5dc-5096f1559b9f",
"custom": {
"state": {
"isActive": false
}
},
"attrs": {}
}
Advanced: Pass { isolate: true }
if the property change does not affect the connected links. Typically, changing the element fill color has zero effect on attached links. By default, the element and all connected links are updated.
element.attr(['body', 'fill'], 'red', { isolate: true });
element.remove(options)
Remove the element from the graph. All its embedded elements will get removed too and the element gets unembedded from its parent element. By default,
all the associated links are removed too. To suppress this behaviour, set options.disconnectLinks === true
. In this case, all the associated
links get disconnected from this element rather then removed completely from the graph.
element.removeAttr(path, [options])
Remove a previously set attribute from the element. path
can either be a string that specifies
the path to the, possibly nested, attribute to be removed or an array of more paths.
The associated element view makes sure the element gets re-rendered properly.
If options
is passed, it can contain data that is passed over to
the event listeners for the change:attrs
event triggered on the element
itself and also on the graph the element is in.
element.removePort(port, [opt])
Remove a port from an element, where port
is a port object, or portId
.
element.removePorts(ports [, opt])
Remove an array of ports
from the element.
The ports can be specified as Port interfaces or portIds
. The function skips over any ports in the array that do not exist on the element.
element.removePorts([opt])
If no array is provided, the function removes all ports from the element.
element.resize(width, height [, opt])
Resize an element in place so that the "scalable" group has width width
and height height
. In place in this case means
that the top-left corner of the element stays at the same position after resizing. In other words,
the element is streched to the bottom/right (by default). To change the direction of resizing, set
opt.direction
to one of 'left'
, 'right'
, 'top'
, 'bottom'
,
'top-right'
, 'top-left'
, 'bottom-left'
or 'bottom-right'
(the default).
There is a difference between a classical scale and
resize operations. resize
doesn't actually scale the whole SVG <g>
element grouping
all its subelements. It only scales subelements of the <g class"scalable"/>
group. This is very
useful and brings a lot of flexibility in defining which subelements should be scaled and which not. Imagine a simple
rectangle element with text inside. Usually, when we resize the whole element, we expect the rectangle to
get scaled while the text should stay the same size, only its position should be adjusted so that the text stays
in the center of the rectangle. This can be easilly achieved by adding the <rect/>
element to the <g class"scalable"/>
group
in the markup and positioning the text
subelement relatively to the <rect />
element: <text ref-x=".5" ref-y=".5" ref="rect" />
.
Note that neither of ref-x, ref-y and ref
attributes is an SVG standard attribute. These are special
attributes introduced by JointJS. More on these in the section on Special attributes.
element.rotate(deg, [absolute, origin, opt])
Rotate an element by deg
degrees around its center. If the optional absolute
parameter is true
,
the deg
will be considered an absolute angle, not an addition to the previous angle.
If origin
is passed in the form of an object with x
and y
properties,
then this point will be used as the origin for the rotation transformation.
element.scale(sx, sy, origin[, opt])
Scales the element's position and size relative to the given origin.
element.stopTransitions([path])
Stops all running transitions. If parameter path
is provided, it will stop only transitions specified by this path.
element.toBack([opt])
Move the element so it is behind all other cells (elements/links).
If opt.deep
is true
, all the embedded cells of this
element will get higher z
index than that of this element in a Breadth-first search (BFS) fashion. This
is especially useful in hierarchical diagrams where if you want to send an element to the back, you don't want
its children (embedded cells) to be hidden behind that element.
Set opt.breadthFirst
to false
to index the elements using Depth-first search (DFS).
element.toFront([opt])
Move the element so it is on top of all other cells (element/links).
If opt.deep
is true
, all the embedded cells of this
element will get higher z
index than that of this element in a Breadth-first search (BFS) fashion. This
is especially useful in hierarchical diagrams where if you want to send an element to the front, you don't want
its children (embedded cells) to be hidden behind that element.
Set opt.breadthFirst
to false
to index the elements using Depth-first search (DFS).
All elements have a z
property defining their z-level in the graph. This z
property
can even be set directly by element.set('z', 123)
. This change will be automatically handled by the joint.dia.Paper
object associated with the joint.dia.Graph
object this element is part of and all the SVG elements will get resorted so that
their position in the DOM reflects the z
level.
container.embed(el1);
container.embed(el2);
container.toFront({ deep: true });
element.toJSON()
Return a copy of the element's attributes for JSON serialization. This can be used for persistance or serialization. Note that this method doesn't return a JSON string but rather an object that can be then serialized to JSON with JSON.stringify()
.
element.transition(path, value [, opt])
Allows to change the element's property gradually over a period of time. This method lets you specify what property to change (path
), when the transition will start (options.delay
), how long the transition will last (options.duration
), how the transition will run (options.timingFunction
), and how to interpolate the property value (options.valueFunction
).
element.transition('position/x', 250, {
delay: 100,
duration: 500,
timingFunction: function(t) { return t*t; },
valueFunction: function(a, b) { return function(t) { return a + (b - a) * t }}
});
// will start changing the element's x-coordinate in 100ms, for period of 500ms.
JointJS comes pre-built with some common timing and interpolating functions. The timing functions are defined in the joint.util.timing
namespace and the interpolating functions in the joint.util.interpolate
namespace. The predefined timing functions are:
linear
quad
cubic
inout
exponential
bounce
and the predefined interpolating functions are:
number
object
hexColor
unit
element.transition('attrs/text/font-size', '1em', {
valueFunction: joint.util.interpolate.unit,
timingFunction: joint.util.timing.bounce
});
// will start changing the current font size value to 1em in the bounce fashion.
element.translate(tx, [ty], [opt])
Translate an element by tx
pixels in x axis and ty
pixels in y axis.
ty
is optional in which case the translation in y axis will be considered zero.
The optional options object opt
can be used to pass additional parameters to the event handlers
listening on 'change:position'
events. opt.transition
can be used to initiate an animated
transition rather than a sudden move of the element. See joint.dia.Element:transition for more info
on transitions. If opt.restrictedArea
is set, the translation of the element will be restricted to that area only.
The restrictedArea
is an object of the form { x: Number, y: Number, width: Number, height: Number }
.
This is useful, e.g. if you want to restrict the translation of an embedded element within its parent. The only thing
you have to do in this case is to pass the bounding box of the parent element to the restrictedArea
option:
myElement.translate(50, 50, { restrictedArea: graph.getCell(myElement.get('parent')).getBBox() })
The code above makes sure that the element myElement
never crosses the bounding box of its parent element.
note that this also works if the element myElement
has other embedded elements. In other words, the
bounding box of the myElement
that is used to calculate the restriction is the total bounding box,
including all its children (in case they are sticking out
).
element.unembed(cell, [opt])
element.unembed(cells, [opt])
Free up an embedded cell or an array of cells from its parent element.
The view for the joint.dia.Element model. It inherits from joint.dia.CellView and is responsible for:
To find the view associated with a specific element (model), use the findViewByModel method of the paper.
var elementView = paper.findViewByModel(element);
elementView.addTools(toolsView)
Add the provided toolsView
(of the joint.dia.ToolsView
type) to the element view.
Adding a tools view to an element view is the last (third) step in the process of setting up element tools on an element view:
// 1) creating element tools
var boundaryTool = new joint.elementTools.Boundary();
var removeButton = new joint.elementTools.Remove();
// 2) creating a tools view
var toolsView = new joint.dia.ToolsView({
name: 'basic-tools',
tools: [boundaryTool, removeButton]
});
// 3) attaching to an element view
var elementView = element.findView(paper);
elementView.addTools(toolsView);
Every element view we want to attach to requires its own tools view object (ToolsView
objects are automatically reassigned to the last element view they are added to). Similarly, every tools view we create requires its own set of tools (ToolView
objects are automatically reassigned to the last toolsView.tools
array they were made part of).
The element tools are added in the visible state. Use the elementView.hideTools
function if this behavior is not desirable (e.g. if you want the element tools to appear in response to user interaction). Example:
elementView.addTools(toolsView);
elementView.hideTools();
paper.on('element:mouseenter', function(elementView) {
elementView.showTools();
});
paper.on('element:mouseleave', function(elementView) {
elementView.hideTools();
});
elementView.getBBox([opt])
Return a bounding box of the element view.
If opt.useModelGeometry
option is set to true
, the resulting bounding box is calculated based on the dimensions of the element model. (This means that SVG sub elements sticking out
of the element are excluded.) This behavior is similar to the element.getBBox
function – but the elementView
function transforms the bounding box to match joint.dia.Paper
translation and scaling.
elementView.getNodeBBox(magnet)
Return the bounding box of the SVGElement provided as magnet
(element/subelement/port of this element view).
Use the paper.localToPaperRect
function to transform the returned bounding box to match the paper's translation and scaling.
elementView.getNodeUnrotatedBBox(magnet)
Return the unrotated bounding box of the SVGElement provided as magnet
(element/subelement/port of this element view).
Use the paper.localToPaperRect
function to transform the returned bounding box to match the paper's translation and scaling.
elementView.hideTools()
Hide all tools attached to this element view.
elementView.removeTools()
Remove the tools view attached to this element view.
elementView.showTools()
Show all tools attached to this element view.
joint.dia.Graph is the model holding all the cells (elements and links) of the diagram. It's a Backbone model.
The collection of all the cells is stored in the property cells
.
The graph is a powerful data model behind all JointJS diagrams. It not only provides an efficient storage for directed graphs but also offers useful algorithms for traversing the graphs.
Additionally, the graph accepts an option object in its constructor function that can contain the cellNamespace
option.
This option can be used to change the default behavior of JointJS which by default reads cell model definitions from the joint.shapes
namespace.
For example, if a cell is of type 'myshapes.MyElement'
, then the graph looks up joint.shapes.myshapes.MyElement
model when deserializing a graph from the JSON format. If the graph is instantiated as e.g. var graph = new joint.dia.Graph({}, { cellNamespace: myShapesNamespace })
, then
the graph will read the model definition from the myShapesNamespace.myshapes.MyElement
object instead. This is useful in situations where you don't want to - for any reason -
use the joint.shapes
namespace for defining your own custom shapes. This option is often used in combination with the
cellNamespaceView
option on the joint.dia.Paper object.
The following list contains events that you can react on:
change
- generic event triggered for any change in the graphadd
- triggered when a new cell is added to the graphremove
- triggered when a cell is removed from the graphgraph.on('add', function(cell) {
alert('New cell with id ' + cell.id + ' added to the graph.')
})
The JointJS graph JSON representation has the following format:
{
cells: [// Array of cells (ie. links and elements).
{
id: '3d90f661-fe5f-45dc-a938-bca137691eeb',// Some randomly generated UUID.
type: 'basic.Rect',
attrs: {
'stroke': '#000'
},
position: {
x: 0,
y: 50
},
angle: 90,
size: {
width: 100,
height: 50
},
z: 2,
embeds: [
'0c6bf4f1-d5db-4058-9e85-f2d6c74a7a30',
'cdbfe073-b160-4e8f-a9a0-22853f29cc06'
],
parent: '31f348fe-f5c6-4438-964e-9fc9273c02cb'
// ... and some other, maybe custom, data properties
}
]
}
graph.addCell(cell)
Add a new cell to the graph. If cell
is an array, all the cells in the array will be added to the graph.
var rect = new joint.shapes.basic.Rect({
position: { x: 100, y: 100 },
size: { width: 70, height: 30 },
attrs: { text: { text: 'my rectangle' } }
})
var rect2 = rect.clone()
var link = new joint.dia.Link({ source: { id: rect.id }, target: { id: rect2.id } });
var graph = new joint.dia.Graph
graph.addCell(rect).addCell(rect2).addCell(link)
graph.addCells(cells[, opt])
graph.addCells(cell, cell, ..[, opt])
Add new cells to the graph. This is just a convenience method that wraps the addCell method.
graph.bfs(element, iteratee [, opt])
Traverse the graph using the Breadth-first search algorithm starting at element
(note the element itself will be visited too). iteratee
is a function of the form function(element, distance) {}
that will be called with the currently visited element and distance of that element from the root element of the search (the element
passed to bfs()
). If iteratee
explicitely returns false
, the search stops.
The following image shows the order in which elements are traversed in the graph:
Note that the bfs()
algorithm is not only capable of traversing tree graphs but it can traverse any directed graph too.
It is smart enough not to traverse an element that was already visited.
If opt.inbound
is true
, reverse the search direction (it's like reversing all the link directions, i.e. swaping their source
and target
).
If opt.outbound
is true
, search follows the link directions. Calling bfs()
with opt.outbound
set to true
is the most common case (graph is traversed following the direction of links).
If none of opt.inbound
and opt.outbound
are used or both options are set to true
, the graph is traversed in both directions (very rare use case).
If opt.deep
is true
, the traversal takes into account embedded elements too. This option has the usual meaning as in other methods were deep
option is used. For example, in a hierarchy A (top level element), A1 (embedded in A), B (top level element), where A is not directly connected to B but its embedded element is (there is a link from A1 ----> B), bfs(A)would not visit B but bfs(A, function() {}, { deep: true }) would.
graph.clear([options)
Remove all the cells from the graph. options
object can optionally contain additional data that is passed over to the event listeners of the graph cells remove event.
graph.cloneCells(cells)
Clone all the cells (elements and/or links) from the cells
array and return an
object that maps the original cell ID to the clone (i.e. an object of the form { [original cell ID]: [clone] }
).
The reason why this object is returned instead of an array of clones is that it is very useful to know which object the clone
was created for.
The number of clones returned equals cells.length
. This function does not simply
clone all the cells but it also reconstructs all the source/target and parent/embed references
within cells
. This is very useful. For example, for a graph A --- L ---> B
,
cloneCells([A, L, B])
returns { A.id: A2, L.id: L2, B.id: B2 }
resulting
in a graph A2 --- L2 ---> B2
, i.e. the source and target of the link L2
is changed to point to A2
and B2
(in contrast to just looping over cells
and calling cell.clone()
on each item).
graph.cloneSubgraph(cells [, opt])
Clone the whole subgraph, including all the connected links whose source/target is in the subgraph.
This is equivalent to calling graph.cloneCells(graph.getSubgraph(cells))
.
If opt.deep
is true
, take into account embedded cells of the subgraph cells.
Return an object of the form { [original cell ID]: [clone] }
.
graph.dfs(element, iteratee [, opt])
Traverse the graph using the Depth-first search algorithm starting at element
(note the element itself will be visited too). iterate
is a function of the form function(element, distance) {}
that will be called with the currently visited element and distance of that element from the root element of the search (the element
passed to dfs()
). If iteratee
explicitely returns false
, the search stops.
The following image shows the order in which elements are traversed in the graph:
Note that the dfs()
algorithm is not only capable of traversing tree graphs but it can traverse any directed graph too. It is smart enough not to traverse an element that was already visited.
If opt.inbound
is true
, reverse the search direction (it's like reversing all the link directions, i.e. swaping their source
and target
).
If opt.outbound
is true
, search follows the link directions. Calling dfs()
with opt.outbound
set to true
is the most common case (graph is traversed following the direction of links).
If none of opt.inbound
and opt.outbound
are used or both options are set to true
, the graph is traversed in both directions (very rare use case).
If opt.deep
is true
, the traversal takes into account embedded elements too. This option has the usual meaning as in other methods were deep
option is used. For example, in a hierarchy A (top level element), A1 (embedded in A), B (top level element), where A is not directly connected to B but its embedded element is (there is a link from A1 ----> B), dfs(A) would not visit B but dfs(A, function() {}, { deep: true }) would.
graph.disconnectLinks(element)
Disconnect all the associated links with the element
.
graph.findModelsFromPoint(point)
Find elements (instance of joint.dia.Element
) under a certain point in the graph. point
is an object with x
and y
properties. Returns an
array of elements whose bounding box contains point
. Note that there can be more then one element as elements might overlap.
graph.findModelsInArea(rect)
Find elements (instance of joint.dia.Element
) in a certain area in the graph. rect
is an object with x
, y
, width
and height
properties. Returns an
array of elements whose bounding box top/left coordinate falls into the rect
rectangle.
graph.findModelsUnderElement(element [, opt])
Find all the elements (instances of joint.dia.Element
) that are located below element
.
opt.searchBy
parameter optionally determines what it means for an element to be below another element.
Possible values are 'bbox'
(default), 'center'
, 'origin'
, 'corner'
, 'topRight'
,
and 'bottomLeft'
.
graph.fromJSON(jsonObject, [options])
Load a graph from a JSON object (not string). Used in conjunction with the graph.toJSON()
function.
The options
object may contain additional data that is passed over to graph change event listeners.
Note that this method does not expect a JSON string but rather an object in the JSON format. Use JSON.parse(jsonString)
if you need to convert a JSON string into the object form:
graph.fromJSON(JSON.parse(jsonString));
Example of storing stringified JSON objects:
var jsonString = JSON.stringify(graph.toJSON());
// ... send jsonString to the server
// store jsonString to localStorage or do whatever you want
// later on ...
graph.fromJSON(JSON.parse(jsonString));
Example of storing JSON objects directly:
var jsonObject = graph.toJSON();
// ... send jsonObject to the server
// store jsonObject (e.g. in a non-relational database)
// later on ...
graph.fromJSON(jsonObject)
graph.getBBox()
Returns the bounding box (g.Rect) that surrounds all cells in the graph. It returns null
if the graph is empty.
var bbox = graph.getBBox().inflate(10);
graph.getCell(id)
Get a cell from the graph by its id
.
graph.getCells()
Return an array of all elements and links in the graph. The cells are sorted by their z
index (the smallest z
being first).
graph.getCellsBBox(cells[, opt])
Returns the bounding box (g.Rect) that surrounds all the given cells.
// Get the bounding box of all `el1` successors and their embeds
var bbox = graph.getCellsBBox(graph.getSuccessors(el1), { deep: true });
graph.getCommonAncestor(...cells)
Return the common ancestor of all the cells passed as arguments. For example, if an element B
is embedded in an element A
and an element C
is also embedded in the element A
, graph.getCommonAncestor(B, C)
returns the element A
. This also works on an arbitrary deep hierarchy.
graph.getConnectedLinks(element [, opt])
Get all links connected with element
.
If opt.inbound === true
, return only inbound connected links. Conversely, if opt.outbound === true
, return only outbound connected links. If both of these options are left undefined, or if both of them are set to true
, return both inbound and outbound links.
By default, this function returns only immediate (shallow
) inbound and outbound links - no recursion. (Note that connections from element
to embedded child elements, and connections to element
from embedding parent elements count as shallow
, too - they too are returned.)
If opt.deep === true
, return all outside links that connect with element
or any of its descendants (descendants
meaning elements that are embedded or deeply embedded within element
). The inbound
and outbound
options can still be applied on top of this option.
Note that the specification of opt.deep
excludes links that connect two descendants of element
(enclosed
links). If you do need to find all links connected with and/or enclosed within element
, you should use opt.deep === true
alongside an additional option: opt.includeEnclosed === true
.
If opt.indirect === true
, also return links that can only be considered connected to element
if we go against the flow
of directed links at link-link connections.
Example use:
var links = graph.getConnectedLinks(element); // inbound and outbound
var links = graph.getConnectedLinks(element, { outbound: true });
var links = graph.getConnectedLinks(element, { deep: true }); // inbound and outbound
var links = graph.getConnectedLinks(element, { inbound: true, deep: true });
var links = graph.getConnectedLinks(element, { outbound: true, deep: true, includeEnclosed: true });
var links = graph.getConnectedLinks(element, { indirect: true });
graph.getElements()
Return an array of all elements in the graph. The elements are sorted by their z
index (the smallest z
being first).
graph.getFirstCell()
Get the first cell (element or link) in the graph. The first cell is defined as the cell
with the lowest z
property (the cell most in the back, see the Presentation section of joint.dia.Element).
graph.getLastCell()
Get the last cell (element or link) in the graph. The last cell is defined as the cell
with the highest z
property (the cell most in the front, see the Presentation section of joint.dia.Element).
graph.getLinks()
Return an array of all links in the graph. The links are sorted by their z
index (the smallest z
being first).
graph.getNeighbors(element [, opt])
Get all the neighbors of element
in the graph. Neighbors are all the elements connected to element
via either an inbound or an outbound link.
Accepts several options, which may be provided inside an opt
object:
deep
- also return all the neighbors of all the elements embedded inside element
.inbound
- return only inbound neighbors (neighbors connected with a link whose target
is the element
).outbound
- return only outbound neighbors (neighbors connected with a link whose source
is the element
).indirect
- in addition to standard rules (including deep
/outbound
/inbound
modifications), also return the elements that can only be considered neighbors of element
if we go against the flowof directed links at link-link connections.
graph.getPredecessors(element [, opt])
Return an array of all the predecessors of element
. By default, Depth-first search algorithm is used (important for the order of returned elements).
If opt.breadthFirst
is set to true
, use Breadth-first search algorithm instead.
If opt.deep
is set to true
, take into account embedded elements too (see dfs() for details).
graph.getSinks()
Return an array of all the leafs of the graph.
Time complexity: O(|V|)
.
graph.getSources()
Return an array of all the roots of the graph.
Time complexity: O(|V|)
.
graph.getSubgraph(cells [, opt])
Return an array of cells that result from finding elements/links that are connected to any of the cells in the cells
array.
This function loops over cells
and if the current cell is a link, it collects its source/target elements; if it is an element,
it collects its incoming and outgoing links if both the link ends (source/target) are in the cells
array.
For example, for a single element, the result is that very same element.
For two elements connected with a link: A --- L ---> B
, the result of getSubgraph([A, B])
is [A, L, B]
and the result of getSubgraph([L])
is also [A, L, B]
.
If opt.deep
is true
take into account all the embedded cells too when finding neighboring links/elements.
graph.getSuccessors(element [, opt])
Return an array of all the successors of element
. By default, Depth-first search algorithm is used (important for the order of returned elements).
If opt.breadthFirst
is set to true
, use Breadth-first search algorithm instead.
If opt.deep
is set to true
, take into account embedded elements too (see dfs() for details).
graph.isNeighbor(elementA, elementB [, opt])
Return true
if elementB
is a neighbor of elementA
. A neighbor of an element is another element connected to it via an inbound and/or outbound link.
Accepts several options, which may be provided inside an opt
object:
deep
- return true
also if elementB
is a neighbor of an element embedded in elementA
.outbound
- return true
only if elementB
is a succeeding neighbor of elementA
. For example, if elementB
is connected to a directed link behindthe connection of
elementA
.inbound
- return true
only if elementB
is a preceding neighbor elementA
. For example, if elementB
is connected to a directed link ahead ofthe connection of
elementA
.indirect
- in addition to standard rules (including deep
/outbound
/inbound
modifications), also return true
if elementB
can only be considered a neighbor of elementA
if we go against the flowof directed links at link-link connections.
graph.isPredecessor(elementA, elementB)
Return true
if elementB
is a predecessor of elementA
.
graph.isSink(element)
Return true
if element
is a leaf, i.e. there is no link coming out of the element.
Time complexity: O(1)
.
graph.isSource(element)
Return true
if element
is a root, i.e. there is no link that targets the element.
Time complexity: O(1)
.
graph.isSuccessor(elementA, elementB)
Return true
if elementB
is a successor of elementA
.
graph.maxZIndex()
Get the highest Z value in the graph (the value of the cell on front).
graph.minZIndex()
Get the lowest Z value in the graph (the value of the cell on the back).
graph.removeCells(cells[, opt])
graph.removeCells(cell, cell, ..[, opt])
Removes the given cells from the graph.
graph.removeLinks(element)
Remove all the associated links with the element
.
graph.resetCells(cells[, opt])
graph.resetCells(cell, cell, ..[, opt])
Reset cells in the graph. Update all the cells in the graph in one bulk. This is a more efficient method of adding cells to the graph if you ou want to replace all the cells in one go. options
object can optionally contain additional data that is passed over to the event listeners of the graph reset event.
graph.toJSON()
Return an object representation of the graph, which can be used for persistence or serialization. Use the graph.fromJSON()
function to load a previously converted graph.
Note that this method does not return a JSON string but rather an object that can then be serialized to JSON with JSON.stringify(jsonObject)
:
var jsonString = JSON.stringify(graph.toJSON());
graph.translate(tx, ty [, opt])
Translate all cells in the graph by tx
and ty
pixels. It uses the dia.Element.translate()
and dia.Link.translate()
methods internally.
The base class for highlighters. The HighlighterView class does not implement any particular visual emphasis. It takes care of the management of the instances and introduce mechanisms for displaying, updating and cleaning arbitrary markings added to the SVGElements of cellViews.
The class is not meant to be initialized with the constructor.
HighlighterView.add(cellView, selector, id, options)
Create an instance of the highlighter and add it to the cellView.
It returns an instance of the HighlighterView.
If a highlighter with the same id exists on the CellView, it is removed and a new highlighter is added and returned. You can check if a highlighter already exists by calling HighlighterView.get().
Type | Description | Example |
---|---|---|
String | A selector from the cell's markup. |
|
Object | An object with property selector, which is a selector from the (cell/label/port) markup. There is additional property port (id of a port) available for dia.ElementView. And property label (index of a label) is available for dia.LinkView. If no selector property is provided, the root is used. |
|
SVGElement | An SVGElement, the descendant of cellView.el or the root itself. |
|
HighlighterView.get(cellView)
Return an array of highlighters on the cellView. Only highlighters, which are instances of this class are returned.
// Extend the Base Highlighter Class
const ChildHighlighterView = joint.dia.Highlighter.extend({});
const h1 = joint.dia.HighlighterView.add(cellView, 'root', 'id1');
const c1 = ChildHighlighterView.add(cellView, 'root', 'id2');
const parentHighlighters = joint.dia.Highlighters.get(cellView);
assert.ok(parentHighlighters.includes(h1));
assert.ok(parentHighlighters.includes(c1));
const childHighlighters = ChildHighlighterView.get(cellView);
assert.notOk(parentHighlighters.includes(h1));
assert.ok(parentHighlighters.includes(c1));
HighlighterView.get(cellView, id)
Return the highlighter with given id on the cellView. The highlighter returned must be an instance of this class. If such a highlighter does not exist, null
is returned.
// Extend the Base Highlighter Class
const ChildHighlighterView = joint.dia.Highlighter.extend({});
const h1 = joint.dia.HighlighterView.add(cellView, 'root', 'h1');
const c1 = ChildHighlighterView.add(cellView, 'root', 'c1');
assert.equal(h1, joint.dia.Highlighters.get(cellView, 'h1));
assert.equal(c1, joint.dia.Highlighters.get(cellView, 'c1));
// There is no instance of ChildHighlighterView on the CellView
assert.equal(null, ChildHighlighterView.get(cellView, 'h1));
assert.equal(c1, ChildHighlighterView.get(cellView, 'c1));
HighlighterView.remove(cellView)
Remove all the highlighters from the cellView. Only highlighters, which are instances of this class are removed.
HighlighterView.remove(cellView, id)
Remove the highlighter with given id from the cellView. The highlighter which is not an instance of this class is not removed.
highlighter.highlight(cellView, node)
Mark/Emphasize the node (SVGElement) of the cellView. e.g. render a rectangle above the
HighlighterView.prototype.MOUNTABLE
If the property is true, the highlighter view is attached to the DOM.
If the property is false, it is not attached upon highlight.
The default is true.
const ChildView = joint.dia.HighlighterView.extend({
// HighlighterView `el` is not meant to be appended
MOUNTABLE: false,
highlight(cellView, node) {
node.setAttribute('my-attribute', String(cellView.model.get('highlightAttribute')));
},
unhighlight(cellView, node) {
node.removeAttribute('my-attribute');
}
});
highlighter.unhighlight(cellView, node)
Remove marks made by highlight() from the node (SVGElement) of the cellView.
HighlighterView.prototype.UPDATABLE
If the property is true, the highlighter is updated (highlight() function is called) every time when the related cellView requires an update.
If the property is false, the highlight() method is called only once when the highlighter is added.
The default is true.
const ChildView = joint.dia.HighlighterView.extend({
MOUNTABLE: false,
UPDATABLE: false,
highlight(_cellView, node) {
node.classList.add('my-class');
},
unhighlight(_cellView, node) {
node.classList.remove('my-class');
}
});
layer - the stacking context of the highlighter. Applicable for mountable highlighters only.
null |
Render the highlighter above the cell. It can be hidden by a cell with a higher z index. |
"back" |
Render the highlighter behind all the cells. |
"front" |
Render the highlighter in front of all the cells. |
z - the stacking order (z-index) of the highlighter in the given stacking context. Applicable for mountable highlighters only.
The basic model for diagram links. It inherits from joint.dia.Cell with a few additional properties and methods specific to links. For a quick introduction to elements, see our tutorial.
Links' properties may be split into several groups according to their function:
Links have two crucial attributes: source
and target
. They define the starting point and the end point of the link. They can be defined with a Cell id (optionally, with additional subelement/magnet/port reference) or with a Point:
var link1 = new joint.dia.Link({
source: { id: sourceId },
target: { id: targetId, port: portId }
});
var link2 = new joint.dia.Link({
source: { id: sourceId },
target: { x: 100, y: 100 }
});
The source
and target
properties accept additional modifier properties that modify the actual position of the link end: anchor
/linkAnchor
, and connectionPoint
.
Additionally, the path of the link is determined by its vertices
, and the applied router
and connector
. All these attributes are described in more detail in link geometry documentation.
Each joint.dia.Link
defines its own SVG markup
which is then used by joint.dia.LinkView
to render the link to the paper. For instance, the joint.shapes.standard.Link
(which inherits from joint.dia.Link
) defines its markup using the JSON array notation as follows:
markup: [{
tagName: 'path',
selector: 'wrapper',
attributes: {
'fill': 'none',
'cursor': 'pointer',
'stroke': 'transparent',
'stroke-linecap': 'round'
}
}, {
tagName: 'path',
selector: 'line',
attributes: {
'fill': 'none',
'pointer-events': 'none'
}
}]
As we can see, the joint.shapes.standard.Link
shape consists of two subelements
: one SVGPathElement named 'wrapper'
and one SVGPathElement named 'line'
. The attrs
object refers to the subelements' names (selectors
) to provide SVG attributes to these constituent SVGElements.
joint.dia.Link
is deprecated. Use links from standard
namespace.
The joint.dia.Link
markup looks like:
<path class="connection"/>
<path class="marker-source"/>
<path class="marker-target"/>
<path class="connection-wrap"/>
<g class="labels" />
<g class="marker-vertices"/>
<g class="marker-arrowheads"/>
<g class="link-tools" />
As you can see, the link consists of a couple of SVG path elements and couple of SVG group elements:
.connection
is the actual line of the link..connection-wrap
is an SVG path element that covers the .connection
element and is usually thicker so that the link is able to
handle pointer events (mousedown, mousemove, mouseup) that didn't target the thin .connection
path exactly. This makes it easy
to "grab" the link even though the mouse cursor didn't point exactly at the (usually thin) .connection
path element..marker-source
and .marker-target
are the arrowheads of the link.link.attr({
'.connection': { stroke: 'blue' },
'.marker-source': { fill: 'red', d: 'M 10 0 L 0 5 L 10 10 z' },
'.marker-target': { fill: 'yellow', d: 'M 10 0 L 0 5 L 10 10 z' }
});
The markup
attribute is not the only presentation attribute that may be specified for a joint.dia.Link
. However, all the following Link properties are deprecated and included only for backwards compatibility. Use link tools instead:
vertexMarkup
- provide default vertex markup for all vertices created on an instance of this Link type (on hover).toolMarkup
- provide custom tool markup for all instances of this Link type (on hover).doubleToolMarkup
- provide custom markup for second set of tools for all instances of this Link type (on hover, if linkView.doubleLinkTools
is true
).arrowheadMarkup
- provide custom arrowhead markup for all instances of this Link type (on hover).The keys of the attrs
object are selectors that match subelements defined in the link's markup
. The values of this object are SVG attributes that will be set on the selected subelements. One can find the full list of SVG attributes and their descriptions online, e.g. on MDN.
For example, in order to set a red stroke color on a subelement called 'line'
, the attrs
object would contain:
line: { stroke: 'red' }
If you simply need to change a value of an attribute, it is not recommended to modify the attrs
object directly. Instead, use the link.attr()
method.
We can use the joint.shapes.standard.Link
(which inherits from joint.dia.Link
) as an example. The attrs
object in its definition is provided below:
attrs: {
line: {
connection: true,
stroke: '#333333',
strokeWidth: 2,
strokeLinejoin: 'round',
targetMarker: {
'type': 'path',
'd': 'M 10 -5 0 0 10 5 z'
}
},
wrapper: {
connection: true,
strokeWidth: 10,
strokeLinejoin: 'round'
}
}
Notice that the object makes use of special JointJS attributes (e.g. connection
, targetMarker
) on top of standard SVG attributes (e.g. stroke
, strokeWidth
). All of these special attributes
are listed in the attributes section of this documentation. You should also refer to our tutorial on special attributes.
In the context of links, the most important special attribute is connection
. It specifies that the SVGPathElement(s) in question should follow the path of the Link's model, as provided by the interplay of link geometry methods.
Attributes defined in markup
are evaluated once at CellView creation,
while attributes defined in attrs
are evaluated on every model change. As JointJS special attributes usually depend on the
current state of the model, we should define them in attrs
, along with any other SVG attributes that will be modified
in the runtime of your application.
The z
property specifies the stack order of the element in the SVG DOM. An element with a higher z
value will be rendered in front of an element with a lower z
value. (This also applies to Elements.)
You may provide an array of labels to the link through the labels
attribute. Each can have its own markup, position, and attrs. See the link labels documentation. You should also refer to our tutorial on link labels.
It is also possible to pass custom attributes to the link. These may be useful to identify an individual link model for the purposes of linkView interaction (see LinkView
documentation for more information). For example, to only enable custom contextmenu interaction for link1
but not link2
:
var CustomLinkView = joint.dia.LinkView.extend({
contextmenu: function(evt, x, y) {
if (this.model.get('customLinkInteractions')) {
// only links with `customLinkInteractions: true`
this.addLabel(x, y);
}
}
});
var paper = new joint.dia.Paper({
//...
linkView: CustomLinkView,
interactive: function(cellView) {
if (cellView.model.get('customLinkInteractions')) {
// only links with `customLinkInteractions: true`
return { vertexAdd: false };
}
return true; // otherwise
}
});
var link1 = new joint.dia.Link({
//...
customLinkInteractions: true // right-click adds a label
});
var link2 = new joint.dia.Link({
//...
customLinkInteractions: false // or omit completely
});
Links trigger several special events, detailed in the link events documentation.
It is possible to extend the joint.dia.Link
class to create a custom link. A custom link may override Link properties to assign its own defaults. These values override builtin defaults, if provided, and are applied to all instances of the new Link type, unless an individual instance overrides them with its own values. The following Link properties are applicable in this context:
markup
- provide default link markup for all instances of this Link type, specified with a JSON array.attrs
- provide default link attributes for all instances of this Link type. These allow you to change the style and size of SVG elements, identified by their selectors.defaultLabel
- provide default properties (markup, attrs, position) for all labels created on an instance of this Link type.The values of these defaults may be important; the linkView.addLabel()
shortcut function is only capable of adding default labels to the link.
Creating custom links is explained in more detail in our tutorial.
Example:
var CustomLink = joint.dia.Link.define('examples.CustomLink', {
defaultLabel: {
markup: [
{
tagName: 'circle',
selector: 'body'
}, {
tagName: 'text',
selector: 'label'
}
],
attrs: {
label: {
text: '%', // default label text
fill: '#ff0000', // default text color
fontSize: 14,
textAnchor: 'middle',
yAlignment: 'middle',
pointerEvents: 'none'
},
body: {
ref: 'label',
fill: '#ffffff',
stroke: '#000000',
strokeWidth: 1,
refR: 1,
refCx: 0,
refCy: 0
}
},
position: {
distance: 0.5, // place label at midpoint by default
offset: {
y: -20 // offset label by 20px upwards by default
},
args: {
absoluteOffset: true // keep offset absolute when moving by default
}
}
}
});
var link = new CustomLink({
//...
});
To ensure backwards compatibility, the joint.dia.Link
class comes with a private builtin defaultLabel
attribute. It is reproduced here for reference:
defaultLabel: {
// builtin default markup:
// used if neither defaultLabel.markup
// nor label.markup is set
markup: [
{
tagName: 'rect',
selector: 'rect'
}, {
tagName: 'text',
selector: 'text'
}
],
// builtin default attributes:
// applied only if builtin default markup is used
attrs: {
text: {
fill: '#000000',
fontSize: 14,
textAnchor: 'middle',
yAlignment: 'middle',
pointerEvents: 'none'
},
rect: {
ref: 'text',
fill: '#ffffff',
rx: 3,
ry: 3,
refWidth: 1,
refHeight: 1,
refX: 0,
refY: 0
}
},
// builtin default position:
// used if neither defaultLabel.position
// nor label.position is set
position: {
distance: 0.5
}
}
If custom position
object is not provided (neither as a type default nor as an instance value), builtin default label position is applied instead ({ distance: 0.5 }
).
Furthermore, if custom markup
is not provided (neither as a type default nor as an instance value), builtin default label markup is applied, alongside the builtin default label attrs object. However, it is very highly recommended that you provide both your own markup
and your own attrs
- unless you want to use the builtin default precisely as-is.
change
- generic event triggered for any change on the linkchange:source
- triggered when the link changes its sourcechange:target
- triggered when the link changes its targetchange:attrs
- triggered when the link changes its attributeschange:smooth
- (deprecated) triggered when the link toggled interpolation change:manhattan
- (deprecated) triggered when the link toggled orthogonal routingchange:vertices
- triggered when the link changes its vertices array change:z
- triggered when the link is moved in the z-level (toFront and toBack)transition:start
- triggered when a transition starts.transition:end
- triggered when a transition ends.link.on('change:source', function() { alert('source of the link changed') })
The shape of a link is determined by five properties - source
, target
, vertices
, router
and connector
.
The source
and target
properties have to be provided when creating a Link. Either a Point can be provided (with x
and y
properties) or a Cell (Element or Link, either directly or via an id
property).
link.source(new g.Point(100, 100));
link.source({ x: 100, y: 100 });
link.source(rect);
link.source({ id: rect.id });
link.source(link2);
link.source({ id: link2.id });
If the end is specified as a Cell, a specific subelement on that cell may be identified for use as the link end. The selector
property allows specifying the subelement with a selector string, while the magnet
property uses magnet id, and the port
property (on Elements only) uses the port id.
link.source(rect, {
selector: 'connectorSquare'
});
link.source(link2, {
selector: 'midPointCircle'
});
In any case, we need to obtain a single point from the object provided to the source/target property. That point is found according to additional properties provided to the function. The accepted modifier properties depend on the class of the provided object:
Point
or an object with x
and y
properties - coordinates of the point are used as the coordinates of the link's anchor directly. No modification of any kind. (Ignores anchor/linkAnchor/connectionPoint properties, if provided.)Element
- the precise position of the link's end anchor depends on used anchor
function, with additional optical modifications applied by connectionPoint
function. (Ignores linkAnchor property, if provided.)Link
- the precise position of the link's end anchor depends on provided linkAnchor
function. (Ignores anchor/connectionPoint properties, if provided.)Link
with specified subelement
/magnet
property - same as Element
(see above).The connectionStrategy
paper option is also relevant to mention in this context. It determines what happens to the link end when it is modified due to specific kinds of user interaction.
If the link end (source/target) is an Element (or a subelement/magnet of a Link – but not a Link itself), the precise position of the link end's anchor may be specified by the anchor
property. Every link has two anchors; one at the source end and one at the target end.
A link end anchor is a point inside a given (sub)element that the link path wants to connect to at that source/target end - if it were not obstructed by the body of the element itself (it is the role of connectionPoints to then take the obstructing end element itself into account). Alongside link vertices, source and target anchors determine the basic link path. Then, it is the job of connectionPoints, routers, and connectors to modify that path to make it look good.
The anchor functions reference the end element to find the anchor point - e.g. the center of the end element, or its top-right corner. Notably, anchor functions also allow you to offset found anchor points by a custom distance in both dimensions from the standard position (e.g. 10 pixels to the right and 20 pixels below the center of an end element). Several pre-made anchors are provided inside the JointJS library in the joint.anchors
namespace.
link.source(rect, {
anchor: {
name: 'bottomLeft',
args: {
dx: 20,
dy: -10
}
}
});
If an anchor function is not provided, the defaultAnchor
paper option is used instead. The joint.anchors.center
function is used by default.
If the link end (source/target) is a Link (not an Element or a Link subelement/magnet), the precise position of the link end's anchor may be specified by the linkAnchor
property. If a link anchor method is used, no connectionPoints are applied.
A link end link anchor is a point on another link that this link's path wants to connect to at that source/target end. Alongside link vertices, source and target anchors determine the basic link path.
The link anchor functions reference the end link to find the anchor point - e.g. a point at a given ratio from the start, or the closest point. Several pre-made linkAnchors are provided inside the JointJS library in the joint.linkAnchors
namespace.
link.source(link2, {
linkAnchor: {
name: 'connectionRatio',
args: {
ratio: 0.25
}
}
});
If a link anchor function is not provided, the defaultLinkAnchor
paper option is used instead. The joint.linkAnchors.connectionRatio
function with a value of 0.5
is used by default (i.e. the anchor is placed at the midpoint of a Link by default).
The link end's connection point may be specified by the connectionPoint
property. Every link has two connection points; one at the source end and one at the target end.
A link connection point is the point at which the link path actually ends at, taking the end element into account. This point will always lie on the link path (the line connecting link anchors and vertices together, in order).
The connectionPoints are found by considering intersections between the link path and a desired feature of the end element (e.g. bounding box, shape boundary, anchor). Although connectionPoints are not capable of being offset off the link path (anchors should be used to modify the link path if this is required), they can be offset along the path - e.g. to form a gap between the element and the actual link. Several pre-made connectionPoints are provided in the JointJS library in the joint.connectionPoints
namespace.
link.source(rect, {
connectionPoint: {
name: 'boundary',
args: {
offset: 5
}
}
});
If a connection point function is not provided, the defaultConnectionPoint
paper option is used instead. The joint.connectionPoints.bbox
function is used by default.
Related to anchors and connectionPoints is the connectionStrategy
paper option. It allows you to specify which anchor
and connectionPoint
properties should be assigned to end elements in response to user interaction (e.g. in response to dragging a link arrowhead onto a new element). This setting is necessary because assigned anchor
and connectionPoint
are not preserved when the end element is reassigned by the user - and the paper's defaultAnchor
and defaultConnectionPoint
are used instead.
Several pre-made connectionStrategies are provided inside the JointJS library in the joint.connectionStrategies
namespace, but you may find it necessary to create your own custom connection strategies. This is a paper-level setting; connectionStrategies not assigned on a link-by-link basis.
paper.options.connectionStrategy = joint.connectionStrategies.pinAbsolute;
If a connection strategy is not provided, the joint.connectionStrategies.useDefaults
function is used by default.
The vertices
array is an array of user-defined points for the link to pass through. Alongside the source and target anchors, the link vertices determine the basic link path. This skeleton path is then used for determining the link route. The vertices can be accessed with the link.vertices()
function and related functions.
link.vertices();
link.vertices([{ x: 100, y: 120 }, { x: 150, y: 60 }]);
Routers take an array of link vertices and transform them into an array of route points that the link should go through. This route is then used for generating the connection SVG path commands. The router
property of a link can be accessed with the link.router()
function.
A collection of pre-made routers is provided inside the JointJS library in the joint.routers
namespace. This includes smart routers
that are able to automatically avoid obstacles (elements) in their way.
link.router('manhattan', {
excludeEnds: ['source'],
excludeTypes: ['myNamespace.MyCommentElement'],
startDirections: ['top'],
endDirections: ['bottom']
});
If a router is not provided, the defaultRouter
paper option is used instead. The joint.routers.normal
function is used by default.
Connectors take an array of link route points and generate SVG path commands so that the link can be rendered. The connector
property of a link can be accessed with the link.connector()
function.
A collection of pre-made connectors is provided inside the JointJS library in the joint.connectors
namespace.
link.connector('rounded', {
raw: true,
radius: 20
});
If a connector is not provided, the defaultConnector
paper option is used instead. The joint.connectors.normal
function is used by default.
Note that the modular architecture of JointJS allows mixing-and-matching connectors with routers as desired; for example, a link may be specified to use the jumpover
connector on top of the manhattan
router:
var link = new joint.shapes.standard.Link();
link.source(rect);
link.target(rect2);
link.router('manhattan');
link.connector('jumpover');
JointJS supports adding labels on links. One link can have multiple labels, and each label can have different properties. For a quick introduction to link labels, see our tutorial
Properties recognized by JointJS are summarized in the following TypeScript-like schema:
{
markup?: string | Array<{
tagName: SVGElement,
selector?: string
}>,
attrs?: {
[key: selector]: {
[key: SVG attribute | JointJS attribute]: any
} | null
},
position?: number | {
distance: number,
offset?: number | { x: number, y: number },
angle?: number,
args?: {
absoluteDistance?: boolean,
reverseDistance?: boolean,
absoluteOffset?: boolean,
keepGradient?: boolean,
ensureLegibility?: boolean
}
}
}
The markup
property specifies the markup of the label. It can be provided either as a parsable SVG (e.g. '<rect /><text />'
), or as a JSON array (e.g.[{ tagName: 'rect' }, { tagName: 'text' }]
). The JSON allows the user to specify custom selectors for the SVGElements; these can then be used for targeting elements from within the attrs
property.
If markup
is not provided on the label, markup is taken from the Link type's defaultLabel.markup
property. A custom Link type can be created by the user, providing a defaultLabel
(see the Link
documentation for more information). However, if the used link type does not provide defaultLabel.markup
(this includes joint.dia.Link
and joint.shapes.standard.Link
), the builtin default Link markup is used, which defines markup as a JSON array with a 'body'
(a <rect>
SVGElement) under a 'label'
(a <text>
SVGElement).
The attrs
property is an object where the keys are CSS selectors (referring to custom selectors or SVGElements specified in markup
, e.g. body
in the above example). They are expected to contain objects that specify native SVG and/or JointJS special attributes (e.g. fill
), alongside the value to be assigned (e.g. 'white'
).
If the Link type provides defaultLabel.attrs
, these attrs are merged with label.attrs
. This allows you to reference selectors from defaultLabel.markup
in label.attrs
and, for example, simply add an attribute (attrs: { body: { stroke: 'black' } }
).
If the builtin default markup is used (i.e. no custom label.markup
was provided, and no defaultLabel.markup
), several builtin default attrs
are applied for reasons of backwards compatibility. These are merged with defaultLabel.attrs
(if present on the Link prototype) and then label.attrs
(if provided). See Link
documentation for more information.
Finally, the position
property specifies the position of the label, relative to the SVG path of the link. It may be defined as a number or as an object with distance
and optionally offset
and args
properties. If no position
is provided, the builtin default ({ distance: 0.5 }
) is used to maintain backwards compatibility.
number |
|
||||||||||||||||||||||||||||
object |
If
|
link.addTo(graph)
Add the link to the graph
(an instance of joint.dia.Graph
). This is equivalent to calling graph.addCell(link)
.
link.appendLabel(label [, opt])
Add a new label
at the end of the labels
array.
link.attr(attrs, [, opt])
Set presentation attributes (SVG and JointJS attributes) on subelements. This is a method analogous to attr method of joint.dia.Element
. The keys of the attrs
object are selectors (JSON Markup Selector or CSS Selector) matching the SVG element the link consists of. The values are objects containing SVG attributes and their values. attrs
object will be mixed with attrs
property of the link
model. This is a convenient way of rewriting only some of the attributes of the SVG elements. For overwriting all attributes of all SVG elements, use link.set('attrs', attrs)
.
link.attr({
// selector as defined in JSON markup
line: {
stroke: 'red',
targetMarker: {
type: 'circle',
r: 5
}
}
});
element.attr(path, value [, opt])
An alternative call using a string path and a value:
link.attr('line/stroke', 'red');
link.attr(['line', 'targetMarker', 'type'], 'path');
element.attr([path])
Get attribute value defined by a path. If no path provided the whole attrs
object is returned.
var color = link.attr('line/stroke');
var targetMarkerType = link.attr(['line', 'targetMarker', 'type']);
link.clone()
Returns a new instance of the link with identical attributes.
link.connector()
Return a shallow copy of the connector
property of the link.
For backwards compatibility, if there is no connector, the function also checks whether the legacy smooth
property is set on the link and returns { name: smooth }
if it is.
link.connector(connector [, opt])
Set the connector
of the link.
If the connector
argument is an object, it is expected to have the form { name: connectorName, args?: connectorArgs }
. Here connectorName
is expected to match either the name of a built-in connector or the name of a custom connector.
If the connector
argument is a function, it is expected to define a custom connector with the signature function(sourcePoint, targetPoint, vertices, connectorArgs, linkView)
that returns a string representing the SVG path data that will be used to render the link.
link.connector(connectorName [, connectorArgs, opt])
Set the connector
of the link to have the value { name: connectorName, args: connectorArgs }
.
The connectorName
string is expected to match either the name of a built-in connector or the name of a custom connector. The connectorArgs
parameter is optional.
link.disconnect()
Disconnect the link from its source
and target
elements. The source
and target
then become a point at [0,0]
.
link.findView(paper)
Find view (joint.dia.LinkView
) for the link model in the paper
. This is a shortcut to the equivalent call paper.findViewByModel(link)
.
link.getAncestors()
Return an array of all the ancestors of this link starting from the immediate parent all the way up to the most distant ancestor.
link.getBBox()
Return the bounding box of the link model, represented as a g.Rect
.
This method creates the bounding box from the link polyline (see the link.getPolyline
function). This simplified heuristic may cause portions of curved links to protrude out of the reported bounding box. See the documentation of the linkView.getBBox
function for details about the differences.
link.getPolyline()
Return a polyline that approximates the shape of the link. The polyline is created from the two link endpoints, with link vertices in between.
link.getSourceCell()
Return the source cell (element or link) of the link, or null
if the element's source is a point.
link.getSourceElement()
Return the source element of the link or null
if there is none.
link.getSourcePoint()
Return the source point of the link.
link.getTargetCell()
Return the target cell (element or link) of the link, or null
if the element's target is a point.
link.getTargetElement()
Return the target element of the link or null
if there is none.
link.getTargetPoint()
Return the target point of the link.
link.getTransitions()
Return an array of all active transitions (their paths).
link.hasLabels()
Return true
if the link has at least one label. Return false
otherwise.
link.hasLoop([opt])
Return true
if this link is a loop link
. In a loop link
source
and target
are equal.
If opt.deep
is true
, the notion of a loop link
is extended to a deep hierarchy. For example, if the link connects a parent element with one of its embedded elements, the link is considered a loop link
, as well.
link.insertLabel(index, label [, opt])
Add a new label
at index
. Pass index -1
to add the label at the end of the labels array.
link.insertVertex(index, vertex [, opt])
Add a new vertex
at index
. Pass index -1
to add the vertex at the end of the vertices array.
link.isElement()
Always returns false
. The reason the method is here is that both joint.dia.Element
and joint.dia.Link
inherit from joint.dia.Cell
. This method is useful if you don't know what the cell is. Calling cell.isElement()
is equivalent to cell instanceof joint.dia.Element
. Example:
var cell = graph.getCell(myId)
if (cell.isElement()) {
// Do something if the cell is an element.
}
link.isEmbeddedIn(element [, opt])
Return true
if the link is embedded in an element element
.
If opt.deep
is false
, only direct parentage will be checked. opt.deep
is true
by default.
link.isLink()
Always returns true
. The reason the method is here is that both joint.dia.Element
and joint.dia.Link
inherit from joint.dia.Cell
. This method is useful if you don't know what the cell is. Calling cell.isLink()
is equivalent to cell instanceof joint.dia.Link
. Example:
var cell = graph.getCell(myId)
if (cell.isLink()) {
// Do something if the cell is a link.
}
link.label(index)
Return the label at index
.
link.label(index, properties [, opt])
Update properties
of the label at index
. By default, the new properties are merged into the old ones; pass the { rewrite: true }
option along to disregard old properties.
Example usage:
link.label(0, {
markup: [
{
tagName: 'rect',
selector: 'body'
}, {
tagName: 'text',
selector: 'label'
}
],
attrs: {
body: {
fill: 'white' // white background
},
label: {
text: 'my label', // text to show
fill: 'blue' // blue text
}
},
position: {
distance: 0.5, // midway on the connection path
offset: {
x: 10, // 10 local x units to the right
y: -5 // 5 local y units above
},
angle: 45, // rotate by 45 degrees clockwise
args: {
keepGradient: true, // auto-rotate by path slope at distance
ensureLegibility: true // auto-rotate label if upside-down
}
}
});
Note that all labels are stored in an array on the link model under the attribute labels
. Use the link.labels
function to access the array.
link.labels()
Return a shallow copy of labels array.
link.labels(labelsArray [, opt])
Set array of labels on the link.
link.prop(properties)
Set properties, possibly nested, on the element model. This is equivalent to the attr() method but this time for custom data properties.
link.prop('name/first', 'John')
link.prop('name/first') // 'John'
link.prop({ name: { first: 'John' } })
// Nested arrays are supported too:
link.prop('mylist/0/data/0/value', 50)
link.prop({ mylist: [ { data: [ { value: 50 } ] } ] })
As you can see, this is the exact same method as the joint.dia.Element.prop() method.
To overwrite attributes, enable rewrite mode by adding { rewrite: true } as the 3rd argument. This differs from the default behaviour which is to merge our properties.link.prop('custom/state/isVisible', true);
link.prop('custom/state', { isActive: false }, { rewrite: true });
// Output from link.toJSON();
// We can see our attributes have been overwritten
{
"type": "link",
"source": { "x": 10, "y": 10 },
"target": { "x": 100, "y": 100 },
"id": "4ddf9c16-649b-40b3-96b3-7150711da955",
"custom": {
"state": {
"isActive": false
}
},
"attrs": {}
}
Advanced: Pass { isolate: true }
if the property change does not affect the connected links. Typically, changing the link color has zero effect on attached links. By default, the link and all connected links are updated.
link.attr(['line', 'stroke'], 'red', { isolate: true });
link.remove()
Remove the link from the graph.
link.removeAttr(path [, opt])
Remove a previously set attribute from the link. path
can either be a string that specifies the path to the (possibly nested) attribute to be removed, or an array of more paths. The associated link view makes sure the link gets re-rendered properly.
If opt
is passed, it can contain data that is passed over to the event listeners for the change:attrs
event triggered on the link itself and also on the graph the link is in.
link.removeLabel(index [, opt])
Remove label
at index
. Pass index -1
to remove the last label of the labels array.
link.removeVertex(index [, opt])
Remove vertex
at index
. Pass index -1
to remove the last vertex of the vertices array.
link.reparent()
Automatically find and set the best parent element for the link so that when the parent element is moved, the link and all its vertices are moved too. The best parent is determined as the common ancestor of the source and target elements of the link. Useful for hierarchical diagrams. See the DEVS demo on how this can be used.
link.router()
Return a shallow copy of the router
property of the link.
For backwards compatibility, if there is no router, the function also checks whether the legacy manhattan
property is set on the link and returns { name: orthogonal }
if it is.
link.router(router [, opt])
Set the router
of the link.
If the router
argument is an object, it is expected to have the form { name: routerName, args?: routerArgs }
. Here routerName
is expected to match either the name of a built-in router or the name of a custom router.
If the router
argument is a function, it is expected to define a custom router with the signature function(vertices, routerArgs, linkView)
that returns an array of route points.
link.router(routerName [, routerArgs, opt])
Set the router
of the link to have the value { name: routerName, args: routerArgs }
.
The routerName
string is expected to match either the name of a built-in router or the name of a custom router. The routerArgs
parameter is optional.
link.scale(sx, sy, origin [, opt])
Scales the link's points (vertices) relative to the given origin.
link.source()
Return a shallow copy of the source
property of the link.
If the beginning of the link is connected to an element, an object in the format { id: elementID }
is returned. If the beginning of the link is specified as a point instead (the link is pinned
to the paper at that point), an object in the format { x: sourceX, y: sourceY }
is returned. Furthermore, any additional arguments of the source are returned alongside those properties.
If you need to be sure that a Cell is returned (and not a Point), use the link.getSourceCell
function instead. If you need an Element, use the link.getSourceElement
function.
link.source(source [, opt])
Set the source
of the link.
If the link is to be connected to an element, send an object in the format { id: element.id }
(or a joint.dia.Element
object). If the link is to be pinned to the paper, send an object in the format { x: sourceX, y: sourceY }
(or a g.Point
object).
link.source(rect);
link.source({ id: rect.id });
link.source(new g.Point(100, 100));
link.source({ x: 100, y: 100 });
Additional options may by provided to further specify the behavior of link at the source:
link.source(rect, {
selector: 'body',
anchor: {
name: 'bottomLeft',
args: {
dx: 20,
dy: -10
}
}
});
More information can be found in link source documentation.
link.stopTransitions([path])
Stops all running transitions. If parameter path
is provided, it will stop only transitions specified by this path.
link.target()
Return a shallow copy of the target
property of the link.
If the ending of the link is connected to an element, an object in the format { id: elementID }
is returned. If the ending of the link is specified as a point instead (the link is pinned
to the paper at that point), an object in the format { x: targetX, y: targetY }
is returned. Furthermore, any additional arguments of the target are returned alongside those properties.
If you need to be sure that a Cell is returned (and not a Point), use the link.getTargetCell
function instead. If you need an Element, use the link.getTargetElement
function.
link.target(target [, opt])
Set the target
of the link.
If the link is to be connected to an element, send an object in the format { id: element.id }
(or a joint.dia.Element
object). If the link is to be pinned to the paper, send an object in the format { x: targetX, y: targetY }
(or a g.Point
object).
link.target(rect);
link.target({ id: rect.id });
link.target(new g.Point(100, 100));
link.target({ x: 100, y: 100 });
Additional options may by provided to further specify the behavior of link at the target:
link.target(rect, {
selector: 'body',
anchor: {
name: 'bottomLeft',
args: {
dx: 20,
dy: -10
}
}
});
More information can be found in link target documentation.
link.toBack()
Move the link so it is behind all other cells (elements/links). This is a method analogous to toBack method of Joint.dia.Element
.
link.toFront()
Move the element so it is on top of all other cells (elements/links). This is a method analogous to toFront method of Joint.dia.Element
.
link.toJSON()
Return a copy of the link's attributes for JSON serialization. This is a method analogous to toJSON method of Joint.dia.Element
.
link.transition(path, value [, opt])
Allows changing of the link properties gradually over a period of time. This is method is analogous to the transition method of joint.dia.Element
.
link.transition('target', { x: 250, y: 250 }, {
delay: 100,
duration: 500,
timingFunction: joint.util.timing.bounce,
valueFunction: joint.util.interpolate.object
});
// will start changing the link target coordinates in 100ms, for period of 500ms and performing a bounce effect.
link.translate(tx, ty [, opt])
Translate the link vertices (and source and target if they are points) by tx
pixels in the x-axis and ty
pixels in the y-axis.
If opt
object is passed, it can contain data that is passed over the the event listeners for the change event on the link or graph.
link.vertex(index)
Return the vertex at index
.
link.vertex(index, vertex [, opt])
Update the vertex at index
with value of vertex
. By default the new value is merged into the old value. Pass the { rewrite: true }
option along to disregard the old value.
A vertex has only two properties, x
and y
. It may be defined as a g.Point object.
Example vertex:
link.vertex(0, {
x: 100,
y: 200
});
Note that all vertices are stored in an array on the link model under the attribute vertices
. Use the link.vertices
function to access the array.
link.vertices()
Return a shallow copy of vertices array.
link.vertices(verticesArray [, opt])
Set array of vertices on the link.
The view for the joint.dia.Link model. It inherits from joint.dia.CellView and is responsible for:
To find the view associated with a specific link model, use the paper.findViewByModel()
method:
var linkView = paper.findViewByModel(link);
It is possible to use a custom default link view for all your links in a paper. This can be set up via the linkView
option on the paper object. Several options in joint.dia.LinkView
may be overridden in a custom LinkView:
105
.longLinkLength
). Defaults to false
.doubleLinkTools: true
, render link tools on both ends of the link. Defaults to 155
.40
.longLinkLength
and doubleLinkTools: true
. Defaults to 65
.A custom LinkView type may also override default LinkView event handlers, or provide new ones. It may be necessary to modify the interactive
paper option to prevent interference from builtin event handlers (most notably vertexAdd
which listens for pointerdown events).
Example:
var CustomLinkView = joint.dia.LinkView.extend({
// custom interactions:
pointerdblclick: function(evt, x, y) {
this.addVertex(x, y);
},
contextmenu: function(evt, x, y) {
this.addLabel(x, y);
},
// custom options:
options: joint.util.defaults({
doubleLinkTools: true,
}, joint.dia.LinkView.prototype.options)
});
var paper = new joint.dia.Paper({
//...
linkView: CustomLinkView,
interactive: { vertexAdd: false } // disable default vertexAdd interaction
});
linkView.addLabel(x, y [, angle, opt])
Add a new default label to the link at the (x,y) coordinates provided. See also the link.appendLabel()
function.
linkView.addLabel(point [, angle, opt])
Add a new default label to the link at the coordinates specified by point
. See also the link.appendLabel()
function.
In either case, this method uses the linkView.getLabelPosition()
function to determine the new label's position
. By default, position.distance
is recorded relative to connection length (as a number in the [0,1]
range), and position.offset
is set relative to the connection (as a number). This behavior may be changed by providing an opt
object with some of the accepted boolean flags:
absoluteDistance: true
records distance
absolutely (as distance from beginning of link)reverseDistance: true
switches distance
to be calculated from end of link, if absoluteDistance
absoluteOffset: true
records offset
absolutely (as x
and y
from connection)The angle
parameter, if provided, is saved as position.angle
attribute inside the returned object. Two additional flags, which may be passed in the opt
object, provide more control over label rotation:
keepGradient: true
- adjust the rotation of the label to match the angle of incline of the path at position.distance
ensureLegible: true
- if the label text ends up being upside-down, rotate the label by additional 180 degrees to ensure that the text stays legible, if keepGradient
The opt
object passed to the label is recorded as label.position.args
. The label uses these options during subsequent labelMove interactions.
This function is useful within custom linkView
event listener definitions:
var CustomLinkView = joint.dia.LinkView.extend({
contextmenu: function(evt, x, y) {
this.addLabel(x, y, 45, {
absoluteDistance: true,
reverseDistance: true, // applied only when absoluteDistance is set
absoluteOffset: true,
keepGradient: true,
ensureLegibility: true // applied only when keepGradient is set
});
}
});
var paper = new joint.dia.Paper({
// ...
linkView: CustomLinkView,
interactive: { vertexAdd: false } // disable default vertexAdd interaction
});
linkView.addTools(toolsView)
Add the provided toolsView
(of the joint.dia.ToolsView
type) to the link view.
Adding a tools view to a link view is the last (third) step in the process of setting up link tools on a link view:
// 1) creating link tools
var verticesTool = new joint.linkTools.Vertices();
var segmentsTool = new joint.linkTools.Segments();
var boundaryTool = new joint.linkTools.Boundary();
// 2) creating a tools view
var toolsView = new joint.dia.ToolsView({
name: 'basic-tools',
tools: [verticesTool, segmentsTool, boundaryTool]
});
// 3) attaching to a link view
var linkView = link.findView(paper);
linkView.addTools(toolsView);
Every link view we want to attach to requires its own tools view object (ToolsView
objects are automatically reassigned to the last link view they are added to). Similarly, every tools view we create requires its own set of tools (ToolView
objects are automatically reassigned to the last toolsView.tools
array they were made part of).
The link tools are added in the visible state. Use the linkView.hideTools
function if this behavior is not desirable (e.g. if you want the link tools to appear in response to user interaction). Example:
linkView.addTools(toolsView);
linkView.hideTools();
paper.on('link:mouseenter', function(linkView) {
linkView.showTools();
});
paper.on('link:mouseleave', function(linkView) {
linkView.hideTools();
});
Note that the above example may not work as expected if toolsView
includes the SourceArrowhead
tool and/or the TargetArrowhead
tool - the link tools view might not be hidden when the link is reconnected to a topic. See our link tools tutorial for more information.
linkView.addVertex(x, y)
Add a new default vertex to the link at the coordinates provided, and let the linkView automatically determine the index of the new vertex in the vertices array. If you need to add the vertex at a custom index, use the link.addVertex()
function instead.
This method uses the linkView.getVertexIndex()
function to determine the index of the new vertex in the vertices
array. The linkView checks the distance of vertices in the link.vertices
array from the beginning of path and compares it to the distance of the added vertex. The new vertex is inserted before the first farther vertex in the vertices
array.
This function is useful within custom linkView
event listener definitions. The following example adds a new vertex on a double click event, instead of a pointerdown event (which is the default behavior):
var CustomLinkView = joint.dia.LinkView.extend({
pointerdblclick: function(evt, x, y) {
this.addVertex(x, y);
}
});
var paper = new joint.dia.Paper({
// ...
linkView: CustomLinkView,
interactive: { vertexAdd: false } // disable default vertexAdd interaction
});
linkView.findLabelNode(index)
Return the root SVGElement of the label at given index. If label at index doesn't exist, null
is returned.
linkView.findLabelNode(index, selector)
Return an SVGElement referenced by selector of the label at given index. If there is no label at index or no SVGElement was found by the selector, null
is returned.
linkView.getBBox([opt])
Return a bounding box of the link view.
If opt.useModelGeometry
option is set to true
, the resulting bounding box is calculated based on the dimensions of the link model. (This means that a simplified polyline is used and portions of curved links may be sticking out
of the reported bounding box.) This behavior is similar to the link.getBBox
function – but the linkView
function transforms the bounding box to match joint.dia.Paper
translation and scaling.
linkView.getClosestPoint(point)
Return the point on the connection that lies closest to point
.
linkView.getClosestPointLength(point)
Return the length of the connection up to the point that lies closest to point
.
linkView.getClosestPointRatio(point)
Return the ratio (normalized length) of the connection up to the point that lies closest to point
. The returned value lies in the interval [0,1]
(inclusive).
linkView.getConnection()
Return a geometric representation of the connection (instance of g.Path).
linkView.getConnectionLength()
Return a cached total length of the connection in pixels.
linkView.getConnectionSubdivisions()
Return a cached array of segment subdivision arrays of the connection. (See g.Path.prototype.getSegmentSubdivisons()
documentation for more information.)
linkView.getLabelCoordinates(labelPosition)
Return the x
and y
coordinates based on the provided labelPosition
object.
See link.label()
documentation for more information about the position
object.
An object in the format { x: number, y: number }
is returned.
linkView.getLabelPosition(x, y [, angle, opt])
Return a label position
object based on the x
and y
coordinates provided.
The function translates the provided coordinates and angle
into an object with three fields:
distance
- the distance (following the line) of the point on the line that is closest to point x,y
.offset
- the distance between the closest point and the point x,y
.angle
- the angle of the label relative to the connection line, as determined by the angle
parameter, or 0
if angle
was not specified.
By default, position.distance
is calculated as relative to connection length (as a number in the [0,1]
range that records the length ratio), and position.offset
is calculated as relative to the connection (as a number recording the perpendicular distance). The user may change this behavior by providing an opt
object with some of the following accepted boolean flags:
absoluteDistance: true
- record distance
absolutely (as absolute distance from beginning of link, a positive number)reverseDistance: true
- if absoluteDistance: true
, record distance
absolutely from end of link (as a negative number)absoluteOffset: true
- record offset
absolutely (as x
and y
distance from closest point)Please note that if the absoluteOffset
flag is not set, label can only be placed/moved in the area that is reachable by lines perpendicular to the link (that is, the label can never be moved beyond link endpoints).
Two additional flags, which may be passed in the opt
object, provide control over label rotation:
keepGradient: true
- adjust the rotation of the label to match the angle of incline of the path at position.distance
ensureLegible: true
- if the label text ends up being upside-down, rotate the label by additional 180 degrees to ensure that the text stays legible, if keepGradient
The opt
object passed to the label is recorded as label.position.args
. The label uses these options during subsequent labelMove interactions.
An object in the following format is returned:
{
distance: number,
offset: number | { x: number, y: number },
angle: number,
args?: {
absoluteDistance?: boolean,
reverseDistance?: boolean, // applied only when absoluteDistance is set
absoluteOffset?: boolean,
keepGradient?: boolean,
ensureLegible?: boolean // applied only when keepGradient is set
}
}
See link.label()
documentation for more information about the position
object.
This function can be used to add a custom label to the link.labels
array, in situations when the linkView.addLabel()
function is not sufficient. For example:
var CustomLinkView = joint.dia.LinkView.extend({
contextmenu: function(evt, x, y) {
var idx = -1; // add at end of `labels`
var label = {
markup: '<g class="label"><circle /><path /></g>',
attrs: {
circle: {
r: 15,
fill: 'lightgray',
stroke: 'black',
strokeWidth: 2
},
path: {
d: 'M 0 -15 0 -35 20 -35',
stroke: 'black',
strokeWidth: 2,
fill: 'none'
}
},
position: this.getLabelPosition(x, y, 45, {
absoluteOffset: true,
keepGradient: true,
ensureLegible: true
})
}
this.model.addLabel(idx, label);
}
});
var paper = new joint.dia.Paper({
// ...
linkView: CustomLinkView,
interactive: { vertexAdd: false } // disable default vertexAdd interaction
});
elementView.getNodeBBox(magnet)
Return the bounding box of the SVGElement provided as magnet
(model of this link view).
Use the paper.localToPaperRect
function to transform the returned bounding box to match the paper's translation and scaling.
elementView.getNodeUnrotatedBBox(magnet)
Return the unrotated bounding box of the SVGElement provided as magnet
(model of this link view).
Use the paper.localToPaperRect
function to transform the returned bounding box to match the paper's translation and scaling.
linkView.getPointAtLength(length)
Return the point on the path that lies length
away from the beginning of the connection.
linkView.getPointAtRatio(ratio)
Return the point on the path that lies ratio
(normalized length) away from the beginning of the connection.
linkView.getSerializedConnection()
Return a cached path data of the connection (the value of the 'd'
SVGPathElement attribute).
linkView.getTangentAtLength(length)
Return a line tangent to the path at point that lies length
away from the beginning of the connection.
linkView.getTangentAtRatio(ratio)
Return a line tangent to the path at point that lies ratio
(normalized length) away from the beginning of the connection.
linkView.getVertexIndex(x, y)
Return the vertices
array index at which to insert a new vertex with the provided x
and y
coordinates.
The linkView finds the point on the connection that lies closest to the point x,y
. Then, the linkView iterates over the vertices in the link.vertices
array until one is found that lies farther than the identified closest point. The index of the farther point is returned.
The returned index can be used as the first argument of the link.addVertex
function.
linkView.hasTools()
Return true
if this link view has a tools view attached.
linkView.hasTools(name)
Return true
if this link view has a tools view of the provided name
attached.
linkView.hideTools()
Hide all tools attached to this link view.
linkView.removeRedundantLinearVertices([opt])
Remove any redundant vertices from the model's vertices
array. Return the number of removed vertices.
A vertex is considered redundant
if it lies on the straight line formed by the vertex that immediately precedes it and the vertex that immediately follows it. (Intuitively speaking, a vertex is considered redundant
if its removal from the link does not change the shape of the link.)
linkView.removeTools()
Remove the tools view attached to this link view.
linkView.requestConnectionUpdate()
Schedule an update of the connection (anchors, connection points, route and connector) of this link view in the next animation frame (for paper async
mode) or run immediately (for paper sync
mode). Useful for links with an automatic router (such as manhattan
) if the route has to be recalculated due to another element in the graph changing its position.
linkView.sendToken(token [, opt, callback])
Send a token along the link. token
is an SVG element (or Vectorizer element) that will be animated along the link path for opt.duration
milliseconds (default is 1000ms). The callback
function will be called once the token reaches the end of the link path.
opt.direction
specifies whether the animation should be played forwards ('normal'
- from the link source to target, the default) or backwards ('reverse'
).
Use opt.connection
to specify the SVGPathElement for the token to follow. It expects a string selector, e.g. '.connection'
.
// Send an SVG circle token along the link.
var vCircle = V('circle', { r: 7, fill: 'green' });
link.findView(paper).sendToken(vCircle, { duration: 500, direction: 'reverse' }, function() {
console.log('animation end');
});
Note that in the code above, we use the Vectorizer mini-library to create the SVG circle
element.
See the Petri Net simulator demo for a full working example.
linkView.showTools()
Show all tools attached to this link view.
linkView.sourceAnchor
A prototype variable. Contains a g.Point that records the position of the source anchor of the link, as determined by the anchor function specified on the link's source.
linkView.sourceBBox
A prototype variable. Contains a g.Rect that records the position and dimensions of the bounding box of the source magnet (element/subelement/port), as determined by the link's source definition.
If the source is defined as a point, a g.Rect is returned that records the position of the point and has the dimensions (1,1).
linkView.sourcePoint
A prototype variable. Contains a g.Point that records the position of the source connection point of the link, as determined by the connection point function specified on the link's source.
linkView.targetAnchor
A prototype variable. Contains a g.Point that records the position of the target anchor of the link, as determined by the anchor function specified on the link's target.
linkView.targetBBox
A prototype variable. Contains a g.Rect that records the position and dimensions of the bounding box of the target magnet (element/subelement/port), as determined by the link's target definition.
If the target is defined as a point, a g.Rect is returned that records the position of the point and has the dimensions (1,1).
linkView.targetPoint
A prototype variable. Contains a g.Point that records the position of the target connection point of the link, as determined by the connection point function specified on the link's target.
joint.dia.Paper is the view for the joint.dia.Graph
model. It inherits from Backbone View. Accepts an options object in its constructor with numerous settings.
When a paper is associated with a graph, the paper makes sure that all the cells added to the graph are automatically rendered.
var graph = new joint.dia.Graph
var paper = new joint.dia.Paper({
el: $('#paper'),
width: 600,
height: 400,
gridSize: 10,
model: graph
});
var rect = new joint.shapes.basic.Rect({
position: { x: 50, y: 70 },
size: { width: 100, height: 40 }
});
graph.addCell(rect);
Paper automatically handles this change and renders the rectangle to the SVG document that it internally holds.
The following list contains events that you can react on in a paper:
pointerdblclick |
Triggered when pointer is double-clicked on a target (a The callback function is passed
|
||||||||
---|---|---|---|---|---|---|---|---|---|
pointerclick |
Triggered when pointer is clicked on a target without pointer movement (a The callback function is passed
|
||||||||
contextmenu |
Triggered when pointer is right-clicked on a target (a The callback function is passed
|
||||||||
pointerdown |
Triggered when pointer is pressed down on a target (a The callback function is passed
|
||||||||
pointermove |
Triggered when pointer is moved over a target while pressed down (a The callback function is passed
|
||||||||
pointerup |
Triggered when pointer is released on a target after being pressed down (a The callback function is passed
Calling |
||||||||
mouseover |
Triggered when pointer begins to hover directly over a target. The callback function is passed
|
||||||||
mouseout |
Triggered when pointer ceases to hover directly over a target. The callback function is passed
|
||||||||
mouseenter |
Triggered when pointer enters the area above a target. The callback function is passed
|
||||||||
mouseleave |
Triggered when pointer leaves the area above a target. The callback function is passed
|
||||||||
mousewheel |
Triggered when mouse wheel is rotated while pointer is on a target. The callback function is passed
|
||||||||
magnet |
Triggered when interacting with a magnet target. The callback function is passed
|
||||||||
cell:highlight |
Triggered when the The callback function is invoked with the following arguments:
|
||||||||
cell:unhighlight |
Triggered when the The callback function is invoked with the following arguments:
|
||||||||
cell:highlight:invalid |
Triggered when a highlighter makes an attempt to highlight a node, that doesn't exist on the CellView (e.g. a port was highlighted and now it is removed). The callback function is passed
|
||||||||
link:connect |
Triggered when a link is connected to a cell. The event is triggered after the user reconnects a link. The callback function is passed |
||||||||
link:disconnect |
Triggered when a link is disconnected from a cell. The event is triggered after the user reconnects a link. The callback function is passed |
||||||||
link:snap:connect |
Triggered when a link is connected to a cell. The event (or multiple events) can be triggered while the user is reconnecting a link and snapLinks option is enabled. The callback function is passed |
||||||||
link:snap:disconnect |
Triggered when a link is disconnected from a cell. The event (or multiple events) can be triggered while the user is reconnecting a link and snapLinks option is enabled. The callback function is passed |
||||||||
render:done |
Triggered when all scheduled updates are done (i.e. all scheduled batches have finished). |
||||||||
[custom] |
Custom cell event can be triggered on pointerdown with Event attribute. Calling |
An example of a simple blank:pointerdown
event listener:
paper.on('blank:pointerdown', function(evt, x, y) {
alert('pointerdown on a blank area in the paper.')
})
Consecutive pointerdown
, pointermove
and pointerup
events can share information through the evt.data
object:
// Create a new link by dragging
paper.on({
'blank:pointerdown': function(evt, x, y) {
var link = new joint.dia.Link();
link.set('source', { x: x, y: y });
link.set('target', { x: x, y: y });
link.addTo(this.model);
evt.data = { link: link, x: x, y: y };
},
'blank:pointermove': function(evt, x, y) {
evt.data.link.set('target', { x: x, y: y });
},
'blank:pointerup': function(evt) {
var target = evt.data.link.get('target');
if (evt.data.x === target.x && evt.data.y === target.y) {
// remove zero-length links
evt.data.link.remove();
}
}
});
paper.checkViewport()
For every view in the paper, determine if it fits into the viewport specified by paper.options.viewport
function. Views that fit (views for which that function returns true
) are attached to the DOM; views that do not are detached.
While async
papers do this automatically, synchronous papers require an explicit call to this method for this functionality to be applied. To show all views again, use paper.dumpViews()
.
paper.checkViewport(opt)
If opt.viewport
is provided, it is used as the callback function instead of paper.options.viewport
. The format of the callback method is described in the option's documentation, as are examples of usage.
In async
papers, providing opt.viewport
causes viewport to be temporarily recalculated according to the provided callback function. This may be useful in situations where you need to show different views depending on context (e.g. viewing a section of the diagram vs. printing all of it).
paper.clearGrid()
Hide the current grid.
paper.clientOffset()
Returns coordinates of the paper viewport, relative to the application's client area.
paper.clientToLocalPoint(p)
Transform client coordinates represented by point p
to the paper local coordinates.
This is especially useful when you have a mouse event object and want coordinates
inside the paper that correspond to event clientX
and clientY
point.
var localPoint1 = paper.clientToLocalPoint({ x: evt.clientX, y: evt.clientY });
// alternative method signature
var localPoint2 = paper.clientToLocalPoint(evt.clientX, evt.clientY);
paper.clientToLocalRect(rect)
Transform the rectangle rect
defined in the client coordinate system to the local coordinate system.
var bcr = paper.svg.getBoundingClientRect();
var localRect1 = paper.clientToLocalRect({ x: bcr.left, y: bcr.top, width: bcr.width, height: bcr.height });
// alternative method signature
var localRect2 = paper.clientToLocalRect(bcr.left, bcr.top, bcr.width, bcr.height);
// Move the element to the center of the paper viewport.
var localCenter = localRect1.center();
var elSize = element.size();
element.position(localCenter.x - elSize.width, localCenter.y - elSize.height);
paper.defineFilter(filterDefinition)
Define an SVG filter for later reuse within the paper. The method returns the filter id and the filterDefinition
must have the following form:
{
name: <name of the filter>,
args: <filter arguments>
}
Where name
is the name of the filter. See below for the list of built-in filters. args
is an object containing filter parameters. These parameters are dependent on the filter used and are described in the list below as well. Example usage:
var filterId = paper.defineFilter({
name: 'dropShadow',
args: {
dx: 2,
dy: 2,
blur: 3
}
});
svgNode.setAttribute('filter', 'url(#' + filterId + ');
The following is the list of built-in filters. All these filters are defined in the joint.util.filter
namespace. This namespace can be extended simply by adding a new method to it with one argument, an object with filter parameters, returning a string representing the SVG filter definition.
blur
x
- horizontal blury
- vertical blur [optional, if not defined y
is the same as x
]dropShadow
dx
- horizontal shiftdy
- vertical shiftblur
- blurcolor
- coloropacity
- opacitygrayscale
amount
- the proportion of the conversion. 1
is completely grayscale. 0
leaves the element unchanged.sepia
amount
- the proportion of the conversion. 1
is completely sepia. 0
leaves the element unchanged.saturate
amount
- the proportion of the conversion. 0
is completely un-saturated. 1
leaves the element unchanged.hueRotate
angle
- the number of degrees around the color circle the input samples will be adjustedinvert
amount
- the proportion of the conversion. 1
is completely inverted. 0
leaves the element unchanged.brightness
amount
- the proportion of the conversion. 0
makes the element completely black. 1
leaves the element unchanged.contrast
amount
- the proportion of the conversion. 0
makes the element completely black. 1
leaves the element unchanged.paper.defineGradient(gradientDefinition)
Define an SVG gradient for later reuse within the paper. The method returns the gradient id and the gradientDefinition
must be an object with the following properties:
Name | Type | Description |
---|---|---|
type | 'linearGradient' | 'radialGradient' |
The linearGradient or radialGradient type of the gradient. |
id | string | (optional) - A unique identifier of the gradient. The id is generated if none provided. |
attrs | object | (optional) - Additional SVG attributes for the SVGGradientElement. |
stops | array | An array of stops. |
Where a stop
is an object with the following properties:
Name | Type | Description |
---|---|---|
color | string | Indicates what color to use at that gradient stop. |
offset | number | string | The offset of the gradient stop. |
opacity | number | (optional) - A number in the [0..1] range representing the transparency of the stop color. |
const gradientId = paper.defineGradient({
type: 'linearGradient',
stops: [
{ offset: '0%', color: '#E67E22' },
{ offset: '20%', color: '#D35400' },
{ offset: '40%', color: '#E74C3C' },
{ offset: '60%', color: '#C0392B' },
{ offset: '80%', color: '#F39C12' }
]
});
svgNode.setAttribute('fill', `url(#${gradientId})`);
For an introduction to gradients, please refer to the tutorial on Filters and Gradients.
paper.defineMarker(markerDefinition)
Define an SVG marker for later reuse within the paper. The method returns the marker id and the markerDefinition
must be an object with the following properties:
Name | Type | Description |
---|---|---|
id | string | (optional) - A unique identifier of the marker. The id is generated if none provided. |
attrs | object | (optional) - Additional SVG attributes for the SVGMarkerElement. |
markup | string | array | String or JSON Markup of the marker content. |
paper.defineMarker({
id: 'my-marker',
// The coordinate system for defining the path data
// ------------------------------------------------
// 0,0: marker-start, marker-end or marker-mid
// n,0: n > 0 in path direction
// n < 0 opposite path direction
// 0,m: m > 0 left to the path direction
// m < 0 right to the path direction
markup: `
<circle cx="6" cy="0" r="12" fill="#7b5cce"/>
<polygon points="0,0 6,6 12,0 6,-6" fill="#d63865" stroke="#fff" />
`
});
svgNode.setAttribute('marker-start', `url(#my-marker)`);
An alternative definition of the markerDefinition
is:
Name | Type | Description |
---|---|---|
id | string | (optional) - A unique identifier of the marker. The id is generated if none provided. |
type | string | The type of the SVGElement representing the marker ('path' , 'circle' , 'ellipse' , 'rect' , 'polyline' and 'polygon' ). |
[SVGAttribute] | any | (optional) - a presentation SVG attribute (e.g fill: 'red' ) |
const markerId = paper.defineMarker({
type: 'path', // SVG Path
fill: '#666',
stroke: '#333',
d: 'M 10 -10 0 0 10 10 z'
});
// Draw an arrow at the start and the end of a path
svgPath.setAttribute('marker-start', `url(#${markerId})`);
svgPath.setAttribute('marker-end', `url(#${markerId})`);
paper.definePattern(patternDefinition)
Define an SVG pattern for later reuse within the paper. The method returns the pattern id and the patternDefinition
must be an object with the following properties:
Name | Type | Description |
---|---|---|
id | string | (optional) - A unique identifier of the pattern. The id is generated if none provided. |
attrs | object | (optional) - Additional SVG attributes for the SVGGradientElement. |
markup | string | array | St |