## Links

In the first part of the tutorial, we saw a working example of a simple JointJS application:

In the previous section, we focused on making the elements look much more interesting:

In this section, we are going to understand the other crucial building block of JointJS diagrams - links. This is an introduction to links as they appear in the Hello, World! application; more advanced topics are covered later in the tutorial series. We will continue with the code we modified in the previous section:

    <!DOCTYPE html>
    <html>
    <head>
        <link rel="stylesheet" type="text/css" href="node_modules/jointjs/dist/joint.css" />
    </head>
    <body>
        <!-- content -->
        <div id="myholder"></div>

        <!-- dependencies -->
        <script src="node_modules/jquery/dist/jquery.js"></script>
        <script src="node_modules/lodash/lodash.js"></script>
        <script src="node_modules/backbone/backbone.js"></script>
        <script src="node_modules/jointjs/dist/joint.js"></script>

        <!-- code -->
        <script type="text/javascript">

            var graph = new joint.dia.Graph;

            var paper = new joint.dia.Paper({
                el: document.getElementById('myholder'),
                model: graph,
                width: 600,
                height: 300, // height had to be increased
                gridSize: 10,
                drawGrid: true,
                background: {
                    color: 'rgba(0, 255, 0, 0.3)'
                }
            });

            var rect = new joint.shapes.standard.Rectangle();
            rect.position(100, 30);
            rect.resize(100, 40);
            rect.attr({
                body: {
                    fill: 'blue'
                },
                label: {
                    text: 'Hello',
                    fill: 'white'
                }
            });
            rect.addTo(graph);

            var rect2 = new joint.shapes.standard.Rectangle();
            rect2.position(400, 30);
            rect2.resize(100, 40);
            rect2.attr({
                body: {
                    fill: '#2C3E50',
                    rx: 5,
                    ry: 5,
                    strokeWidth: 2
                },
                label: {
                    text: 'World!',
                    fill: '#3498DB',
                    fontSize: 18,
                    fontWeight: 'bold',
                    fontVariant: 'small-caps'
                }
            });
            rect2.addTo(graph);

            var link = new joint.shapes.standard.Link();
            link.source(rect);
            link.target(rect2);
            link.addTo(graph);

            var rect3 = new joint.shapes.standard.Rectangle();
            rect3.position(100, 130);
            rect3.resize(100, 40);
            rect3.attr({
                body: {
                    fill: '#E74C3C',
                    rx: 20,
                    ry: 20,
                    strokeWidth: 0
                },
                label: {
                    text: 'Hello',
                    fill: '#ECF0F1',
                    fontSize: 11,
                    fontVariant: 'small-caps'
                }
            });
            rect3.addTo(graph);

            var rect4 = new joint.shapes.standard.Rectangle();
            rect4.position(400, 130);
            rect4.resize(100, 40);
            rect4.attr({
                body: {
                    fill: '#8E44AD',
                    strokeWidth: 0
                },
                label: {
                    text: 'World!',
                    fill: 'white',
                    fontSize: 13
                }
            });
            rect4.addTo(graph);

            var link2 = new joint.shapes.standard.Link();
            link2.source(rect3);
            link2.target(rect4);
            link2.addTo(graph);

            var rect5 = new joint.shapes.standard.Rectangle();
            rect5.position(100, 230);
            rect5.resize(100, 40);
            rect5.attr({
                body: {
                    fill: '#2ECC71',
                    strokeDasharray: '10,2'
                },
                label: {
                    text: 'Hello',
                    fill: 'black',
                    fontSize: 13
                }
            });
            rect5.addTo(graph);

            var rect6 = new joint.shapes.standard.Rectangle();
            rect6.position(400, 230);
            rect6.resize(100, 40);
            rect6.attr({
                body: {
                    fill: '#F39C12',
                    rx: 20,
                    ry: 20,
                    strokeDasharray: '1,1'
                },
                label: {
                    text: 'World!',
                    fill: 'gray',
                    fontSize: 18,
                    fontWeight: 'bold',
                    fontVariant: 'small-caps',
                    textShadow: '1px 1px 1px black'
                }
            });
            rect6.addTo(graph);

            var link3 = new joint.shapes.standard.Link();
            link3.source(rect5);
            link3.target(rect6);
            link3.addTo(graph);

        </script>
    </body>
    </html>

Working with links is similar to working with elements:

- First, a link is created by calling its constructor.
- Then, different methods are called on the link to set its properties (source & target, vertices, router & connector, attributes, labels...).
- Finally, the link is added to the graph.

