Skip to content

Group

A Group is a container for organizing elements into a hierarchy — much like a <div> in HTML. Groups support property inheritance, child management, and powerful querying capabilities inspired by the DOM.

Groups are the backbone of Ripl's scene graph. By nesting elements inside groups, you can apply shared styles (which cascade to children like CSS), manage collections of elements as a unit, and use CSS-like selectors to find elements deep in the tree. Groups themselves don't draw anything — they orchestrate their children.

Demo

Creating a Group

ts
import {
    createCircle,
    createGroup,
    createRect,
} from '@ripl/web';

const circle = createCircle({
    cx: 100,
    cy: 100,
    radius: 40,
});

const rect = createRect({
    x: 160,
    y: 60,
    width: 80,
    height: 80,
});

const group = createGroup({
    fill: '#3a86ff',
    children: [circle, rect],
});

Property Inheritance

Style properties set on a group are inherited by all child elements that don't explicitly set those properties. This works just like CSS inheritance:

ts
const group = createGroup({
    fill: '#3a86ff', // inherited by all children
    lineWidth: 2, // inherited by all children
    children: [
        createCircle({
            cx: 80,
            cy: 100,
            radius: 40,
        }), // uses group's fill
        createCircle({
            cx: 200,
            cy: 100,
            radius: 40,
            fill: '#ff006e',
        }), // overrides fill
    ],
});

Child Management

add(element)

Add one or more elements to the group:

ts
group.add(newCircle);
group.add([circle1, circle2]);

If an element already has a parent, it is automatically removed from its previous parent before being added.

remove(element)

Remove one or more elements:

ts
group.remove(circle);
group.remove([circle1, circle2]);

clear()

Remove all children:

ts
group.clear();

set(elements)

Replace all children with a new set:

ts
group.set([newCircle, newRect]);

children

Get an array of direct child elements:

ts
const kids = group.children; // Element[]

graph(includeGroups?)

Flatten the entire element tree into a single array. This recursively collects all non-group elements. Pass true to include group elements in the result:

ts
const allElements = group.graph(); // only leaf elements
const everything = group.graph(true); // includes groups too

Querying Elements

Groups provide several methods for finding elements, inspired by the DOM API.

getElementById(id)

ts
const circle = group.getElementById('my-circle');

getElementsByType(type)

ts
const circles = group.getElementsByType('circle');
const rectsAndLines = group.getElementsByType(['rect', 'line']);

getElementsByClass(classes)

ts
const highlighted = group.getElementsByClass('highlighted');
const both = group.getElementsByClass(['active', 'visible']);

query(selector) / queryAll(selector)

CSS-like selector querying. Returns the first match or all matches:

ts
const el = group.query('circle.highlighted');
const all = group.queryAll('.shape');

Supported Selector Syntax

css
/* Type */
circle

/* ID */
#my-element

/* Class */
.highlighted

/* Attribute */
circle[radius="50"]

/* Descendants */
.parent circle
.parent > circle

/* Sibling */
rect + circle

NOTE

Type and id matching is case-sensitive#Item and rect will not match an element with id item or type Rect. Extra whitespace in a selector is collapsed, so group circle and group circle are equivalent.

matches(selector)

Tests whether an element matches a CSS-like selector, returning a boolean. matches is a method on every element (it comes from Element, so leaf shapes have it too — not just groups). Multi-segment selectors (with combinators) are evaluated relative to the element's root, so ancestor and sibling constraints are honoured:

ts
group.matches('group'); // true
group.matches('#chart.active'); // matches id + class
circle.matches('circle.highlighted'); // works on any element

A standalone matches(element, selector) function is also exported:

ts
import {
    matches,
} from '@ripl/core';

matches(circle, 'circle.highlighted');

closest(selector)

Walks up the parent chain (starting with the element itself) and returns the nearest ancestor that matches the selector, or undefined if none do — the inverse of query. Like matches, closest is available on every element:

ts
const chart = marker.closest('#chart'); // nearest ancestor group with id "chart"
group.closest('.panel');

A standalone closest(element, selector) function is exported as well.

Nesting Groups

Groups can contain other groups, creating a tree structure:

ts
const innerGroup = createGroup({
    fill: '#3a86ff',
    children: [circle1, circle2],
});

const outerGroup = createGroup({
    lineWidth: 2,
    children: [innerGroup, rect],
});

Child elements inherit properties through the full chain — circle1 inherits lineWidth from outerGroup and fill from innerGroup.

Rendering

Call render(context) to draw all children in order:

ts
group.render(context);

Groups themselves are abstract — they don't draw anything directly. They simply iterate over their children and render each one.

NOTE

For the full list of Group properties and methods, see the Group API Reference.