Search code examples
javascriptjqueryjsplumb

JS-Plumb: (Re-)Draw a line between two elements after clicking


Edit: After trying out different hand-made solutions, I am using JSPlumb and trying to let it visually connect a clicked item from one list with a clicked item from another list (see screenshot).

Basic visual connection

I built upon this Stackoverflow thread and made it work basically, however the code provided there allows multiple connections, i.e. JSPlumb draws multiple endpoints and lines, and it doesn't react if a 'Target' is clicked first. However, in my case there should be strictly only one connection, and JSPlumb should re-connect once I click on another list item on either side. (E.g. I click on 'Source 1' and 'Target 3', JSPlumb draws the connection. I click on 'Target 4', JSPlumb should keep 'Source 1' as source and re-set 'Target 4' as the target, e.g. now draw the connection from 'Source 1' to 'Target 4'. The same with clicking a different 'Source', i.e. the target should stay the same.)

In what way would I need to alter the code in order to achieve the desired re-draw?

CodePen

jQuery(document).ready(function () {  
   var targetOption = {
        anchor: "LeftMiddle",
        isSource: false,
        isTarget: true,
        reattach: true,
        endpoint: "Dot",
        connector: ["Bezier", {
                   curviness: 50}],
        setDragAllowedWhenFull: true
    };

    var sourceOption = {
        tolerance: "touch",
        anchor: "RightMiddle",
        maxConnections: 1,
        isSource: true,
        isTarget: false,
        reattach: true,
        endpoint: "Dot",
        connector: ["Bezier", {
                   curviness: 50}],
        setDragAllowedWhenFull: true
    };

    jsPlumb.importDefaults({
        ConnectionsDetachable: true,
        ReattachConnections: true,
        Container: 'page_connections'
    });

    //current question clicked on
    var questionSelected = null;
    var questionEndpoint = null;

    //remember the question you clicked on
    jQuery("#select_list_lebensbereiche ul > li").click( function () {

        //remove endpoint if there is one
        if( questionSelected !== null )
        {
            jsPlumb.removeAllEndpoints(questionSelected);
        }

        //add new endpoint
        questionSelected = jQuery(this)[0];
        questionEndpoint = jsPlumb.addEndpoint(questionSelected, sourceOption);
    });

    //now click on an answer to link it with previously selected question
    jQuery("#select_list_wirkdimensionen ul > li").click( function () {

        //we must have previously selected question
        //for this to work
        if( questionSelected !== null )
        {
            //create endpoint
            var answer = jsPlumb.addEndpoint(jQuery(this)[0], targetOption);

            //link it
            jsPlumb.connect({ source: questionEndpoint, target: answer }); 
            //cleanup
            questionSelected = null;
            questionEndpoint = null;
        }
    }); 

});

Solution

  • You were already keeping track of the "source" end of the linked items in a global variable; one way of getting to your desired behavior mostly just requires keeping track of the "target" end the same way. (There's room for improving this -- globals are maybe not an ideal strategy, and there's some code duplication between the 'source' and 'target' click handlers, but this should do for demonstration at least.)

      // ...other init variables skipped
    
      var questionEndpoints = []; // 'source' and 'target' endpoints
    
      // "source" click handler
      jQuery("#select_list_lebensbereiche ul > li").click(function() {
        //remove existing start endpoint, if any:
        jsPlumb.deleteEndpoint(questionEndpoints[0]);
        // add a new one on the clicked element:
        questionEndpoints[0] = jsPlumb.addEndpoint(jQuery(this), sourceOption);
        connectEndpoints();
      });
      
      // "target" endpoint
      jQuery("#select_list_wirkdimensionen ul > li").click(function() {
        if (!questionEndpoints[0]) return; // don't respond if a source hasn't been selected
        // remove existing endpoint if any
        jsPlumb.deleteEndpoint(questionEndpoints[1]);
        //create a new one:
        questionEndpoints[1] = jsPlumb.addEndpoint(jQuery(this), targetOption);
        connectEndpoints();
      });
    
      var connectEndpoints = function() {
        jsPlumb.connect({
          source: questionEndpoints[0],
          target: questionEndpoints[1]
        });
      };
    });
    

    Working CodePen example