Skip to main content

Graph

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 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 shapes.custom.Element model when deserializing a graph from JSON format. If the graph is instantiated as e.g. const graph = new 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.

constructor

Methods

addCell()

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);

addCells()

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.

bfs()

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:

graph BFS algorithm

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.

clear()

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.

cloneCells()

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).

cloneSubgraph()

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] }.

dfs()

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:

graph DFS algorithm

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.

findModelsFromPoint()

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.

findModelsInArea()

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.

findModelsUnderElement()

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'.

fromJSON()

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)

getBBox()

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);

getCell()

graph.getCell(id)

Get a cell from the graph by its id.

getCells()

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).

getCellsBBox()

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 });

getCommonAncestor()

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 });

getElements()

graph.getElements()

Return an array of all elements in the graph. The elements are sorted by their z index (the smallest z being first).

getFirstCell()

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).

getLastCell()

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).

getNeighbors()

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 flow of directed links at link-link connections.

getPredecessors()

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).

getSinks()

graph.getSinks()

Return an array of all the leafs of the graph. Time complexity: O(|V|).

getSources()

graph.getSources()

Return an array of all the roots of the graph. Time complexity: O(|V|).

getSubgraph()

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.

getSuccessors()

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.

getSuccessors diagram no embedding

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.

getSuccessors diagram with embedding

isNeighbor()

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 behind the 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 of the 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 flow of directed links at link-link connections.

isPredecessor()

graph.isPredecessor(elementA, elementB)

Return true if elementB is a predecessor of elementA.

isSink()

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).

isSource()

graph.isSource(element)

Return true if element is a root, i.e. there is no link that targets the element. Time complexity: O(1).

isSuccessor()

graph.isSuccessor(elementA, elementB)

Return true if elementB is a successor of elementA.

maxZIndex()

graph.maxZIndex()

Get the highest Z value in the graph (the value of the cell on front).

minZIndex()

graph.minZIndex()

Get the lowest Z value in the graph (the value of the cell on the back).

removeCells()

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.

resetCells()

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.search(element, iteratee [, opt])

Traverse the graph starting at element following links. This function is a wrapper around dfs() or bfs(). By default, it uses dfs(). If opt.breadthFirst is set to true, bfs() will be used instead.

toJSON()

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());

translate()

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.

Events

Graph triggers several events of its own that you can react on.

tip

In addition, all events triggered by Elements and Links are propagated to their containing Graph as well.

add

Triggered when a new cell is added to the graph.

graph.on('add', function(cell) {
alert('New cell with id ' + cell.id + ' added to the graph.')
})

change

Generic event triggered for any change in the graph.

remove

Triggered when a cell is removed from the graph.

Graph JSON

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
}
]
}