🎉 JointJS has new documentation! 🥳
This is the API reference to the open source JointJS core library. If you're looking for the JointJS+ 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 lightweight 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 'boundary'
; 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.). If set to false , the magnet is used as is, even if it is an SVGGroup (it's the most suitable for use in conjunction with magnetSelector). |
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 six built-in connectors in JointJS:
'straight'
- connector with straight lines and different ways to handle corners (point, rounded, bevelled, gap)'jumpover'
- connector with angled straight lines and bridges over link intersections'normal'
- (deprecated) default connector with angled straight lines'rounded'
- (deprecated) connector with straight lines and rounded edges'smooth'
- connector interpolated as a bezier curve'curve'
- connector interpolating as a curve defined by ending tangentsExample:
link.connector('straight', {
cornerType: 'line',
cornerRadius: 20
});
The default connector is 'normal'
; this can be changed with the defaultConnector
paper option. Example:
paper.options.defaultConnector = {
name: 'straight',
args: {
cornerType: 'line',
cornerRadius: 20
}
}
All 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('straight', {
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'
});
Deprecated. Use the 'straight'
connector instead.
Example:
link.connector('straight');
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:
// deprecated
link.connector('normal');
Deprecated. Use the 'straight'
connector with cornerType: 'cubic'
instead. (To exactly replicate the 'rounded'
connector functionality, you should also pass precision: 0
.)
Example:
link.connector('straight', {
cornerType: 'cubic',
precision: 0,
cornerRadius: 20
});
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:
// deprecated
link.connector('rounded', {
radius: 20
});
The 'smooth'
connector interpolates route points using a cubic bezier curve.
Example:
link.connector('smooth');
The 'straight'
connector connects provided route points with straight lines while handling corners on the route in different ways depending on provided arguments (point, rounded, bevelled, gap). It accepts four additional arguments, which can be passed within the connector.args
property:
cornerType | string | (Optional) How should corners on the route be handled? The default is 'point' . The available values are the following:
|
||||||||
---|---|---|---|---|---|---|---|---|---|---|
cornerRadius | number | (Optional) The distance of endpoints of line segments around corner points (e.g. curve radius for 'cubic' corner type). Default is 10 . This argument is ignored for cornerType: 'point' . |
||||||||
cornerPreserveAspectRatio | boolean | (Optional) Should distance of both endpoints be adjusted if one of them needs to be placed closer to a corner point than specified by the cornerRadius argument; or are the two allowed to be independent? (This situation happens when the distance between two consecutive route points is lower than cornerRadius * 2 .) Default is false . This argument is ignored for cornerType: 'point' |
||||||||
precision | number | (Optional) To how many decimal places should the returned point coordinates be rounded? Default is 1 . This argument is ignored for cornerType: 'point' . |
Example of a bevelled connector:
link.connector('straight', {
cornerType: 'line',
cornerPreserveAspectRatio: true
});
Example of a rounded connector:
link.connector('straight', {
cornerType: 'cubic',
cornerRadius: 20
});
Example of a connector with gaps at corners:
link.connector('straight', {
cornerType: 'gap',
cornerRadius: 2
});
Example of a connector with simple angled lines connecting route points:
link.connector('straight');
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 . |
rotate | boolean | Whether the rotation of the source or target element should be taken into account. Only works for AUTO, HORIZONTAL and VERTICAL directions. The default is false . |
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(w / 2)'
'calc(h / 3)'
'calc(w / 2)'
'calc(h / 3)'
'calc(2 * w + 5)'
'calc(0.5 * h - 10)'
'calc(w / 2 + 5)'
'calc(h / 3 - 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 *
/
symbol followed by a number.
/ 2
+
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 the defineFilter() 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.
Special attribute for setting the properties of the HTMLElement. The value is not set as a node attribute, but using the DOM property.
element.attr('input', {
props: { value: 'text', readonly: false },
type: 'text',
placeholder: 'Enter text'
});
props
is an object with one or more of the following properties: value
, checked
, disabled
, readOnly
, contentEditable
, multiple
, selected
, indeterminate
.
For example, to set the content of the textarea element, use the following syntax:
element.attr('textarea/props/value', 'textarea content');
When setting a value of the select element, pass the value of the option element.
element.attr('select/props/value', 'option1');
For the select with multiple options, pass an array of values.
element.attr('select/props/value', ['option1', 'option2]', { rewrite: true });
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.
It enables the text wrapping for the text
attribute (automatically breaks the lines to fit within the reference bounding box).
It expects an object with several optional properties.
width
and height
adjust the final size of the wrapped text.
'calc(w - 10)'
)'50%'
is half the width or height)50
fits the text within 50 pixels)-10
is an equivalent to 'calc(w - 10)'
resp. 'calc(h - 10)'
)null
to disable wrapping in a given dimensionIf 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.
element1.attr('label', {
text: 'Text to wrap.',
textWrap: {
width: 'calc(w - 10)', // reference width minus 10
height: null', // no height restriction
}
});
element2.attr('label', {
text: 'lorem ipsum dolor sit amet consectetur adipiscing elit',
textWrap: {
width: -10, // reference width minus 10
height: '50%', // half of the reference height
ellipsis: true // could also be a custom string, e.g. '...!?'
}
});
For more info see util.breakText.
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.
new Cell([attributes], [options])
When creating an instance of a cell, you can pass in the initial values of the attributes, which will be set on the model.
If you pass a { mergeArrays: true }
as the options, all the arrays defined as class defaults will be merged instead of overridden.
const MyRect = joint.shapes.standard.Rectangle.define('Rect', { array: [1,2] });
const rect1 = new MyRect({ array: [3] });
console.log(rect1.get('array')); // [3] array was overridden
const rect2 = new MyRect({ array: [3] }, { mergeArrays: true });
console.log(rect2.get('array')); // [3,2] array was merged
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,
rx: 'calc(0.5*w)',
ry: 'calc(0.5*h)',
cx: 'calc(0.5*w)',
cy: 'calc(0.5*h)'
}
}
});
// 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. Also JSON markup can be described using an svg tagged template. Used as a template to build DOM Elements on the fly when the associated cellView is rendered.
JSON markup is defined recursively as an array of JSONElement
or strings representing text nodes, 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'
},
'text content',
{
tagName: 'circle',
selector: 'circle2',
groupSelector: 'circles'
}]
}]
JSON markup also can be defined using an svg tagged template. This tagged template converts SVG representation of the markup into a JSON markup object. Let's write the previous example using an svg tagged template:
const svg = joint.util.svg;
// single DOM element
markup: svg`<rect\>`
// multiple DOM elements
markup: svg`
<rect @selector="body"\>
<text
@selector="label"
stroke="none"
\>`
// nested DOM elements
markup: svg`
<g>
<circle
@selector="circle1"
@group-selector="circles"
/>
text content
<circle
@selector="circle2"
@group-selector="circles"
/>
</g>`
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' }
});
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 you also need to set the useCSSSelectors=true
on the model's prototype in order to use CSS selectors in the attrs
attribute.
joint.dia.Element.define('Rectangle', {
attrs: {
rect: { // CSS Selector for the <rect/> element
fill: 'red'
}
}
}, {
markup: '<rect class="rectangle"/>',
useCSSSelectors: true
});
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.z()
Return z – the stacking order (an equivalent to HTML z-index
).
When z is undefined
or null
, function returns 0
.
The view for the joint.dia.Cell model. It inherits from mvc.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.findNode(selector)
The method returns a DOM node matching the selector
.
The method searches for matching sub-element among the descendants of this.el
. If no such sub-element is found, null
is returned.
The available selectors
are defined by the markup
attribute of the Cell model from which the CellView was initialized.
cellView.findNodes(groupSelector)
The method returns an array of DOM nodes matching the groupSelector
.
The method searches for matching sub-elements among the descendants of this.el
. If no such sub-elements are found, an empty array is returned.
The available groupSelectors
are defined by the markup
attribute of the Cell model from which the CellView was initialized.
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.isDefaultInteractionPrevented(event)
Returns whether preventDefaultInteraction was ever called on this event
object.
cellView.preventDefaultInteraction(event)
The method tells the view that its default interactivity action should not be taken as it normally would be.
For example, if the view is a joint.dia.ElementView and the user clicks on it, the view will normally enter the interactive mode and starts moving the element.
Calling preventDefaultInteraction
will prevent the view from entering the interactive mode. The view will still trigger all events, but it will not react to them in the default way.
It is useful when you want to handle the pointer events yourself. For example, you can use it to implement your own custom interaction with the view. A selection lasso can be drawn whenever the user holds down the shift key and starts dragging from any point on the paper (regardless of whether it is an element, a link or a blank space).
paper.on('element:pointerdown', (elementView, evt) => {
if (evt.shiftKey) {
// prevent element movement
elementView.preventDefaultInteraction(evt);
// start drawing selection lasso
}
});
The following table shows which default interaction will be stopped depending on what point the method was called.
Paper events | Description | Paper interactive option |
---|---|---|
'element:pointerdown' |
Prevents element movement. | elementMove |
'element:magnet:pointerdown' |
Prevents adding link from magnet and element movement. | addLinkFromMagnet |
'link:pointerdown' |
Prevents link label and link movement. | labelMove and linkMove |
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 mvc.Model 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 mvc.Model 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 mvc.Model 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.Rectangle
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: {
width: 'calc(w)',
height: 'calc(h)',
strokeWidth: 2,
stroke: '#000000',
fill: '#FFFFFF'
},
label: {
textVerticalAnchor: 'middle',
textAnchor: 'middle',
x: 'calc(0.5*w)',
y: 'calc(0.5*h)',
fontSize: 14,
fill: '#333333'
}
}
Notice that the object makes use of special JointJS attributes (e.g. 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, along with information on how to use calc()
for sizing.
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 |
A custom port markup. The default port markup is The root of the port markup is referenced by the If the markup contains more than one node, an extra group is created to wrap the nodes. This group becomes the new
|
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 |
A custom port label markup. The default port label markup is The root of the label markup is referenced by the If the markup contains more than one node, an extra group is created to wrap the nodes. This group becomes the new All Use an empty array |
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: { width: 'calc(w)', height: 'calc(h)' },
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.
Any additional option or custom property provided in the options object will be accessible in the callback function of
the Element 'change:embeds'
event.
const rect1 = new joint.shapes.standard.Rectangle({
position: { x: 100, y: 100 },
size: { width: 90, height: 30 },
attrs: { label: { text: 'Rect' } }
});
const rect2 = rect1.clone();
rect2.translate(100, 0);
graph.addCells([rect1, rect2]);
rect1.on('change:embeds', function(element, newEmbeds, opt) {
console.log(opt); // {testOption: true}
});
// Add custom 'testOption' property
rect1.embed(rect2, { testOption: true });
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])
An alias for the element.fitToChildren
function.
element.fitParent([opt])
Resize and reposition this element's embedding parent element such that all of its children (including this element) end up within the parent's new bounding box.
Starting from a given element, this function proceeds upwards
to that element's parent (and further ancestors, if opt.deep
is used). In that sense, this function is the opposite of the element.fitToChildren
function.
Available options:
padding | number | Inflate the embedding parent element's calculated bounding box by this much additional padding. |
---|---|---|
expandOnly | boolean | If true , the algorithm is only ever allowed to expand the bounding box of the embedding parent element, never to shrink it. You can visualize this setting as the algorithm dragging the top-left and bottom-right corners of the parent's bounding box outward until its children (including this element) are within the bounding box. |
shrinkOnly | boolean |
If If only a portion of this element (or this element's sibling element) initially overlaps the embedding parent element, the parent's calculated bounding box will only include that portion. If there is no overlap between this element and its parent (i.e. this element is |
deep | boolean |
If Note that if this option is used in conjunction with |
terminator | Cell | Cell.ID |
If Handling of edge cases:
|
element.fitToChildren([opt])
Resize and reposition this element such that all of its embedded child elements end up within this element's new bounding box.
Starting from a given element, this function proceeds downwards
through that element's children (and further descendants, if opt.deep
is used). In that sense, this function is the opposite of the element.fitParent
function.
Available options:
padding | number | Inflate this element's calculated bounding box by this much additional padding. |
---|---|---|
expandOnly | boolean | If true , the algorithm is only ever allowed to expand the bounding box of this element, never to shrink it. You can visualize this setting as the algorithm dragging the top-left and bottom-right corners of this element's bounding box outward until all its embedded child elements are within the bounding box. |
shrinkOnly | boolean |
If If only a portion of an embedded child element initially overlaps this element, the calculated bounding box will only include that portion. If there is no overlap between this element and its children (i.e. all children are currently placed |
deep | boolean |
If Note that if this option is used in conjunction with |
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": {}
}
When changing model attributes via prop()
or attr()
, some useful information is passed along with the
change event in JointJS. propertyPath
, propertyValue
, and propertyPathArray
are all values
which can be accessed when updating the model. This can prove useful if for some reason you need to listen to a specific
attribute change.
graph.on('change', (cell, opt) => {
console.log(opt);
// --> {propertyPath: 'attrs/body/fill', propertyValue: 'cornflowerblue', propertyPathArray: Array(3)}
if ('attrs' in cell.changed) {
console.log(opt.propertyPathArray, 'was changed');
// --> ['attrs', 'body', 'fill'] 'was changed'
}
});
element.prop('attrs/body/fill', 'cornflowerblue');
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.prop(['attrs', '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 be updated in a Breadth-first
search (BFS) fashion.
If opt.foregroundEmbeds
is true
(as by default), all the embedded
cells will get a higher z
index than that of this element. 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. If
opt.foregroundEmbeds
is false
, the original order within the group is preserved, allowing children to remain behind their parents.
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 be updated
in a Breadth-first search (BFS) fashion.
If opt.foregroundEmbeds
is true
(as by default), all the embedded
cells will get a higher z
index than that of this element. 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. If opt.foregroundEmbeds
is false
,
the original order within the group is preserved, allowing children to remain behind their parents.
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.findPortNode(portId)
Return the port DOM node of this ElementView that is identified by portId
(i.e. an SVGElement within this.el
which has 'port': portId
, an SVGElement referenced by portRoot
selector).
If the ElementView does not have a port identified by portId
, return null
.
elementView.findPortNode(portId, selector)
Return an SVGElement referenced by selector of the port identified by portId. If there is no port with portId or no SVGElement|HTMLElement was found by the selector, null
is returned.
The available selectors
are defined by the markup
attribute of the port from which the port was built.
elementView.findPortNodes(portId, groupSelector)
Return an array of SVGElement|HTMLElement referenced by groupSelector of the port identified by portId. If there is no port with portId or no SVGElement|HTMLElement was found by the groupSelector, an empty array is returned.
The available groupSelectors
are defined by the markup
attribute of the port from which the port was built.
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.hasTools()
Return true
if this element view has a tools view attached.
elementView.hasTools(name)
Return true
if this element view has a tools view of the provided name
attached.
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 cells (elements and links) of the diagram.
It inherits from mvc.Model.
The collection of all the cells is stored in the property cells
as an mvc.Collection.
The graph is a powerful data model behind all JointJS diagrams. It not only provides efficient storage for directed graphs, but also offers useful algorithms for traversing the graphs.
In order for JointJS to find the correct constructor for your cell, the graph
option cellNamespace
must be provided in
its constructor function when a graph is instantiated. Built-in shapes are usually located in the joint.shapes
namespace, so this is
a common namespace to use. It's possible to add custom shapes to this namespace, or alternatively, you may like to use a different namespace
completely.
For example, if joint.shapes
is provided as the value of cellNamespace
, and a cell is of type 'custom.Element'
,
then the graph looks up the joint.shapes.custom.Element
model when deserializing a graph from JSON format. If the graph is instantiated
as e.g. const graph = new joint.dia.Graph({}, { cellNamespace: myCustomNamespace })
, then the graph
will read the model
definition from the myCustomNamespace.custom.Element
object instead. This option is often used in combination with the
cellViewNamespace
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[, opt])
Add a new cell to the graph. If cell
is an array, all the cells in the array will be added to the graph.
Any additional option or custom property provided in the options object will be accessible in the callback function of
the graph add event.
If opt.dry
is set to true
, the graph reference is not stored on the cell
after it's added.
If opt.async
is set to false
, this ensures the cell
is rendered synchronously.
If opt.sort
is set to false
, the cell
will be added at the end of the collection.
const rect = new joint.shapes.standard.Rectangle({
position: { x: 100, y: 100 },
size: { width: 90, height: 30 },
attrs: { label: { text: 'my rectangle' } }
});
const rect2 = rect.clone();
const link = new joint.shapes.standard.Link({ source: { id: rect.id }, target: { id: rect2.id } });
const graph = new joint.dia.Graph({}, { cellNamespace: joint.shapes });
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, a Depth-first search algorithm is used (important for the order of returned elements).
If opt.breadthFirst
is set to true
, use a Breadth-first search algorithm
instead.
Generally, getSuccessors
cares about the direction of the links. It follows links from their source to target only. The
resulting array contains the elements that you visit if you follow the directed links. The links are simply navigated, and embedding
is not considered.
In the following image, the successors of A
are C
and B
. Embedding is not taken into account.
If opt.deep
is set to true
, embedded elements are taken into account too
(see dfs() for details). That means elements connected to any of the descendants are also
successors.
In the following image, if { deep: false }
, the only successor of A
is D
. Embedding is not
taken into account. If { deep: true }
, and B
is embedded in A
, that means the successors of
A
are C
and D
.
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 want to replace all the cells in one go. The 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.
HighlighterView.removeAll(paper)
Remove all the highlighters on paper. Only highlighters, which are instances of this class are removed.
HighlighterView.removeAll(paper, id)
Remove all the highlighters with given id on the paper. Only highlighters, which are instances of this class are 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');
}
});
HighlighterView.prototype.UPDATE_ATTRIBUTES
The highlighter is updated (highlight() function is called) every time any of the attributes from the list change.
The value could be an array of strings or a function returning an array of strings.
The default is [].
const ColorHighlighter = joint.dia.HighlighterView.extend({
UPDATE_ATTRIBUTES: ['color'],
highlight(cellView, node) {
node.style.fill = cellView.model.get('color');
},
unhighlight(_cellView, node) {
node.style.fill = '';
}
});
ColorHighlighter.add(rectangle.findView(paper), 'body', 'color-highlighter');
rectangle.set('color', 'gray'); // will call highlight()
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 properties: 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:
// `shapes.standard.Link` inherits from `dia.Link` (`dia.Link` is an abstract class that has no SVG markup defined)
var link1 = new joint.shapes.standard.Link({
source: { id: sourceId },
target: { id: targetId, port: portId }
});
var link2 = new joint.shapes.standard.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 properties 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.
The keys of the attrs
object are selectors
that match subelements defined in the link's markup
(see above). The values of this object are special JointJS attributes or native SVG attributes that should be set on the selected subelements. (A list of native SVG attributes and their descriptions can be found 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 of the link directly. You should use the link.attr()
method instead. For example, to set the attributes according to the above example, you would write:
link.attr({
line: { stroke: 'red' }
});
We can use the joint.shapes.standard.Link
type (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 native 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 directly inside link markup
are evaluated only once at CellView creation, while attributes defined in the attrs
object are evaluated on every model change. JointJS special attributes usually depend on the current state of the model, which means that they should be defined in the attrs
object (alongside any SVG attributes that you expect to be modified during 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
property. Each label can have its own markup
, size
, attrs
, and position
objects specified. The values in those objects take precedence over any defaults which may apply on the label.
To avoid excessive repetition, you can provide a defaultLabel
property to the link, to set the markup
, size
, attrs
, and position
objects which should be applied to all labels on the link. The properties from defaultLabel
act as a template, which is overwritten / extended by individually-specified label properties as appropriate.
An example of creating a new link instance with both the defaultLabel
and labels
properties specified can be seen below. For more details, see the link labels documentation. You should also refer to our tutorial on link labels.
const link = new joint.shapes.standard.Link({
source: { x: 50, y: 400 },
target: { x: 500, y: 400 },
defaultLabel: {
// applied to all labels on this link:
markup: [
{
tagName: 'rect',
selector: 'body'
}, {
tagName: 'text',
selector: 'label'
}
],
size: {
// used by `calc()` expressions in `attrs`
width: 150,
height: 30
},
attrs: {
body: {
width: 'calc(w)',
height: 'calc(h)',
// center around label position:
x: 'calc(w/-2)',
y: 'calc(h/-2)',
stroke: 'black',
fill: 'white'
},
label: {
textWrap: {
width: 'calc(w-5)',
height: 'calc(h-5)'
},
// center text around label position:
// (no `x` and `y` provided = no offset)
textAnchor: 'middle',
textVerticalAnchor: 'middle',
fontSize: 16,
fontFamily: 'sans-serif'
}
}
},
labels: [{
// specification of an individual label:
size: { width: 200 }, // partially overwrites `defaultLabel.size`
attrs: {
label: {
text: 'Hello World'
}
},
position: { distance: 0.25 } // overwrites built-in default
}]
});
It is also possible to pass custom properties 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, if a custom contextmenu interaction should only be enabled for link1
but not link2
, we could add a custom property customLinkInteractions
to link1
:
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 true;
}
return { labelMove: false }; // otherwise
}
});
var link1 = new joint.shapes.standard.Link({
//...
customLinkInteractions: true // right-click adds a label
});
var link2 = new joint.shapes.standard.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 default Link properties to assign its own defaults. These values override built-in defaults, if necessary, 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, size, 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'
}
],
size: {
// used by `calc()` expressions in `attrs`
width: 20,
height: 20
},
attrs: {
label: {
text: '%', // default text for all labels
fill: '#ff0000', // default text color for all labels
fontSize: 14,
textAnchor: 'middle',
textVerticalAnchor: 'middle',
pointerEvents: 'none'
},
body: {
// currently, calc() is responsive to `size` property
// uncomment to make calc() responsive to size of 'label':
//ref: 'label', // subelement identified by 'label' selector
fill: '#ffffff',
stroke: '#000000',
strokeWidth: 1,
r: 'calc(d/2 + 2)'
}
},
position: {
// keep built-in default `distance` for all labels (0.5)
offset: {
y: -20 // offset by 20px upwards for all labels
},
args: {
absoluteOffset: true // absolute offset for all labels
}
}
}
});
var link = new CustomLink({
//...
});
To ensure backwards compatibility, the joint.dia.Link
class comes with a private built-in defaultLabel
property. It is reproduced here for reference:
defaultLabel: {
// built-in default markup:
// applied only if neither one of the following is provided:
// - individual label `markup` property
// - `defaultLabel.markup` property
markup: [
{
tagName: 'rect',
selector: 'rect'
}, {
tagName: 'text',
selector: 'text'
}
],
// built-in default attributes:
// applied only if built-in default markup is used
attrs: {
text: {
fill: '#000000',
fontSize: 14,
textAnchor: 'middle',
textVerticalAnchor: 'middle',
pointerEvents: 'none'
},
rect: {
ref: 'text',
fill: '#ffffff',
rx: 3,
ry: 3,
x: 'calc(x)',
y: 'calc(y)',
width: 'calc(w)',
height: 'calc(h)'
}
},
// built-in default position:
// merged with `defaultLabel.position` and individual label `position`
position: {
distance: 0.5
}
}
If custom markup
object is not provided (i.e. there is no class-specific defaultLabel.markup
object, nor any instance-specific defaultLabel.markup
, nor an individual label-specific markup
property), then built-in default label markup
is applied (as reproduced above). Alongside, the built-in default label attrs
object is applied. Note that the built-in default attrs
object is applied as a template in this context, which means that you may enhance it with a custom attrs
object (class-specific / instance-specific / individual label-specific). However, in the interest of keeping your code maintainable and easy to understand, it is very highly recommended that you provide both your own markup
object and your own attrs
object, unless you want to use the built-in default precisely as-is.
The built-in default position
object behaves slightly differently. Regardless of markup
, it is always merged with custom position
objects (class-specific / instance-specific / individual label-specific) - but it has the lowest priority of the four. That is, if at least one of the custom position
objects provides a distance
value, that value will have precedence over the built-in default position.distance
. If no custom position.distance
is provided, then the built-in default is applied (placing labels at midpoints of links).
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:connector
- triggered when the link changes its connector change:router
- triggered when the link changes its routerchange: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
}>,
size?: {
width?: number,
height?: number
},
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
, size
, attrs
and position
objects defined on individual labels may be supplemented by properties from one of a hierarchy of defaultLabel
objects, each of them following the same schema as above.
defaultLabel
object provided when creating a new link instance.defaultLabel
object provided when defining a custom Link type.defaultLabel
object, if any.If a defaultLabel
object is found in one of the above sources, its markup
, size
, attrs
and position
properties act as a template into which the individually-specified label properties are deep-merged as appropriate. In addition, the properties of the highest-precedence defaultLabel
object may prove to be important on their own; for example, the linkView.addLabel()
shortcut function is only capable of adding a default label to links.
The markup
property specifies the markup of the label. It can be provided in three ways:
'<rect /><text />'
).[{ tagName: 'rect', selector: 'body' }, { tagName: 'text', selector: 'label' }]
).joint.util.svg
ES6 tag template:joint.util.svg`
<rect @selector="body"/>
<text @selector="label"/>
`
Note that the latter two options allow the user to specify custom selectors for the individual SVGElements; these can then be used for targeting elements within the attrs
property.
If no markup
property is provided on the individual label, but a defaultLabel
is found on the link according to the defaultLabel
hierarchy, then the label markup is taken from the defaultLabel.markup
property.
However, if no markup
property is provided on the individual label, and no defaultLabel
is found on the link according to the defaultLabel
hierarchy (as may be the case when extending from out-of-the-box Link types like joint.dia.Link
or joint.shapes.standard.Link
without providing one's own defaultLabel
object), then built-in default Link markup is used to maintain backwards compatibility. That object defines markup
as a JSON array with a <rect>
SVGElement ('rect'
selector) under a <text>
SVGElement ('text'
selector).
The size
property specifies the dimensions of the label. It may be defined as an object with a width
and/or height
properties. These values are then used as the reference size for all calc()
attribute expressions within the link's attrs
object (this may be overwritten for an individual subelement with the ref
property to choose a different subelement as a reference).
If a defaultLabel
object was found on the link (according to the defaultLabel
hierarchy), then the label's own size
properties are deep-merged into it as appropriate.
There is no built-in defaultLabel.size
object; if no defaultLabel
is found, and no size
is provided on the individual label, then no size
is assigned to that label. (That causes calc()
attribute expressions to assign 0
as the value of all their variables - which may be unexpected - unless the expression is used alongside a ref
property.)
object |
|
The attrs
property is an object where the keys are CSS selectors (referring to custom selectors or SVGElements specified in markup
- e.g. the body
selector in the above markup example). They are expected to contain objects that specify native SVG attributes and/or JointJS special attributes (e.g. fill
), alongside the value to be assigned (e.g. 'white'
).
If a defaultLabel
object was found on the link (according to the defaultLabel
hierarchy), then the label's own attrs
properties are deep-merged into it as appropriate. For example, this may prove useful when you just need to change a handful of attributes for one specific label (e.g. attrs: { body: { stroke: 'black' } }
). (Note that selectors defined in defaultLabel.markup
can also be referenced from within a specific label's attrs
.)
If the built-in default markup is applied on an individual label (i.e. no markup
property was provided, and no defaultLabel.markup
was found), then several additional built-in default attributes need to be automatically applied for reasons of backwards compatibility. The individual label's attrs
(if any) are deep-merged with defaultLabel.attrs
(if found) as appropriate, and the resulting object is then deep-merged with the built-in default attrs
as appropriate. (Note that this is the only case in which the absence of one property - markup
/defaultLabel.markup
- influences another property - attrs
/defaultLabel.attrs
.)
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.
The built-in default position ({ distance: 0.5 }
) is used to maintain backwards compatibility. The individual label's position
(if any) is deep-merged with defaultLabel.position
(if found in the defaultLabel
hierarchy) as appropriate, and the resulting object is then deep-merged with the built-in default position
as appropriate. (Note that the built-in default object is always merged in with the other objects, which is a difference from how the built-in defaults of markup
and attrs
properties are treated.)
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.
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": {}
}
When changing model attributes via prop()
or attr()
, some useful information is passed along with the
change event in JointJS. propertyPath
, propertyValue
, and propertyPathArray
are all values
which can be accessed when updating the model. This can prove useful if for some reason you need to listen to a specific
attribute change.
graph.on('change', (cell, opt) => {
console.log(opt);
// --> {propertyPath: 'attrs/line/targetMarker/fill', propertyValue: 'cornflowerblue', propertyPathArray: Array(4)}
if ('attrs' in cell.changed) {
console.log(opt.propertyPathArray, 'was changed');
// --> ['attrs', 'line', 'targetMarker', 'fill'] 'was changed'
}
});
link.prop('attrs/line/targetMarker/fill', 'cornflowerblue');
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.
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:
const linkView = paper.findViewByModel(link);
// const linkView = link.findView(paper); // alternatively
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.
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.
Example:
const CustomLinkView = dia.LinkView.extend({
// custom interactions:
pointerdblclick: function(evt, x, y) {
this.addVertex(x, y);
},
contextmenu: function(evt, x, y) {
this.addLabel(x, y);
}
});
const paper = new dia.Paper({
//...
linkView: CustomLinkView,
});
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
});
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
});
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|HTMLElement referenced by selector of the label at given index. If there is no label at index or no DOM node was found by the selector, null
is returned.
The available selectors
are defined by the markup
attribute of the label from which the label was built.
linkView.findLabelNodes(index, groupSelector)
Return an array of SVGElement|HTMLElement referenced by groupSelector of the label at given index. If there is no label at index or no DOM node was found by the groupSelector, an empty array is returned.
The available groupSelectors
are defined by the markup
attribute of the label from which the label was built.
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