Interactive Legend Example
A scatter plot with interactive guides. Shift-click legend entries to select subsets of the data, or drag along the x-axis to create a 1D brush. Click the chart background to clear all selections.
Vega JSON Specification <>
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"description": "A scatter plot example with interactive legend and x-axis.",
"width": 200,
"height": 200,
"padding": 5,
"autosize": "pad",
"signals": [
{
"name": "clear", "value": true,
"on": [
{
"events": "pointerup[!event.item]",
"update": "true",
"force": true
}
]
},
{
"name": "shift", "value": false,
"on": [
{
"events": "@legendSymbol:click, @legendLabel:click",
"update": "event.shiftKey",
"force": true
}
]
},
{
"name": "clicked", "value": null,
"on": [
{
"events": "@legendSymbol:click, @legendLabel:click",
"update": "{value: datum.value}",
"force": true
}
]
},
{
"name": "brush", "value": 0,
"on": [
{
"events": {"signal": "clear"},
"update": "clear ? [0, 0] : brush"
},
{
"events": "@xaxis:pointerdown",
"update": "[x(), x()]"
},
{
"events": "[@xaxis:pointerdown, window:pointerup] > window:pointermove!",
"update": "[brush[0], clamp(x(), 0, width)]"
},
{
"events": {"signal": "delta"},
"update": "clampRange([anchor[0] + delta, anchor[1] + delta], 0, width)"
}
]
},
{
"name": "anchor", "value": null,
"on": [{"events": "@brush:pointerdown", "update": "slice(brush)"}]
},
{
"name": "xdown", "value": 0,
"on": [{"events": "@brush:pointerdown", "update": "x()"}]
},
{
"name": "delta", "value": 0,
"on": [
{
"events": "[@brush:pointerdown, window:pointerup] > window:pointermove!",
"update": "x() - xdown"
}
]
},
{
"name": "domain",
"on": [
{
"events": {"signal": "brush"},
"update": "span(brush) ? invert('x', brush) : null"
}
]
}
],
"data": [
{
"name": "source",
"url": "data/cars.json",
"transform": [
{
"type": "filter",
"expr": "datum['Horsepower'] != null && datum['Miles_per_Gallon'] != null && datum['Origin'] != null"
}
]
},
{
"name": "selected",
"on": [
{"trigger": "clear", "remove": true},
{"trigger": "!shift", "remove": true},
{"trigger": "!shift && clicked", "insert": "clicked"},
{"trigger": "shift && clicked", "toggle": "clicked"}
]
}
],
"scales": [
{
"name": "x",
"type": "linear",
"round": true,
"nice": true,
"zero": true,
"domain": {"data": "source", "field": "Horsepower"},
"range": [0,200]
},
{
"name": "y",
"type": "linear",
"round": true,
"nice": true,
"zero": true,
"domain": {"data": "source", "field": "Miles_per_Gallon"},
"range": [200,0]
},
{
"name": "color",
"type": "ordinal",
"range": {"scheme": "category10"},
"domain": {"data": "source", "field": "Origin"}
}
],
"axes": [
{
"scale": "x",
"grid": true,
"domain": false,
"orient": "bottom",
"tickCount": 5,
"title": "Horsepower"
},
{
"scale": "y",
"grid": true,
"domain": false,
"orient": "left",
"titlePadding": 5,
"title": "Miles_per_Gallon"
}
],
"legends": [
{
"stroke": "color",
"title": "Origin",
"encode": {
"symbols": {
"name": "legendSymbol",
"interactive": true,
"update": {
"fill": {"value": "transparent"},
"strokeWidth": {"value": 2},
"opacity": [
{"test": "!length(data('selected')) || indata('selected', 'value', datum.value)", "value": 0.7},
{"value": 0.15}
],
"size": {"value": 64}
}
},
"labels": {
"name": "legendLabel",
"interactive": true,
"update": {
"opacity": [
{"test": "!length(data('selected')) || indata('selected', 'value', datum.value)", "value": 1},
{"value": 0.25}
]
}
}
}
}
],
"marks": [
{
"type": "rect",
"name": "xaxis",
"interactive": true,
"encode": {
"enter": {
"x": {"value": 0},
"height": {"value": 35},
"fill": {"value": "transparent"},
"cursor": {"value": "ew-resize"}
},
"update": {
"y": {"signal": "height"},
"width": {"signal": "span(range('x'))"}
}
}
},
{
"type": "rect",
"interactive": false,
"encode": {
"enter": {
"y": {"value": 0},
"height": {"signal":"height"},
"fill": {"value": "#ddd"}
},
"update": {
"x": {"signal": "brush[0]"},
"x2": {"signal": "brush[1]"},
"fillOpacity": {"signal": "domain ? 0.2 : 0"}
}
}
},
{
"name": "marks",
"type": "symbol",
"from": {"data": "source"},
"interactive": false,
"encode": {
"update": {
"x": {"scale": "x", "field": "Horsepower"},
"y": {"scale": "y", "field": "Miles_per_Gallon"},
"shape": {"value": "circle"},
"strokeWidth": {"value": 2},
"opacity": [
{"test": "(!domain || inrange(datum.Horsepower, domain)) && (!length(data('selected')) || indata('selected', 'value', datum.Origin))", "value": 0.7 },
{"value": 0.15}
],
"stroke": [
{"test": "(!domain || inrange(datum.Horsepower, domain)) && (!length(data('selected')) || indata('selected', 'value', datum.Origin))", "scale": "color", "field": "Origin"},
{"value": "#ccc"}
],
"fill": {"value": "transparent"}
}
}
},
{
"type": "rect",
"name": "brush",
"encode": {
"enter": {
"y": {"value": 0},
"height": {"signal":"height"},
"fill": {"value": "transparent"}
},
"update": {
"x": {"signal": "brush[0]"},
"x2": {"signal": "brush[1]"}
}
}
},
{
"type": "rect",
"interactive": false,
"encode": {
"enter": {
"y": {"value": 0},
"height": {"signal": "height"},
"width": {"value": 1},
"fill": {"value": "firebrick"}
},
"update": {
"fillOpacity": {"signal": "domain ? 1 : 0"},
"x": {"signal": "brush[0]"}
}
}
},
{
"type": "rect",
"interactive": false,
"encode": {
"enter":{
"y": {"value": 0},
"height": {"signal": "height"},
"width": {"value": 1},
"fill": {"value": "firebrick"}
},
"update": {
"fillOpacity": {"signal": "domain ? 1 : 0"},
"x": {"signal": "brush[1]"}
}
}
}
]
}