Skip to main content

Highlighters

Highlighters can be used to provide visual emphasis to a shape, for example, during user interactions or on some event trigger. You can apply highlighter to the whole shape or to some part of it using markup selectors.

There are several built-in highlighters which you can find in highlighters namespace.

Here is the list of all highlighters provided by JointJS library:

  • addClass - Toggles a class name on an arbitrary cell view's SVG node.
  • list - Adds a list of arbitrary SVGElements to the cell view.
  • opacity - Changes the opacity of an arbitrary cell view's SVG node.
  • stroke - Adds a stroke around an arbitrary cell view's SVG node. Works in a different way than mask highlighter.
  • mask - Adds a stroke around an arbitrary cell view's SVG node. Works in a different way than stroke highlighter.

Here is an example of a highlighter in action. You can click an element, link, label or a port to highlight it. Click a blank area of the paper to unhighlight all shapes.

Usage

Built-in highlighters are provided as static classes in the highlighters namespace. All highlighters inherit from dia.HighlighterView class, which provides basic utilities for working with highlighters.

Generally, it's possible to highlight a shape (Element or Link) or a part of it calling the add() method of a highlighter.

import { highlighters } from '@joint/core';

// Add Mask Highlighter with ID `my-mask-highlighter` to the CellView.
// Note: `root` is a shortcut for `{ selector: 'root' }`
highlighters.mask.add(cellView, 'root', 'my-mask-highlighter', {
deep: true
});

// Add class name `my-highlight` to the CellView's body node.
highlighters.addClass.add(cellView, 'body', 'my-class-highlighter', {
className: 'my-highlight'
});

// Add Stroke Highlighter to a specific port node (using default options).
highlighters.stroke.add(cellView, { port: 'port-id-1', selector: 'portBody' }, 'my-port-highlighter');

To unhighlight a cell, call the remove() method.

import { dia, highlighters } from '@joint/core';

// Remove all highlighters from the CellView
dia.HighlighterView.remove(cellView);

// Remove the highlighter with ID `my-highlighter` from the CellView
dia.HighlighterView.remove(cellView, 'my-highlighter');

// Remove all Mask highlighters from the cellView
highlighters.mask.remove(cellView);

// Remove Stroke Highlighter with ID `my-highlighter` from the cellView.
highlighters.stroke.remove(cellView, 'my-highlighter');

// If you have a reference to a highlighter, calling its prototype `remove()` method is also valid.
const highlighter = dia.HighlighterView.get(cellView, 'my-highlighter');
highlighter.remove();

To see if a shape has a highlighter, call the get() method.

import { dia, highlighters } from '@joint/core';

// Get all the highlighters (an array) from the CellView
dia.HighlighterView.get(cellView);

// Get the highlighter with ID `my-highlighter` from the CellView
dia.HighlighterView.get(cellView, 'my-highlighter');

// Get all Mask highlighters from the cellView
highlighters.mask.get(cellView);

// Get Stroke Highlighter with ID `my-highlighter` from the cellView.
// If there is no such highlighter (ID or Type does not match, `null` is returned).
highlighters.stroke.get(cellView, 'my-highlighter');
note

When adding a highlighter, prefer using a node selector over a reference to an actual node.

// Prefer this:
highlighters.mask.add(elementView, { port: 'port1' }, 'port-highlight');
// Over this:
highlighters.mask.add(elementView, elementView.findPortNode('port1'), 'port-highlight');
  • Using the node selector supports asynchronous rendering. No need to wait for an element to be rendered, when adding the highlighter.
  • Using the node reference highlights the specific node only. If the view is redrawn, the node could have been replaced with another node which wouldn't be highlighted.

Toggle a CSS class for a shape

Using addClass highlighter you can toggle class name to any shape node.

import { highlighters } from 'joint/core';

highlighters.addClass.add(cellView, 'root', 'my-highlighter-id', {
className: 'some-custom-class'
});

Add arbitrary SVG elements

To add list of arbitrary SVG elements to your shape you can use list highlighter. To use list highlighter you need to extend base list class and override createListItem method. To find description of available options you can look at API documentation.

import { highlighters } from '@joint/list';

class StatusList extends highlighters.list {

// list of colored ellipses
createListItem(color: string, { width: number, height: number }): SVGElement {
const { node } = V('ellipse', {
'rx': width / 2,
'ry': height / 2,
'cx': width / 2,
'cy': height / 2,
'fill': color,
'stroke': '#333',
'stroke-width': 2,
});
return node;
}
}