In our case, the three links are instances of `joint.shapes.standard.Link`. The standard shape library contains several other ready-made link definitions (e.g. DoubleLink and ShadowLink) that can be used in your document. Moreover, advanced users may create their own link definitions instead, by extending the basic `joint.dia.Link` class.

Our demo showcases the two required Link methods:

- `link.source()` and `link.target()` - sets the source/target of the link. To connect the link to an element, pass the element to the function (as we do in our example). To pin the link to a fixed point on the paper, pass a `g.Point` (or an object with `x` and `y` properties).

Other important methods include:

- `link.clone()` - clones an existing link, including its source, target, vertices, router, connector, attributes, and labels (attributes and labels are explained in more detail below).
- `link.addTo()` - adds the link to a graph so it can be rendered.

### Link Geometry

The shape of the link can be set with three optional methods:

- `link.vertices()` - sets the `vertices` array of the link.

Vertices are user-defined points on the paper that the link should pass trough on its way from `source` to `target`. The default is an empty array (meaning that the link navigates from source to target with no detour).

- `link.router()` - sets the `router` of the link.

The router is a function that takes the array of link vertices and adds additional points to it as necessary to create a route with desired characteristics; for example, the `orthogonal` router creates a route consisting of vertical and horizontal lines attached at right angles. JointJS comes with a collection of pre-defined routers in the `joint.routers` namespace. Two of the provided routers are smart routers that are able to navigate around obstacles in their path. The default is the `normal` router, which returns the array of vertices as route points. Advanced users may provide their own routers to support custom routing strategies.

- `link.connector()` - sets the `connector` of the link.

The connector is a function in charge of rendering the link's path on the paper. It takes the array of route points provided by a router and constructs a series of SVGPathElement path data commands to create a path with desired characteristics; for example, the `rounded` connector creates straight line segments with rounded corners around route points. JointJS comes with a collection of pre-defined connectors in the `joint.connectors` namespace. The default is the `normal` connector, which connects route points with simple straight lines. Advanced users may provide their own connectors to support custom connecting strategies.

Link geometry is also affected by the anchor and connectionPoint methods applied to link source and target, as well as the connectionStrategy set on the paper.

### Link Styling

Link styling works analogously to element styling:

- `link.attr()` - programmatically assigns SVG attributes directly to the SVGElements of the link's markup. (CSS styles may still be used on top of the styling defined here, and they will have precedence over these attributes.)

When `link.attr()` is called with one object as argument, the keys of the object are selectors that correspond to SVGElements of the link's markup; each of those can have one or more attributes defined alongside the value to be set. If you only need to change one value, you can also call `link.attr()` with two arguments; the first is the path of the attribute in the form `'selector/attribute'` and the second is the value to be assigned.

If you are completely new to SVG, you may want to have a look at, for example, the Fills and Strokes tutorial on MDN. JointJS is able to handle all standard SVG attributes, however, please note that we strongly encourage everyone to use camelCase versions of attribute names for consistency and in order to avoid the need for quotation marks in attribute name keys. In addition, JointJS provides an extensive set of non-standard special JointJS attributes; these allow you to specify attributes relatively to other shape selectors, among other things. Special attributes are discussed in detail later in the tutorial.

The `joint.shapes.standard.Link` shape used in our example has two selectors defined: `line` (the visible `<path>` SVGElement of the link), and `wrapper` (a wider, transparent `<path>` SVGElement under the `line` that makes it easier to interact with the link). Other link shapes have their own selector names (although consistency was preserved where applicable, e.g. with `line`); please refer to the `joint.shapes.standard` documentation for detailed information.

For example, we can change the color of a link by changing the `stroke` color attribute on the `line` selector:

    link.attr('line/stroke', 'orange');

JointJS source code: links-attr.js

The same effect can be achieved by passing a single object argument to `link.attr`:

    link.attr({
        line: { // selector for the visible <path> SVGElement
            stroke: 'orange' // SVG attribute and value
        }
    });

### Link Arrowheads

Two special attributes are in charge of link arrowheads; `sourceMarker` and `targetMarker`. (In the case of `joint.shapes.standard.Link`, they are defined on the `line` selector.) The `'type'` of the arrowhead can be any valid SVGElement type; we will look at `path` and `image` arrowheads specifically, but we will mention the others in more advanced sections of the tutorial.

