Crosstalk is a
standard for widget-to-widget communication that lets users interact
with several visualizations at once — brushing points in one chart dims
matching rows in a companion table, filtering a table hides the
non-matching points on the chart, and so on. myIO participates in this
protocol via setLinked().
Which myIO layers participate
As of v1.2, nine chart-layer types respond to crosstalk selection and filter events:
| Layer type | Selection (target) | Filter (target) | Brush source |
|---|---|---|---|
point |
yes | yes | yes (with setBrush()) |
bar |
yes | yes | yes (with setBrush()) |
groupedBar |
yes | yes | yes (with setBrush()) |
histogram |
yes | yes | yes (with setBrush()) |
hexbin |
yes | yes | yes (with setBrush()) |
waffle |
yes | yes | — |
beeswarm |
yes | yes | yes (with setBrush()) |
lollipop |
yes | yes | — |
dumbbell |
yes | yes | — |
Aggregate layer types (boxplot, violin,
qq, regression, density,
ridgeline, survfit, comparison)
do not participate in v1.2. To drive selection over aggregate views,
layer a linked scatter next to the aggregate chart.
Selection vs filter
-
Selection dims non-matching elements (sets their
CSS opacity to the
--chart-brush-dim-opacityvariable). Other widgets are not affected by their layout. -
Filter hides non-matching elements entirely
(
display: none). Use it when linked partners have a filter UI — e.g. acrosstalk::filter_select()dropdown — that genuinely narrows the dataset.
Pass filter = TRUE to setLinked() to enable
filter-driven hiding.
Source and target modes
setLinked(mode = ...) accepts three values:
-
"source"— this chart emits selections (via its brush) but does not react to incoming ones. -
"target"— this chart reacts to incoming selections but does not emit its own. -
"both"(default) — bidirectional.
Example 1: scatter + DT
library(crosstalk)
library(myIO)
library(DT)
shared <- SharedData$new(mtcars, key = ~rownames(mtcars))
bscols(
widths = c(7, 5),
myIO(data = shared$data()) |>
addIoLayer(
type = "point", label = "cars",
mapping = list(x_var = "wt", y_var = "mpg")
) |>
setBrush(direction = "xy") |>
setLinked(shared),
datatable(shared, rownames = FALSE, options = list(pageLength = 5))
)Brush a rectangle in the chart: the matching rows in the table stay bold and the rest dim. Click rows in the table: the matching points stay solid, the rest dim.
Example 2: lollipop + reactable
library(crosstalk)
library(myIO)
library(reactable)
# Aggregate mtcars by cyl for a simple lollipop
by_cyl <- data.frame(
cyl = c("4", "6", "8"),
avg_mpg = c(
mean(mtcars$mpg[mtcars$cyl == 4]),
mean(mtcars$mpg[mtcars$cyl == 6]),
mean(mtcars$mpg[mtcars$cyl == 8])
)
)
shared <- SharedData$new(by_cyl, key = ~cyl)
bscols(
widths = c(6, 6),
myIO(data = shared$data()) |>
addIoLayer(
type = "lollipop", label = "by-cyl",
mapping = list(x_var = "cyl", y_var = "avg_mpg")
) |>
setLinked(shared),
reactable(shared, selection = "multiple", onClick = "select")
)Example 3: plotly bridge (static listing)
plotly’s own crosstalk integration means you can link a myIO chart
directly to a plotly chart over the same SharedData:
library(crosstalk)
library(myIO)
library(plotly)
shared <- SharedData$new(mtcars, key = ~rownames(mtcars))
bscols(
myIO(data = shared$data()) |>
addIoLayer(type = "point", label = "m",
mapping = list(x_var = "wt", y_var = "mpg")) |>
setBrush(direction = "xy") |>
setLinked(shared),
plot_ly(shared, x = ~hp, y = ~mpg, type = "scatter", mode = "markers")
)Not evaluated in this vignette. Install plotly and run
locally.
Example 4: leaflet map (static listing)
library(crosstalk)
library(myIO)
library(leaflet)
quakes_small <- head(quakes, 50)
shared <- SharedData$new(quakes_small)
bscols(
leaflet(shared) |> addTiles() |> addMarkers(),
myIO(data = shared$data()) |>
addIoLayer(type = "point", label = "mag",
mapping = list(x_var = "depth", y_var = "mag")) |>
setLinked(shared)
)Not evaluated in this vignette. Install leaflet and run
locally.
Troubleshooting
- My boxplot/violin doesn’t respond. Aggregate layer types are not linkable in v1.2. Drive selection through a sibling scatter or bar layer.
-
My chart is in the linked group but brushing doesn’t
emit. Add
setBrush()to the chart layer. Without a brush, the chart can only be a target, not a source. -
Selections don’t visually change anything on the linked
widget. Confirm the
SharedData$new()key vector matches the row identity the other widget expects. Mismatched keys silently fail.
