Maksim
Maksim

Reputation: 260

Highlighting edges with Cytoscape JS doesn't work

I want to highlight outgoing edges after clicking on node. Now, I have such code:

style: cytoscape.stylesheet()
  .selector('node')
    .css({
      'content': 'data(id)',
      'background-color': '#4286f4'
    })
  .selector('edge.highlighted')
    .css({
      'line-color': 'black',
      'target-arrow-color': '#b830f7'
    })
  .selector('edge')
    .css({
      'curve-style': 'bezier',
      'target-arrow-shape': 'triangle',
      'width': 4,
      'line-color': '#4286f4',
      'target-arrow-color': '#4286f4'
    }),
 //else parameters
 });
cy.on('click', function(e){
    var edges = cy.edges();
    edges.removeClass('highlighted');
});
cy.on('click', 'node', function(e){
    var id = e.target.id();
    var outgoing = cy.edges("[source='" + id + "']")
    outgoing.addClass('highlighted');
});

And when line-color isn't set by 'edge' selector it works fine, but if I set some color to edges - new color doesn't apply after clicking on node.

Upvotes: 3

Views: 3467

Answers (4)

GuyFromChennai
GuyFromChennai

Reputation: 1387

You can highlight the selected node and all outgoing edges and connecting nodes to selected node by doing this

 cy.on('tap', 'node', function(evt) {
      const target: any = evt.target;
      const node = target[0]._private.data;
      console.log( 'tapped ' , node.name);

      cy.elements().difference(target.outgoers()).not(target).addClass('semitransp');
      target.addClass('highlight').outgoers().addClass('highlight');


    });

You can deselect all nodes and edges by clicking on the cy (outside the graph) by doing the below

cy.on('click',function(evt){
    //select either edges or nodes to remove the styles
    //var edges = cy.edges();
    //var nodes = cy.nodes()
    // edges.removeClass('semitransp');
    // nodes.removeClass('semitransp');
    //you can select all elements and remove the styles
    cy.elements().removeClass('semitransp');      
})

The style/css details for your reference

{
    selector: 'node.highlight',
    style: {
        'border-color': '#FFF',
        'border-width': '2px'
    }
},
{
    selector: 'node.semitransp',
    style: { 'opacity': '0.5' }
},
{
    selector: 'edge.highlight',
    style: { 'mid-target-arrow-color': '#FFF' }
},
{
    selector: 'edge.semitransp',
    style: { 'opacity': '0.2' }
}

Upvotes: 0

Stephan T.
Stephan T.

Reputation: 6074

I added something crucial to my code at work that solved the problem for me:

style: cytoscape.stylesheet()
  .selector('node')
    .css({
      'content': 'data(id)',
      'background-color': '#4286f4'
    })
  .selector('edge.highlighted')
    .css({
      'line-color': 'black',
      'target-arrow-color': '#b830f7'
    })
  .selector('edge')
    .css({
      'curve-style': 'bezier',
      'target-arrow-shape': 'triangle',
      'width': 4,
      'line-color': '#4286f4',
      'target-arrow-color': '#4286f4'
    }),
});

cy.unbind('click');
cy.bind('click', function(e){
   if (e.target === cy || e.target.group() == "edges")  {
      cy.edges().removeClass('highlighted');  
   }
   else { 
      cy.edges("[source='" + e.target.id() + "']").addClass('highlighted');
   }
});

cy.on() is a synonym for the bind operation, so this can cause a lot of errors, even if your problem still exists, you have to unbind previous binds, otherwhise the code is executed twice or more.

Here is the whole init of cytoscape:

// Initialize cytoscape
cy = window.cy = cytoscape({
    container: $('.cy'),
    boxSelectionEnabled: false,
    autounselectify: true,
    layout: {
       name: 'grid'
    },
    style: [
       {
         selector: 'node',
         style: {
           'shape': 'data(faveShape)',
           'content': 'data(DisplayName)',
           'height': 'data(faveHeight)',
           'width': 'data(faveWidth)',
           'background-color': 'data(faveColor)',
           'line-color': '#a8eae5',
           'font-family': 'Segoe UI,Helvetica Neue,Helvetica,Arial,Verdana',
           'font-size': '15px',
         }  
       },  
       {   
         selector: 'edge',
         style: {
           'label': 'data(myLabel)',
           'curve-style': 'bezier',
           'width': 5,
           'opacity': 0.5,
           'line-color': '#a8eae5',
           'font-size': '12px',
           'target-arrow-shape': 'triangle',
           'target-arrow-color': '#a8eae5'
         } 
       },  
       {   
         selector: '.autorotate',
         style: {
           'edge-text-rotation': 'autorotate'
         } 
       },  
       {   
         selector: ".center-center",
         style: {
           "text-valign": "center",
           "text-halign": "center"
         } 
       },  
       {   
         selector: 'edge.highlighted',
         style: {
           'line-color': '#2a6cd6',
           'target-arrow-color': '#2a6cd6',
           'opacity': 0.7,
         } 
       },  
       {    
         selector: 'edge.deactivate',
         style: {
           'opacity': 0.1,
         } 
       },  
       {   
         selector: 'node.deactivated',
         style: {
           'opacity': 0.1,
         }
       }
   ],
 });
 // After some other functions where I select the nodes I want to display I
 // empty the graph and then:

 cy.add(jsonNew);
 layout = cy.elements().layout({
     name: 'concentric'
 }).run();

 cy.fit(cy.elements());
 cy.center();;

 cy.unbind('click');
 cy.unbind('tapstart');
 cy.unbind('tapend');
 cy.bind('click ', 'node', function (evt) {
    onTap(evt);
 });

 cy.bind('tapstart ', 'node', function (evt) {
    cy.edges("[source = '" + evt.target.id()+"']").addClass('highlighted');
    cy.edges("[source !='" + evt.target.id()+"']").addClass('deactivate');
    // Here is a complicated algorithm to get all nodes not connected to the  
    // node which is currently held, I get all these nodes and then make 
    // them almost invisible. When the node is released I set remove the 
    // node.deactivated class from all nodes
 });

 cy.bind('tapend ', 'node', function (evt) {
    cy.edges("[source ='"+evt.target.id()+"']").removeClass('highlighted');
    cy.edges("[source !='"+evt.target.id()+"']").removeClass('deactivate');
 });

Upvotes: 0

Maksim
Maksim

Reputation: 260

It is possible to add special selector for line-color:

.selector('edge.lines')
    .css({
      'line-color': '#4286f4',
      'target-arrow-color': '#4286f4'
    })

And then use cy.edges().classes('highlighted') to replace edges' class with highlighted class, or use cy.edges().classes('lines') to set class to 'lines'

Upvotes: 0

Stephan T.
Stephan T.

Reputation: 6074

style: cytoscape.stylesheet()
  .selector('node')
    .css({
      'content': 'data(id)',
      'background-color': '#4286f4'
    })
  .selector('edge.highlighted')
    .css({
      'line-color': 'black',
      'target-arrow-color': '#b830f7'
    })
  .selector('edge')
    .css({
      'curve-style': 'bezier',
      'target-arrow-shape': 'triangle',
      'width': 4,
      'line-color': '#4286f4',
      'target-arrow-color': '#4286f4'
    }),
});

cy.on('click', function(e){
   if (e.target === cy || e.target.group() == "edges")  {
      cy.edges().removeClass('highlighted');  
   }
   else { 
      cy.edges("[source='" + e.target.id() + "']").addClass('highlighted');
   }
});

I think this should work fine, you calles two click methods at the same time, that may have caused some problems, please let me know if this fixed it or not :) .

If you want to have two seperate click events, you can write this:

cy.on('click', function(e){
   if (e.target === cy || e.target.group() == "edges")  {
      cy.edges().removeClass('highlighted');
});

cy.on('click', 'node', function(e){
   cy.edges("[source='" + e.target.id() + "']").addClass('highlighted');
});

Upvotes: 2

Related Questions