Reputation: 43
I'm new to Cytoscape.js and so far I really appreciate what it does. My issue is the following : for my project I need to create a graph that can be modified by the user, and I need to have a reset button that would replace all elements as the user found them in the first place.
I use the cose-bilkent layout as I take my data with no position information.
Here is my js code.
var cy = cytoscape({
container: document.getElementById('cy'),
style: [{
selector: 'node',
style: {
shape: 'roundrectangle',
'background-color': 'red',
label: 'data(id )'
}
}, ],
});
cy.on('tap', 'node', function(evt) {
var node = evt.target;
console.log("tap", node.id(), node.position());
});
cy.add([{
data: {
id: 'a'
}
},
{
data: {
id: 'b'
}
},
{
data: {
id: 'c'
}
},
{
data: {
id: 'ab',
source: 'a',
target: 'b'
}
},
{
data: {
id: 'ac',
source: 'a',
target: 'c'
}
}
]);
cy.layout({
name: 'cose-bilkent',
avoidOverlap: true,
avoidOverlapPadding: 10,
}).run();
cy.nodes().forEach(function(ele) {
console.log("loop", ele.id(), ele.position());
});
When I look at the console after having clicked on a node, I see the following :
My question is : why when I click on the nodes, the object position is correct ({x: 30, y: 30}), whereas in the loop the object is {x:0, y:0} ?
EDIT : After Stephan's answer I changed my code to the following :
var cy = cytoscape({
container: document.getElementById('cy'),
style: [
{
selector: 'node',
style: {
shape: 'roundrectangle',
'background-color': 'red',
label: 'data(id )'
}
},
],
});
cy.on('tap', 'node', function(evt) {
var node = evt.target;
console.log("tap", node.id(), node.position());
});
cy.add([
{ data: { id: 'a' } },
{ data: { id: 'b' } },
{ data: { id: 'c' } },
{
data: {
id: 'ab',
source: 'a',
target: 'b'
}
},
{
data: {
id: 'ac',
source: 'a',
target: 'c'
}
}
]);
cy.layout({
name: 'cose-bilkent',
avoidOverlap: true,
avoidOverlapPadding: 10,
}).run();
setTimeout( function() {
cy.nodes().forEach(function(ele){
console.log("loop", ele.id(), ele.position());
});
}, 2000);
// Not Working :
// cy.ready( function() {
// cy.nodes().forEach(function(ele){
// console.log("loop", ele.id(), ele.position());
// });
// });
SOLUTION :
In the end I found a somewhat better solution : I used the event 'layoutstop' in order to start the copy of positions at the right moment :
cy.on('layoutstop', function() {
cy.nodes().forEach(function(ele){
console.log("loop", ele.id(), ele.position().x, ele.position().y);
});
});
However if you use this solution you have to know that it doesn't work with an exterior function (for some unknown reason). Thus the following won't work :
cy.on('layoutstop', copyPos());
function copyPos() {
cy.nodes().forEach(function(ele){
console.log("loop", ele.id(), ele.position().x, ele.position().y);
});
}
Upvotes: 3
Views: 1782
Reputation: 6074
I tested your function, it seems to work fine with me, the loop returns the model position (absolute position) of each node. The only thing that comes to my mind is that you call the function before the nodes actually loaded:
var cy = cytoscape({
...
style: [...],
});
cy.on('tap', 'node', function(evt) {
var node = evt.target;
console.log("tap", node.id(), node.position());
});
cy.add([...]);
// Wait for the eles to be added
cy.layout({...}).run(); // Call layout on eles
cy.ready(function () { // Wait for cytoscape to actually load and map eles
cy.nodes().forEach(function(ele) { // Your function call inside
console.log("loop", ele.id(), ele.position());
});
});
Before cytoscape's layout is fully loaded, all nodes start at the position (0,0), which then is changed (async function). So that may be why your funciton does that...
Upvotes: 1