I built a chart in web webpage using the JavaScript chart program cytoscape.js. It does not display anything at all. But it doesn't' give syntax errors either, other than a warning about taking over the mouse for zooming purposes.
Here is the code:
<style type="text/css">
#cy {
width: 90%;
height: 300px;
display: block;
}
</style>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.14.0/cytoscape.min.js"></script>
<script type="text/javascript">
var cy = cytoscape({
container: document.getElementById('cy'), // container to render in
style: [ // the stylesheet for the graph
{
selector: 'node',
style: {
'background-color': '#666',
'label': 'data(id)'
}
},
{
selector: 'edge',
style: {
'width': 3,
'line-color': '#ccc',
'target-arrow-color': '#ccc',
'target-arrow-shape': 'triangle'
}
}
],
// initial viewport state:
zoom: 1,
pan: { x: 0, y: 0 },
// interaction options:
minZoom: 1e-50,
maxZoom: 1e50,
zoomingEnabled: true,
userZoomingEnabled: true,
panningEnabled: true,
userPanningEnabled: true,
boxSelectionEnabled: true,
selectionType: 'single',
touchTapThreshold: 8,
desktopTapThreshold: 4,
autolock: false,
autoungrabify: false,
autounselectify: false,
// rendering options:
headless: false,
styleEnabled: true,
hideEdgesOnViewport: false,
textureOnViewport: false,
motionBlur: false,
motionBlurOpacity: 0.2,
wheelSensitivity: 1,
pixelRatio: 'auto'
});
let options = {
name: 'breadthfirst',
fit: true, // whether to fit the viewport to the graph
directed: false, // whether the tree is directed downwards (or edges can point in any direction if false)
padding: 30, // padding on fit
circle: false, // put depths in concentric circles if true, put depths top down if false
grid: false, // whether to create an even grid into which the DAG is placed (circle:false only)
spacingFactor: 1.75, // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
avoidOverlap: true, // prevents node overlap, may overflow boundingBox if not enough space
nodeDimensionsIncludeLabels: false, // Excludes the label when calculating node bounding boxes for the layout algorithm
roots: undefined, // the roots of the trees
maximal: false, // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
animate: false, // whether to transition the node positions
animationDuration: 500, // duration of animation in ms if enabled
animationEasing: undefined, // easing of animation if enabled,
animateFilter: function (node, i) { return true; }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
ready: undefined, // callback on layoutready
stop: undefined, // callback on layoutstop
transform: function (node, position) { return position; } // transform a given node position. Useful for changing flow direction in discrete layouts
};
cy.layout(options);
var eles1 = cy.add([
{ group: 'nodes', data: { id: 'E1'} },
{ group: 'nodes', data: { id: 'E2'} },
{ group: 'nodes', data: { id: 'E3'} },
{ group: 'nodes', data: { id: 'E4'} },
{ group: 'nodes', data: { id: 'E5'} },
{ group: 'nodes', data: { id: 'E6'} },
{ group: 'nodes', data: { id: 'E7'} },
{ group: 'nodes', data: { id: 'E8'} } ]);
var eles2 = cy.add([
{ group: 'nodes', data: { id: 'LH1'} },
{ group: 'nodes', data: { id: 'RH1'} },
{ group: 'nodes', data: { id: 'LH2'} },
{ group: 'nodes', data: { id: 'LH3'} },
{ group: 'nodes', data: { id: 'RH2'} },
{ group: 'nodes', data: { id: 'LH4'} },
{ group: 'nodes', data: { id: 'RH3'} },
{ group: 'nodes', data: { id: 'RH4'} } ]);
var eles4 = cy.add([
{ group: 'edges', data: { id: 'edge0', source: 'E4', target: 'E5' } },
{ group: 'edges', data: { id: 'edge1', source: 'E6', target: 'E7' } },
{ group: 'edges', data: { id: 'edge2', source: 'LH1', target: 'E1' } },
{ group: 'edges', data: { id: 'edge3', source: 'LH1', target: 'RH1' } },
{ group: 'edges', data: { id: 'edge4', source: 'LH2', target: 'E4' } },
{ group: 'edges', data: { id: 'edge5', source: 'LH3', target: 'E4' } },
{ group: 'edges', data: { id: 'edge6', source: 'LH4', target: 'E6' } },
{ group: 'edges', data: { id: 'edge7', source: 'RH1', target: 'E2' } },
{ group: 'edges', data: { id: 'edge8', source: 'RH1', target: 'E3' } },
{ group: 'edges', data: { id: 'edge9', source: 'RH2', target: 'E5' } },
{ group: 'edges', data: { id: 'edge10', source: 'RH3', target: 'E7' } },
{ group: 'edges', data: { id: 'edge11', source: 'RH4', target: 'E8' } } ]);
</script>
<center>
<div id="cy" >
</div>
</center>
My code is actually generated in an asp.net page, but that should not make a difference.
You are adding the elements to the graph after the layout has been run. That's why all your nodes are piled up on the upper left corner.
You have two choices here:
cy.json()
) and then tell cytoscape to run the layout with cy.layout(...).run()
.Documentation for cy.layout()
is here, and for cy.json()
here.
The following snippet uses the first approach, but I have left the second one commented in case you cannot supply the elements on the constructor call (i.e. if you have to fetch them asynchronously).
var nodeArray = [
{ group: 'nodes', data: { id: 'E1'} },
{ group: 'nodes', data: { id: 'E2'} },
{ group: 'nodes', data: { id: 'E3'} },
{ group: 'nodes', data: { id: 'E4'} },
{ group: 'nodes', data: { id: 'E5'} },
{ group: 'nodes', data: { id: 'E6'} },
{ group: 'nodes', data: { id: 'E7'} },
{ group: 'nodes', data: { id: 'E8'} },
{ group: 'nodes', data: { id: 'LH1'} },
{ group: 'nodes', data: { id: 'RH1'} },
{ group: 'nodes', data: { id: 'LH2'} },
{ group: 'nodes', data: { id: 'LH3'} },
{ group: 'nodes', data: { id: 'RH2'} },
{ group: 'nodes', data: { id: 'LH4'} },
{ group: 'nodes', data: { id: 'RH3'} },
{ group: 'nodes', data: { id: 'RH4'} }
];
var edgeArray = [
{ group: 'edges', data: { id: 'edge0', source: 'E4', target: 'E5' } },
{ group: 'edges', data: { id: 'edge1', source: 'E6', target: 'E7' } },
{ group: 'edges', data: { id: 'edge2', source: 'LH1', target: 'E1' } },
{ group: 'edges', data: { id: 'edge3', source: 'LH1', target: 'RH1' } },
{ group: 'edges', data: { id: 'edge4', source: 'LH2', target: 'E4' } },
{ group: 'edges', data: { id: 'edge5', source: 'LH3', target: 'E4' } },
{ group: 'edges', data: { id: 'edge6', source: 'LH4', target: 'E6' } },
{ group: 'edges', data: { id: 'edge7', source: 'RH1', target: 'E2' } },
{ group: 'edges', data: { id: 'edge8', source: 'RH1', target: 'E3' } },
{ group: 'edges', data: { id: 'edge9', source: 'RH2', target: 'E5' } },
{ group: 'edges', data: { id: 'edge10', source: 'RH3', target: 'E7' } },
{ group: 'edges', data: { id: 'edge11', source: 'RH4', target: 'E8' } }
];
var stylesArray = [{
selector: 'node',
style: {
'background-color': '#666',
'label': 'data(id)'
}
},{
selector: 'edge',
style: {
'width': 3,
'line-color': '#ccc',
'target-arrow-color': '#ccc',
'target-arrow-shape': 'triangle'
}
}];
var layout = {name: 'breadthfirst'};
// You used mostly default options, there's no need to redefine them
var cy = cytoscape({
container: document.getElementById('cy'),
style: stylesArray,
// If you want to apply the layout on the constructor
// you must supply the elements too
layout: layout,
elements: {
nodes: nodeArray,
edges: edgeArray
}
});
// Or add the elements afterwards, it's your choice
// But then you need to re-run the layout
/*
cy.json({
elements: {
nodes: nodeArray,
edges: edgeArray
}
});
// Tell cytoscape to apply the layout when ready
cy.ready(function () {
cy.layout(layout).run();
});
*/
#cy {
width: 90%;
height: 300px;
display: block;
}
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.14.0/cytoscape.min.js"></script>
<div id="cy"></div>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With