Documentation

where, highlight and focus

Three selection words with three separate meanings. Membership, emphasis and camera.

VisDelta keeps three selection operations strictly apart. They accept the same selector but answer different questions:

base.where({ region: 'North' });     // remove other rows
base.highlight({ region: 'North' }); // keep rows; change emphasis
base.focus({ region: 'North' });     // keep rows; move the camera
MethodChangesState-change categoryAffects rows()
.where()Data membershipDataYes
.highlight()Emphasis of matching marksAttentionNo
.focus()The view cameraAttentionNo

where(selector)

Matching rows stay and non-matching rows exit. where() adds a filter transform, so state.rows() also returns only the matching rows.

Line fits its inferred x/y domains to the visible rows. Colors and legend mappings keep their full data lineage, and missing interior observations still leave gaps. Supply explicit position domains when a story needs fixed axes; repeated panels with shared scales retain a common domain.

Preparing transition
0.00
bar-filter.ts
import { bar } from 'visdelta/bar';
import { transition } from 'visdelta/transition';
import 'visdelta/style.css';

const cities = [
  { city: 'Oslo', visitors: 31 },
  { city: 'Lima', visitors: 44 },
  { city: 'Quito', visitors: 18 },
  { city: 'Seoul', visitors: 52 },
  { city: 'Accra', visitors: 23 },
  { city: 'Porto', visitors: 37 },
];

const from = bar(cities)
  .x('city', { label: 'City' })
  .y('visitors', { label: 'Visitors (thousands)' });

const to = from.where({ visitors: { gte: 30 } });

const change = await transition(from, to, { target: '#chart' });
change.play({ duration: 800 }); // or change.progress(0.5)

highlight(selector, options?)

highlight() changes attention without changing rows, identity, layout, axes or scale domains. Matching marks keep full opacity. Non-matching marks use the chart style's --vd-dim-opacity, or the opacity you pass:

base.highlight({ country: 'Norway' });
base.highlight({ sites: { gte: 10 } }, { opacity: 0.08 });
Preparing transition
0.00

focus(selector)

focus() changes only the camera. Core finds the rendered bounds of the matching marks, then applies one aspect-preserving two-dimensional pan and zoom. Every row and mark identity remains. The visible scales and axes follow the camera; the underlying data domain is not recomputed.

When nothing matches, the camera stays at the complete-chart view. Focus never zooms out farther than that view.

Preparing transition
0.00

Selectors

A selector is always an object, never a string. Specs stay plain data that can be saved, sent and compared without a parser.

Field shorthand

Use a scalar for equality, an array for membership or a constraint object for comparisons. Several fields in one object are joined with AND.

{ region: 'North' }                    // region equal "North"
{ region: ['North', 'South'] }         // region oneOf [...]
{ age: { gte: 18, lt: 65 } }           // 18 <= age < 65
{ region: 'North', age: { gt: 80 } }   // region AND age
{ cancelledAt: null }                  // equality with null

Explicit comparison

{ field: 'sales', equal: 100 }
{ field: 'region', oneOf: ['North', 'South'] }
{ field: 'sales', gte: 10, lt: 100 }
{ field: 'date', gte: '2026-01-01', lt: '2027-01-01' }

The supported operators are exactly equal, notEqual, oneOf, gt, gte, lt and lte. The operator is equal, not equals.

Range operands must be finite numbers, Date values or valid ISO date strings. Missing values, empty strings, booleans and NaN never match a range.

Composition

All conditions in one selector, and all repeated calls to the same method, are joined with AND. Each call narrows the previous state; it does not replace it.

const olderNorth = base
  .where({ region: ['North', 'South'] })
  .where({ region: 'North' })
  .where({ age: { gt: 80 } });

The three scopes are independent and can coexist:

const next = base
  .where({ year: 2022 })                             // membership
  .highlight({ country: 'Sweden' }, { opacity: 0.1 }) // emphasis
  .focus({ country: ['Denmark', 'Sweden'] });        // camera

Because a second .focus() narrows the first, state.focus(north).focus(south) means their intersection. To replace a condition, start from an earlier state or call .reset(), which returns the declaration before the chain's first semantic operation:

const south = state.reset().focus({ region: 'South' });

On this page