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 #1533thisis no longer modified to be the element of interest for many callback functions #1535cyprefix has been removed from names of event fields #1537- A clearer distinction is made between
display,visibility, andopacityin the internal API #1544 - The
:touchselector has been removed #1540 cy.onRender()andcy.offRender()have been replaced with therenderevent #1541- The
grabevent has been broken into two parts:grabandgrabon#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
inhibitorarrow shape has been removed #1655 coseno 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
dataparameters 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
thisis 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
thisin the bodies of functions (keeps thethiscontext 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:
cyprefix has been removed from names of event fields #1537- For example,
event.cyTargetis nowevent.target.event.cyTarget.id()would now beevent.target.id().
- For example,
- A clearer distinction is made between
display,visibility, andopacityin the internal API, bringing them in line with the HTML/ CSS counterparts #1544- Only applies to internal APIs
display,visibility, andopacityrefer 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 beelementfor displayed ornonefor not displayed.visibility: Whether the element is visible; may bevisibleorhidden.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: noneno no hidden no visibility: hiddenyes yes visible no opacity: 0yes yes visible yes - The
:touchselector has been removed #1540- It’s less reliable now that browsers are implementing touch APIs without hardware support.
This means that the
:touchselector 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 therenderevent #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
grabevent has been broken into two parts:grabandgrabon#1545grabnow refers to all elements that are currently being grabbed (such as during a drag).grabonrefers 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 therenderevent. - For example,
cy.one('render', () => console.log('initial render'))
- Instead, use a combination of
- The
inhibitorarrow shape has been removed #1655- The shape was never publically documented (and had an incorrect name).
coseno 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
weaverproperty 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
dataparameter 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
dataparameter 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;
});
});