The following example shows how to create a link with two simple `path` arrowheads. Notice that if the `'fill'` and `'stroke'` colors are not specified, they are adopted from the `line.stroke` attribute. The two arrowheads have the same path data commands, despite pointing in opposite directions; this is because all `targetMarker` values are automatically rotated by 180 degrees. The path commands' coordinate system has origin at the tip of the link and is rotated according to the slope of the link at that point. Together, these characteristics mean that you can design all your arrowheads as if they were pointing left and towards the point `0,0` in local coordinates; JointJS will take care of the rest. Let's illustrate with a simple clock:

    link.attr({
        line: {
            sourceMarker: { // hour hand
                'type': 'path',
                'd': 'M 20 -10 0 0 20 10 Z'
            },
            targetMarker: { // minute hand
                'type': 'path',
                'stroke': 'green',
                'stroke-width': 2
                'fill': 'yellow',
                'd': 'M 20 -10 0 0 20 10 Z'
            }
        }
    });

JointJS source code: links-arrowheads-path.js

Creating `image` arrowheads is similarly straightforward. You just need to provide an url with the path to your image to the `'xlink:href'` property, and then specify the desired `'width'` and `'height'`. The image for `targetMarker` will be automatically rotated by 180 degrees, as expected. Remember to reposition the image in the `'y'` axis if you need the arrowhead to be centered. Let's return to our clock example:

    link.attr({
        line: {
            sourceMarker: {
                'type': 'image',
                'xlink:href': 'http://cdn3.iconfinder.com/data/icons/49handdrawing/24x24/left.png',
                'width': 24,
                'height': 24,
                'y': -12
            },
            targetMarker: {
                'type': 'image',
                'xlink:href': 'http://cdn3.iconfinder.com/data/icons/49handdrawing/24x24/left.png',
                'width': 24,
                'height': 24,
                'y': -12
            }
        }
    });

JointJS source code: links-arrowheads-image.js

### Link Labels

JointJS also supports link labels:

- `link.labels()` - sets the `labels` array of the link. Labels have `markup`, `attrs`, and `position` properties.

Link labels are explained in more detail in a separate section of this tutorial. A simple label definition (including markup and attrs) is built into the `joint.dia.Link` class, from which the `joint.shapes.standard.Link` type inherits it. The builtin default label has two tags: `text` (the `<text>` SVGElement of the label), and `rect` (the `<rect>` SVGElement for label background). The builtin default attributes specify a simple vertical-centered text on a white rounded rectangle. Thus, adding a label can be as simple as passing a value for the `text/text` attribute:

    link.labels([{
        attrs: {
            text: {
                text: 'Hello, World'
            }
        }
    }]);

JointJS source code: links-label-builtin.js

Note that since we were only adding one label, we could have also used the `link.appendLabel()` convenience function:

    link.appendLabel({
        attrs: {
            text: {
                text: 'Hello, World'
            }
        }
    });

More advanced topics about link labels, including custom position, styling and markup, are explained in a separate section of this tutorial.

### Example

