Link Tools

This is the seventh article of the intermediate section of the JointJS tutorial. Go back to link labels. Alternatively, you can return to index of all articles.

JointJS allows creating fully customizable user interaction tools for your links. These tools show up on user interaction (e.g. mouseover) with a link view and allow the user to interact with the underlying link model. For example:

JointJS source code: link-tools-example.js

The process of getting link tools up and running on your link view is relatively straightforward:

We will explain every step in turn. We will also touch on creating custom buttons.

A link tool (type joint.dia.ToolView) is a view that renders a certain type of control elements on top of the link view it is attached to; for example the Vertices tool creates an interactive handle above every vertex (these handles then allow the user to move and/or delete each vertex). The JointJS library comes with a collection of pre-made link tool definitions in the joint.linkTools namespace.

The pre-made link tools include:

To create a new link tool, we call its constructor:

var verticesTool = new joint.linkTools.Vertices();

The link tool constructors also accept optional arguments that modify the appearance and function of the created tools:

var verticesTool = new joint.linkTools.Vertices({
    redundancyRemoval: false,
    snapRadius: 10,
    vertexAdding: false,
});

Add to Tools View

Link tools always need to come bundled in a tools view object (type joint.dia.ToolsView). This allows them to be shown/hidden as a group above a link. We create a new tools view and add our tools to it:

var verticesTool = new joint.linkTools.Vertices();
var segmentsTool = new joint.linkTools.Segments();
var sourceArrowheadTool = new joint.linkTools.SourceArrowhead();
var targetArrowheadTool = new joint.linkTools.TargetArrowhead();
var sourceAnchorTool = new joint.linkTools.SourceAnchor();
var targetAnchorTool = new joint.linkTools.TargetAnchor();
var boundaryTool = new joint.linkTools.Boundary();
var removeButton = new joint.linkTools.Remove();

var toolsView = new joint.dia.ToolsView({
    tools: [
        verticesTool, segmentsTool,
        sourceArrowheadTool, targetArrowheadTool,
        sourceAnchorTool, targetAnchorTool,
        boundaryTool, removeButton
    ]
});

Remember, it is necessary to create a new set of tools for every new tools view object we create; tools are automatically reassigned to the last tools view that uses them.

Finally, we need to add our tools view to a link view. The joint.dia.LinkView class comes with a suite of tools-related methods:

We can thus show all of our tools in one place:

Try playing around with the user controls. Note that redundant vertices are will be removed whenever they appear; new vertices can be added by clicking anywhere on the link. Double-clicking a vertex removes it. The disconnected handles are link anchors; when moved, they stay within the bounds of their respective end elements.

var linkView = link.findView(paper);
linkView.addTools(toolsView);

JointJS source code: link-tools-all.js

Remember, it is necessary to create a new tools view object for every link view; tools view objects are automatically reassigned to the last link view that uses them.

Interaction

You can easily toggle visibility of link tools using the 'link:mouseenter'/'link:mouseleave' Paper events:

paper.on('link:mouseenter', function(linkView) {
    linkView.showTools();
});

paper.on('link:mouseleave', function(linkView) {
    linkView.hideTools();
});

JointJS source code: link-tools-interaction.js

More complex interaction scenarios might require showing, hiding or removing all tools at once. You can find relevant functions in the Paper class:

These functions can also help to make anchor handles (which usually lie outside the path but inside an element) more accessible to users - by hiding link tools only if the mouse pointer enters a blank area of the paper:

paper.on('link:mouseenter', function(linkView) {
    linkView.showTools();
});

paper.on('blank:mouseover', function(linkView) {
    paper.hideTools();
});

Custom Buttons

It is possible to create custom buttons to complement the pre-made Remove button tool; JointJS exposes the joint.linkTools.Button class for you to extend. The markup of the new button can be sent as options.markup, while the behavior of the button on pointerdown interaction is determined by the callback function provided in options.action.

You can add the extended button to the joint.linkTools namespace and then just use that class in the code:

joint.linkTools.InfoButton = joint.linkTools.Button.extend({
    name: 'info-button',
    options: {
        markup: [{
            tagName: 'circle',
            selector: 'button',
            attributes: {
                'r': 7,
                'fill': '#001DFF',
                'cursor': 'pointer'
            }
        }, {
            tagName: 'path',
            selector: 'icon',
            attributes: {
                'd': 'M -2 4 2 4 M 0 3 0 0 M -2 -1 1 -1 M -1 -4 1 -4',
                'fill': 'none',
                'stroke': '#FFFFFF',
                'stroke-width': 2,
                'pointer-events': 'none'
            }
        }],
        distance: 60,
        offset: 0,
        action: function(evt) {
            alert('View id: ' + this.id + '\n' + 'Model id: ' + this.model.id);
        }
    }
});

var infoButton = new joint.linkTools.InfoButton();
var toolsView = new joint.dia.ToolsView({
    tools: [infoButton]
});

var linkView = link.findView(paper);
linkView.addTools(toolsView);

JointJS source code: link-tools-custom-button.js

A single-use custom button can also be created by direct reference to the Button class, without making an entry in the joint.linkTools namespace:

var infoButton = new joint.linkTools.Button({
    markup: [{
        tagName: 'circle',
        selector: 'button',
        attributes: {
            'r': 7,
            'fill': '#001DFF',
            'cursor': 'pointer'
        }
    }, {
        tagName: 'path',
        selector: 'icon',
        attributes: {
            'd': 'M -2 4 2 4 M 0 3 0 0 M -2 -1 1 -1 M -1 -4 1 -4',
            'fill': 'none',
            'stroke': '#FFFFFF',
            'stroke-width': 2,
            'pointer-events': 'none'
        }
    }],
    distance: 60,
    offset: 0,
    action: function(evt) {
        alert('View id: ' + this.id + '\n' + 'Model id: ' + this.model.id);
    }
});

var toolsView = new joint.dia.ToolsView({
    tools: [infoButton]
});

var linkView = link.findView(paper);
linkView.addTools(toolsView);

This concludes the intermediate section of the JointJS tutorial. Congratulations! You should now be able to create serializable diagrams with custom elements and links that make use of JointJS attributes and can react to user interaction.

The next step is to head over to the advanced section of the tutorial.