Here is an example of a list highlighter in action.

Add an outline using mask

mask highlighter adds a stroke around an arbitrary cell view's SVG node.

note

It's an alternative to the stroke highlighter.

Pros:

  • more precise - equidistant padding along the node
  • more powerful - ability to draw a stroke around the descendant nodes or draw a stroke around a path with no fill

Cons:

  • lesser performance - uses SVG masking to find the outline
  • no fill - the color inside the mask must stay transparent.
  • no dasharray - the stroke patterns of dashes and gaps are not applicable.

To find description of available options you can look at API documentation.

highlighters.mask.add(cellView, 'body', 'my-highlighter-id', {
padding: 5,
attrs: {
'stroke-width': 3,
'stroke': '#FF0000',
// round the mask at the corners of paths
'stroke-linejoin': 'round',
// round the mask at the end of open subpaths
'stroke-linecap': 'round'
// to change the opacity use `rgba` color format
// 'stroke': 'rgba(255, 0, 0, 0.5)',
// this would affect the opacity of the stroke and the padding
// 'stroke-opacity': 0.5
}
});

Add an outline using stroke

stroke highlighter adds a stroke around an arbitrary cell view's SVG node.

To find description of available options you can look at API documentation.

highlighters.stroke.add(cellView, 'body', 'my-highlighter-id', {
padding: 10,
rx: 5,
ry: 5,
useFirstSubpath: true,
attrs: {
'stroke-width': 3,
'stroke': '#FF0000'
}
});

Change opacity

opacity highlighter changes the opacity of an arbitrary cell view's SVG node.

highlighters.opacity.add(cellView, 'body', 'my-highlighter-id', {
alphaValue: 0.4
});

Using events for highlighting

To highlight and unhighlight shapes you can use specific JointJS events.

For example, you can listen to the paper for the element:pointerclick event and highlight the entire element when we click on it.

paper.on('element:pointerclick', (elementView) => {
highlighters.mask.add(elementView, { selector: 'root' }, 'my-element-highlight', {
deep: true,
attrs: {
'stroke': '#FF4365',
'stroke-width': 3
}
});
});

Also, you can listen to the paper for the element:magnet:pointerclick event and highlight the port when it is clicked on.

paper.on('element:magnet:pointerclick', (elementView, magnet, evt) => {
// Prevent highlighting the body of the element
evt.stopPropagation();
// Find the port ID of the selected magnet
const port = cellView.findAttribute('port', magnet);
highlighters.mask.add(elementView, { port }, 'my-port-highlight', {
attrs: {
'stroke': '#FF4365',
'stroke-width': 3
}
});
});

Then it is also possible to listen for the link:pointerclick event and highlight the line node of the link when it is clicked on.

paper.on('link:pointerclick', (linkView) => {
highlighters.mask.add(linkView, { selector: 'line' }, 'my-link-highlight', {
// Draw the highlighter under the LinkView
layer: 'back',
attrs: {
'stroke': '#FF4365',
'stroke-width': 3,
'stroke-linecap': 'square'
}
});
});

If a node (determined by the node selector) stops to exist (e.g. a port is removed) the cell:highlight:invalid event is triggered on the paper.

// If we don't want the highlighter wait for the port to reappear, we can remove it when the event occurs.
paper.on('cell:highlight:invalid', (cellView, id) => {
dia.HighlighterView.remove(cellView, id);
})

Custom highlighters

New highlighters can be defined by extending the dia.HighlighterView base class.

// A simple highlighter, which draws a rectangle over the entire CellView
const MyHighlighter = dia.HighlighterView.extend({

tagName: 'rect',

attributes: {
'stroke': 'blue',
'fill': 'blue',
'fill-opacity': 0.1,
'pointer-events': 'none'
},

options: {
padding: 5
},

// Method called to highlight a CellView
highlight(cellView, _node) {
const { padding } = this.options;
const bbox = cellView.model.getBBox();
// Highlighter is always rendered relatively to the CellView origin
bbox.x = bbox.y = 0;
// Increase the size of the highlighter
bbox.inflate(padding);
this.vel.attr(bbox.toJSON());
},

// Method called to unhighlight a CellView
unhighlight(_cellView, _node) {
// Cleaning required when the highlighter adds
// attributes/nodes to the CellView or Paper.
// This highlighter only renders a rectangle.
}

});

MyHighlighter.add(el1.findView(paper), 'root', 'my-custom-highlighter', {
layer: 'front', // "layer" is an option inherited from the base class
padding: 10
});