Now, let's use what we learned to have some fun with our links:

    <!DOCTYPE html>
    <html>
    <head>
        <link rel="stylesheet" type="text/css" href="node_modules/jointjs/dist/joint.css" />
    </head>
    <body>
        <!-- content -->
        <div id="myholder"></div>

        <!-- dependencies -->
        <script src="node_modules/jquery/dist/jquery.js"></script>
        <script src="node_modules/lodash/lodash.js"></script>
        <script src="node_modules/backbone/backbone.js"></script>
        <script src="node_modules/jointjs/dist/joint.js"></script>

        <!-- code -->
        <script type="text/javascript">

            var graph = new joint.dia.Graph;

            var paper = new joint.dia.Paper({
                el: document.getElementById('myholder'),
                model: graph,
                width: 600,
                height: 300,
                gridSize: 10,
                drawGrid: true,
                background: {
                    color: 'rgba(0, 255, 0, 0.3)'
                }
            });

            var rect = new joint.shapes.standard.Rectangle();
            rect.position(100, 30);
            rect.resize(100, 40);
            rect.attr({
                body: {
                    fill: 'blue'
                },
                label: {
                    text: 'Hello',
                    fill: 'white'
                }
            });
            rect.addTo(graph);

            var rect2 = new joint.shapes.standard.Rectangle();
            rect2.position(400, 30);
            rect2.resize(100, 40);
            rect2.attr({
                body: {
                    fill: '#2C3E50',
                    rx: 5,
                    ry: 5,
                    strokeWidth: 2
                },
                label: {
                    text: 'World!',
                    fill: '#3498DB',
                    fontSize: 18,
                    fontWeight: 'bold',
                    fontVariant: 'small-caps'
                }
            });
            rect2.addTo(graph);

            var link = new joint.shapes.standard.Link();
            link.source(rect);
            link.target(rect2);
            link.attr({
                line: {
                    stroke: 'blue',
                    strokeWidth: 1,
                    sourceMarker: {
                        'type': 'path',
                        'stroke': 'black',
                        'fill': 'red',
                        'd': 'M 10 -5 0 0 10 5 Z'
                    },
                    targetMarker: {
                        'type': 'path',
                        'stroke': 'black',
                        'fill': 'yellow',
                        'd': 'M 10 -5 0 0 10 5 Z'
                    }
                }
            });
            link.labels([{
                attrs: {
                    text: {
                        text: 'Hello, World!'
                    }
                }
            }]);
            link.addTo(graph);

            var rect3 = new joint.shapes.standard.Rectangle();
            rect3.position(100, 130);
            rect3.resize(100, 40);
            rect3.attr({
                body: {
                    fill: '#E74C3C',
                    rx: 20,
                    ry: 20,
                    strokeWidth: 0
                },
                label: {
                    text: 'Hello',
                    fill: '#ECF0F1',
                    fontSize: 11,
                    fontVariant: 'small-caps'
                }
            });
            rect3.addTo(graph);

            var rect4 = new joint.shapes.standard.Rectangle();
            rect4.position(400, 130);
            rect4.resize(100, 40);
            rect4.attr({
                body: {
                    fill: '#8E44AD',
                    strokeWidth: 0
                },
                label: {
                    text: 'World!',
                    fill: 'white',
                    fontSize: 13
                }
            });
            rect4.addTo(graph);

            var link2 = new joint.shapes.standard.Link();
            link2.source(rect3);
            link2.target(rect4);
            link2.vertices([
                new g.Point(250, 100),
                new g.Point(300, 150),
                new g.Point(350, 200)
            ]);
            link2.router('orthogonal');
            link2.connector('rounded');
            link2.attr({
                line: {
                    stroke: 'gray',
                    strokeWidth: 4,
                    strokeDasharray: '4 2',
                    sourceMarker: {
                        'type': 'image',
                        'xlink:href': 'http://cdn3.iconfinder.com/data/icons/49handdrawing/24x24/left.png',
                        'width': 24,
                        'height': 24,
                        'y': -12
                    },
                    targetMarker: {
                        'type': 'image',
                        'xlink:href': 'http://cdn3.iconfinder.com/data/icons/49handdrawing/24x24/left.png',
                        'width': 24,
                        'height': 24,
                        'y': -12
                    }
                }
            });
            link2.addTo(graph);

            var link3 = new joint.shapes.standard.Link();
            link3.source(rect3);
            link3.target(rect4);
            link3.connector('jumpover', { size: 10 });
            link3.addTo(graph);

            var rect5 = new joint.shapes.standard.Rectangle();
            rect5.position(100, 230);
            rect5.resize(100, 40);
            rect5.attr({
                body: {
                    fill: '#2ECC71',
                    strokeDasharray: '10,2'
                },
                label: {
                    text: 'Hello',
                    fill: 'black',
                    fontSize: 13
                }
            });
            rect5.addTo(graph);

            var rect6 = new joint.shapes.standard.Rectangle();
            rect6.position(400, 230);
            rect6.resize(100, 40);
            rect6.attr({
                body: {
                    fill: '#F39C12',
                    rx: 20,
                    ry: 20,
                    strokeDasharray: '1,1'
                },
                label: {
                    text: 'World!',
                    fill: 'gray',
                    fontSize: 18,
                    fontWeight: 'bold',
                    fontVariant: 'small-caps',
                    textShadow: '1px 1px 1px black'
                }
            });
            rect6.addTo(graph);

            var link4 = new joint.shapes.standard.Link();
            link4.source(rect5);
            link4.target(rect6);
            link4.attr({
                line: {
                    stroke: '#3498DB',
                    strokeWidth: 3,
                    strokeDasharray: '5 5',
                    strokeDashoffset: 7.5,
                    sourceMarker: {
                        'type': 'path',
                        'stroke': 'none',
                        'fill': '#3498DB',
                        'd': 'M 20 -10 0 0 20 10 Z \
                            M 40 -10 20 0 40 10 Z'
                    },
                    targetMarker: {
                        'type': 'path',
                        'stroke': 'none',
                        'fill': '#3498DB',
                        'd': 'M 7.5 -10 2.5 -10 2.5 10 7.5 10 Z \
                            M 17.5 -10 12.5 -10 12.5 10 17.5 10 Z \
                            M 40 -10 20 0 40 10 Z'
                    }
                }
            });
            link4.addTo(graph);

        </script>
    </body>
    </html>

JointJS source code: links.js

This concludes the introductory section of the JointJS tutorial! Congratulations! We have come a long way from the initial Hello, World! application:

Now that you have seen the basics, you should be confident working with the most important building blocks of JointJS - Graphs, Papers, Elements, and Links.

The next step is to head over to the intermediate section of the tutorial, which explains some handy but more involved concepts.