Element
The basic model for diagram elements. It inherits from dia.Cell with a few additional properties and methods specific to elements. For a quick introduction to elements, see our tutorial. To learn about elements and their features in detail, see the elements learn section.
Methods
addPort()
element.addPort(port, [opt])
Add a single port, where port
could be defined as described in section Port interface
addPorts()
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
angle()
element.angle()
Return the rotation of the element in degrees (0° - 360°).
fitEmbeds()
element.fitEmbeds([opt])
An alias for the element.fitToChildren
function.
fitParent()
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:
|
fitToChildren()
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 |
getAbsolutePointFromRelative()
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.
getBBox()
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. |
getGroupPorts()
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.
getPort()
element.getPort(id)
Returns the port specified by an id
. If such a port does not exist the method returns undefined
.
getPortIndex()
element.getPortIndex(portId)
Returns the port index in the array of port items.
getPorts()
element.getPorts()
Returns an array of all ports on the element. If there is no port an empty array is returned.
getPortsPositions()
element.getPortsPositions(groupName)
Returns the positions and the angle of all ports in the group, relatively to the element position.
getRelativePointFromAbsolute()
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.
hasPort()
element.hasPort(id)
Check if an element contains a port with id
. Returns boolean
.
hasPorts()
element.hasPorts()
Check if an element has any ports defined. Returns boolean
.
insertPort()
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.
isElement()
element.isElement()
Always returns true
. See cell.isElement()
.
isLink()
element.isLink()
Always returns false
. See cell.isLink()
.
portProp()
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' }
position()
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
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
removePort()
element.removePort(port, [opt])
Remove a port from an element, where port
is a port object, or portId
.
removePorts()
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.
resize()
element.resize(
width: number,
height: number,
opt?: { direction?: Direction; [key: string]: any; }
): this;
Change the size of the element.
By default the element is resized in the direction from the top-left corner to the bottom-right corner. This means that the top-left corner of the element stays at the same position after resizing.
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).
rotate()
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.
scale()
element.scale(sx, sy, origin[, opt])
Scales the element's position and size relative to the given origin.
translate()
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).
Events
The following list contains events that you can react on:
change
change | Generic event triggered for any change on the element - fn(element, opt) |
---|---|
change:angle | Triggered when the element gets rotated - fn(element, newAngle, 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:position | Triggered when the element changes its position - fn(element, newPosition, opt) |
change:size | Triggered when the element gets resized - fn(element, newSize, opt) |
change:z | Triggered when the element is moved in the z-level (toFront and toBack) - fn(element, newZ, opt) |
// 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 });
transition
transition:start | Triggered when a transition starts. - fn(element, pathToAttribute) |
---|---|
transition:end | Triggered when a transiton ends. - fn(element, pathToAttribute) |
Ports
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.
Port API on joint.dia.Element
hasPort
/hasPorts
addPort
/addPorts
removePort
/removePorts
getPort
/getPorts
portProp
getPortsPositions
Port configuration
// 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 |
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 |
attrs | object | JointJS style attribute definition. The same notation as the |
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 |
| 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 |
| object | Additional arguments for the layout method. Available properties depend on the layout type. More information can be found in the |
| 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.
Port groups configuration
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 | It states the layout type. Match the layout method name defined in the |
| object | Arguments for the port layout function. Available properties depend on the type of layout. More information can be found in |
attrs | object | JointJS style attribute definition. The same notation as the |
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 |
| 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 |
| object | Additional arguments for the layout method. Available properties depend on the layout type. More information can be found in the |
| MarkupJSON | string | Custom port label markup. Multiple roots are not allowed. The default port label looks like the following: |
Custom markup
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'
}
}]
// ...
});