Announcing Cytoscape.js 3.0.0
Cytoscape.js 3 has been released! As promised, the majority of the changes are housekeeping-related, with a few significant (read: breaking) API changes to help with usability and future-proofing as JS evolves.
Contents
Summary of Changes
Sorted from most to fewest changes necessary to update an app
- Functions with
function(i, ele)
signature are nowfunction(ele, i)
#1531 cy.layout()
andeles.layout()
now return the layout, rather than chaining the calling object #1533this
is no longer modified to be the element of interest for many callback functions #1535cy
prefix has been removed from names of event fields #1537- A clearer distinction is made between
display
,visibility
, andopacity
in the internal API #1544 - The
:touch
selector has been removed #1540 cy.onRender()
andcy.offRender()
have been replaced with therender
event #1541- The
grab
event has been broken into two parts:grab
andgrabon
#1545 - The jQuery plugin has been removed #1539
cy.load( ... )
was deprecated in v2 and has been removed in v3 #1534layout()
andscratch()
mapper support was deprecated in v2 and has been removed in v3 #1536initRender()
has been removed after deprecation in v2 #1538- The
inhibitor
arrow shape has been removed #1655 cose
no longer bundles the Weaver dependecy #1687- First-party extensions have been updated to use the new API #1718
- Remove all shadow style properties #1758
- Remove
data
parameters in.on()
etc #1764
API Changes and Migration
- Functions with a
function(i, ele)
signature are nowfunction(ele, i)
#1531- Affects:
eles.each()
,eles.bfs()
,eles.dfs()
,eles.filter()
,eles.positions()
,eles.layoutPositions()
-
It’s now simpler to use ES6 syntax for operations on elements! Example:
// v2 cy.elements().each(function(i, ele){ console.log( ele.id() + ' is ' + ( ele.selected() ? 'selected' : 'not selected' ) ); }); // new v3 syntax; we can ignore the index i cy.elements().each(ele => console.log( ele.id() + ' is ' ( ele.selected() ? 'selected' : 'not selected' ) ) );
- Affects:
cy.layout()
andeles.layout()
now return the layout (rather than chaining the calling object) #1533- Any code which previously ran a layout by calling
eles.layout( opts )
orcy.layout( opts )
should be changed toeles.layout( opts ).run()
orcy.layout( opts ).run()
.
- Any code which previously ran a layout by calling
this
is no longer modified for many callbacks #1535- Affects:
eles.each()
eles.filter()
eles.positions()
eles.layoutPositions()
eles.bfs()
eles.dfs()
eles.dijkstra()
eles.aStar()
eles.bellmanFord()
eles.kruskal()
eles.floydWarshall()
eles.pageRank()
eles.closenessCentrality()
andeles.closenessCentralityNormalized()
eles.betweennessCentrality()
andeles.betweennessCentralityNormalized()
eles.degreeCentrality()
andeles.degreeCentralityNormalized()
- concentric and cose layouts (already deprecated)
- Avoiding changing
this
in the bodies of functions (keeps thethis
context from outside the function) makes working with arrow functions nicer. -
Example:
// Cytoscape.js v2.x cy.nodes().each(function(i) { if (i % 2 === 0) { this.addClass('highlighted', 500); } }); // Cytoscape.js v3.x cy.nodes().each((ele, i) => { if (i % 2 === 0) { // `this` is no longer modified to be the element of interest for many callback functions ele.addClass('highlighted', 500); } });
- Affects:
cy
prefix has been removed from names of event fields #1537- For example,
event.cyTarget
is nowevent.target
.event.cyTarget.id()
would now beevent.target.id()
.
- For example,
- A clearer distinction is made between
display
,visibility
, andopacity
in the internal API, bringing them in line with the HTML/ CSS counterparts #1544- Only applies to internal APIs
display
,visibility
, andopacity
refer to whether an element takes up space, whether an element is visible, and whether an element is interactive.display
: Whether to display the element; may beelement
for displayed ornone
for not displayed.visibility
: Whether the element is visible; may bevisible
orhidden
.opacity
: The opacity of an element, ranging from 0 to 1. The opacity of a compound node parent will also affect children.
Property Takes up space Bundled bezier edges take up space Visibility of connected edges Interactive display: none
no no hidden no visibility: hidden
yes yes visible no opacity: 0
yes yes visible yes - The
:touch
selector has been removed #1540- It’s less reliable now that browsers are implementing touch APIs without hardware support.
This means that the
:touch
selector will sometimes report that touch is supported even when the user’s device lacks touch capabilities.
- It’s less reliable now that browsers are implementing touch APIs without hardware support.
This means that the
cy.onRender()
andcy.offRender()
have been replaced with therender
event #1541cy.onRender(fn)
becomescy.on('render', fn)
- This allows render events to be used with
cy.once()
when only a single trigger is desired.
- The
grab
event has been broken into two parts:grab
andgrabon
#1545grab
now refers to all elements that are currently being grabbed (such as during a drag).grabon
refers to the element that the user’s mouse/ finger is over while grabbing the elements.
- The jQuery plugin has been removed #1539
- When used as a jQuery plugin, no reference to the graph was returned. Having no reference to the graph made using the API more difficult and was an unnecessary source of mistakes for beginners.
-
It’s still possible to pass a jQuery element to Cytoscape.js during initialization, but Cytoscape.js will no longer register itself as a jQuery plugin.
// v2 $('#cy-div').cytoscape(...) // v3 cytoscape({ container: $('#cy-div'), ... })
-
cy.load( ... )
was deprecated in v2 and has been removed in v3 #1534// v2 cy.load( ... ); // v3 cy.elements().remove(); cy.add( ... ); cy.layout( opts ).run(); // note that .run() is also new to v3
layout()
andscratch()
mapper support was deprecated in v2 and has been removed in v3 #1536- Old:
'scratch(foo)'
- v3:
function(ele) { return ele.scratch('foo') }
- Old:
initRender()
has been removed after deprecation in v2 #1538- Instead, use a combination of
cy.one()
and therender
event. - For example,
cy.one('render', () => console.log('initial render'))
- Instead, use a combination of
- The
inhibitor
arrow shape has been removed #1655- The shape was never publically documented (and had an incorrect name).
cose
no longer bundles the Weaver dependecy #1687- Cose is the only included layout with a dependency; its removal allows for a smaller file size.
- A Thread polyfill is included for cose to use during layouts if Weaver is not provided as an external script.
- To use multitasking, include a reference to Weaver in the
weaver
property of the layout options. -
Example:
var Weaver = require('weaverjs') var options = { name: 'cose', weaver: Weaver // ... remainder of options } cy.layout( options ).run();
- Graphs extensions (including layout extensions, such as cola and dagre; and UI extensions, such as qtip) may need to be updated to their latest versions to work with the API changes #1718
- Additionally, the new versions of these extensions are backwards-compaible with 2.x.
- Affected layout extensions: arbor, cola, cose-bilkent, dagre, spread, springy
- Affected UI extensions: automove, cxtmenu, edgehandles, navigator, panzoom, qtip
- The
data
parameter has been removed from listener functions such as.on()
#1764- To keep events as lean and performant as possible, the feature allowing additional data to be passed to event listeners through the
data
parameter has been removed.
- To keep events as lean and performant as possible, the feature allowing additional data to be passed to event listeners through the
Example migration from v2 to v3
Here’s a modified version of the Grid demo, showing off some of the changes between v2 and v3.
Modified Grid demo with v2
document.addEventListener('DOMContentLoaded', function() {
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
boxSelectionEnabled: true,
autounselectify: false,
style: stylesheet,
elements: elements
});
cy.layout({ name: 'grid' });
cy.nodes().each(function(i) {
if (i % 2 === 0) {
this.addClass('highlighted', 500);
}
});
cy.on('tap', 'node', function(event) {
var node = event.cyTarget;
console.log('tapped on ' + node.id());
});
cy.onRender(function() {
console.log('render event');
cy.offRender();
});
var grabCount = 0;
cy.nodes().on('grab', function() {
grabCount += 1;
});
cy.nodes().on('free', function() {
console.log('grabbed ' + grabCount + ' nodes');
grabCount = 0;
});
});
Modified Grid demo with v3
document.addEventListener('DOMContentLoaded', function() {
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
boxSelectionEnabled: true,
autounselectify: false,
style: stylesheet,
elements: elements
});
// `cy.layout()` and `eles.layout()` now return the layout, rather than chaining the calling object
cy.layout({ name: 'grid' }).run();
// Functions with `function(i, ele)` signature are now `function(ele, i)`
cy.nodes().each((ele, i) => {
if (i % 2 === 0) {
// `this` is no longer modified to be the element of interest for many callback functions
ele.addClass('highlighted', 500);
}
});
cy.on('tap', 'node', function(event) {
// `cy` prefix has been removed from names of event fields
var node = event.target;
console.log('tapped on ' + node.id());
});
// `cy.onRender()` and `cy.offRender()` have been replaced with the `render` event
cy.once('render', () => {
console.log('render event');
});
// The `grab` event has been broken into two parts: `grab` and `grabon`
var grabCount = 0;
cy.nodes().on('grab', () => {
grabCount += 1;
});
cy.nodes().on('grabon', (event) => {
console.log('clicked on node ' + event.target.id());
})
cy.nodes().on('free', () => {
console.log('grabbed ' + grabCount + ' nodes');
grabCount = 0;
});
});