Skip to content

fix(html): slider pointer interaction and edge thumb alignment broken #690

@sampotts

Description

@sampotts

Bug Description

Two issues with sliders in the HTML package:

1. Click and drag does not work

Mouse/pointer interaction on sliders is completely non-functional. Focusing the thumb and using keyboard arrows works fine, but clicking and dragging does not.

Root cause: The rootProps returned by createSlider() (onPointerDown, onPointerMove, onPointerLeave) are never bound to the slider element's DOM. Only thumbProps are applied — via context to SliderThumbElement using applyElementProps().

Affected elements:

  • SliderElement (packages/html/src/ui/slider/slider-element.ts)
  • TimeSliderElement (packages/html/src/ui/time-slider/time-slider-element.ts)
  • VolumeSliderElement (packages/html/src/ui/volume-slider/volume-slider-element.ts)

2. thumb-alignment="edge" has no effect

The thumb-alignment="edge" attribute doesn't constrain the thumb within track bounds like it does in the React package.

Root cause: The HTML slider elements compute CSS variables directly from raw state percentages without calling SliderCore.adjustPercentForAlignment(). The React useSlider hook correctly adjusts fillPercent and pointerPercent using DOM measurements before computing CSS vars (use-slider.ts lines 101–115), but the HTML elements skip this entirely.

Suggested Fix

Fix 1: Bind rootProps

In each element's connectedCallback(), after createSlider(), add:

applyElementProps(this, this.#slider.rootProps, signal);

Add applyElementProps to each file's @videojs/core/dom import.

Fix 2: Edge alignment adjustment

In each element's update(), after computing state and before computing CSS vars, adjust percents when thumbAlignment === 'edge':

let cssState = state;
if (state.thumbAlignment === 'edge') {
  const thumb = this.querySelector<HTMLElement>('media-slider-thumb');
  if (thumb) {
    const isHorizontal = state.orientation === 'horizontal';
    const thumbSize = isHorizontal ? thumb.offsetWidth : thumb.offsetHeight;
    const trackSize = isHorizontal ? this.offsetWidth : this.offsetHeight;
    cssState = {
      ...state,
      fillPercent: this.#core.adjustPercentForAlignment(state.fillPercent, thumbSize, trackSize),
      pointerPercent: this.#core.adjustPercentForAlignment(state.pointerPercent, thumbSize, trackSize),
    };
  }
}
const cssVars = getSliderCSSVars(cssState); // or getTimeSliderCSSVars for time slider

No ResizeObserver needed — measurements are read fresh in update() on every interaction change, matching the React approach.

Reference

  • React implementation that works correctly: packages/react/src/ui/hooks/use-slider.ts
  • Existing pattern for binding props: PopoverElement.connectedCallback() uses applyElementProps(this, this.#popover.popupProps, signal)
  • SliderCore.adjustPercentForAlignment() already exists at packages/core/src/core/ui/slider/slider-core.ts:148

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Status

Done

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions