Floater - Full Demo

17 built-in types (dropdown, number-picker, text-suggestions, popover, chips, slider, input-group, date, time, datetime, fetch, audio, video, image, persist, modal, context-menu) - create() and attach() modes - markers - events - animations - dynamic update/destroy - custom types - viewport edge cases - hover/programmatic triggers - scroll behaviour - fitContent - blockScroll / blockClicks - press trigger - scrollSelect - getAll() / getOpen() - open-time opts - keyboard triggers - Escape stacking - attach/destroy unwrap - warnings & errors in the log

Arrow ▶ only rotates when arrowEl is wired - demos that have it wired are marked with ↕

1. Dropdown

Basic ↕ (arrow wired)
—
Markers (current + preferred) ↕ - select to move current
—
Combined marker (current = preferred on same item)
—
Pre-selected item (3rd in list)
—
20 items + minWidth:200
—
Dynamic item update - markers reapplied after rebuild
—
centerX: true - narrow btn, wide content
← narrow anchor
—
Hover to open (mouseenter/leave)
—

2. Number Picker

Delay presets - field + markers
—
Volume 0-100
—
Custom currentTitle / preferenceTitle
—
centerX: true
—

3. Text Suggestions

String array - filtered on input
—
value/label objects
—
Open on button (not focus)
—
fitContent: true - width = content, not anchor
—

4. Popover

Rich HTML content
Long / scrollable + closeButton: true
Live content update
centerX: true
gap: 0 / 16 / 32

5. Chips

Unlimited multi-select
—
maxSelect: 2 (no auto-close)
—
preferenceValues (dotted border) + closeOnMax: true
—
Pre-selected + update()
—

6. Slider

Single - opacity
—
Multi-slider - RGB
—
Discrete steps 1-5
—
update() slider value
—

7. Input Group

text + number + checkbox
—
select + textarea + text
—
Nested FS sub-types (dropdown with currentValue/preferenceValue + chips)
—
update() field values
—

8. Date / Time / Datetime

date
—
time
—
datetime-local
—
date with min/max (±30 days)
—
time step: 900 (15 min)
—
update() prefill value
—
showArrows: false - value still editable, picker visible
—
allowTyping: false - arrows + picker, no keyboard entry
—
showPicker: false - arrows + typing, no calendar icon
—
showArrows: false + showPicker: false + allowTyping: false - display only
—

9. attach() - existing DOM elements

Existing .fs-dropdown div — type: 'dropdown' passed so type init runs automatically
—
Existing .fs-number-picker div — type: 'number-picker' auto-initialises
—
Arbitrary custom markup

10. Advanced options

closeOthers: false - all can be open
gap: 0 / 12 / 28
minWidth: 320
onChange: false (silent)
events suppressed
bind() multi-anchor - same floater, multiple triggers
—
trigger: 'contextmenu' - right-click zone
Right-click here
—
trigger: 'click' from the keyboard - Tab to the button, then Enter or Space
—
trigger: 'focus' - input focus opens floater
—
Open-time opts - one instance, open() decides plain vs modal
—

11. Custom types - registerType()

Color palette picker
#5588ff
Star rating
—
Icon / emoji picker
—
Confirm dialog (yes/no)
—
context-menu built-in - global: true

Right-click anywhere (except inputs/selects) to open, including over other floaters.
global:true wires contextmenu automatically.
Does not close when other floaters open, does not close others when it opens.
Last selection shown bottom-right.

—
Progress / status indicator
—

12. Dynamic operations

destroy() + recreate
exists: yes
closeAll() / closeAll(filter)
open some first:
update() - live marker change
—
has() / get() API - reads mark-upd state
—
attach() lifecycle - close puts the element back visible, destroy() unwraps it into the page
I live inline in this card
not attached

12b. Programmatic / remote triggers - no direct interaction needed

Open after 1.5s delay (timer)
—
floater anchors here
Remote: click button A → opens anchored to button B
→
—
Chain: picking A fires B on a different anchor
Step 2 opens here
—
Open from input focus (text-suggestions on a label click)
—
Programmatic open/close via API buttons - persist only
floater anchor
Console: _trigProg.open()

13. Viewport edge positions - floater flips above/below as needed

Six fixed buttons are pinned to the viewport edges.

14. preferRight / preferLeft / centerY

preferRight - opens right, flips left if needed
—
preferLeft - opens left, flips right if needed
—
preferRight + centerY - vertically centred on anchor
—

15. persist type / persistOnOutsideClick - stays open until explicitly closed

persistOnOutsideClick: true - click outside does nothing
—
Floater within floater - child keeps parent open
—
Floater within floater - child selection closes both
—

16. Modal - backdrop blocks page; Esc closes the topmost non-protected modal

modal: true + protected: true - backdrop click and Esc do nothing (protected)
blockScroll: true - locks page scroll; backdrop click or Esc closes
blockClicks: true - backdrop absorbs clicks; Esc blocked too - own UI only

17. Fetch / Audio / Video types

fetch - JSON with transform + closeButton: true
—
fetch - image URL (auto-detected from extension)
—
fetch - XHR mode (fetchMethod: 'xhr')
—
audio - track swap
No Track Selected
audio - rememberProgress (close + reopen restores position)
—
video - resetOnOpen (always restarts)
video - rememberProgress (close + reopen continues)

18. setDefaults() - global and type-level defaults

global gap: 10 applied to all instances created after setDefaults()
type default — setDefaults('dropdown', { minWidth: 200 })
instance opt overrides type default — minWidth: 80

19. Image floater

Basic image + caption
update() - swap src + caption
closeButton: true

20. toFrontOn - z-index stacking

All four open on their own anchor with closeOthers: false so they overlap. Each has a different size. Click/hover inside each to test bring-to-front behaviour. Pressing Esc closes the topmost (highest z-index) first, not the oldest.

4 close anchors, 1 callsite - floaters overlap for z-stack test
click
hover
none
attached

21. Scroll behaviour

Default (closeOnScroll: false): stays open and repositions to follow the anchor as the page scrolls. Use closeOnScroll: true to close on scroll. persistOnOutsideClick: true → scroll-immune but static (fixed position, does not follow anchor).

closeOnScroll: true - closes on scroll
—
closeOnScroll: false (default) - follows anchor
—
persistOnOutsideClick: true - also scroll-immune
—
modal: true - scroll-immune + backdrop (click backdrop to close)
—
detached anchor - the floater follows the scroll deltas on both axes
—

22. Press trigger & scroll select

trigger: 'press' opens on mousedown and selects by drag. scrollSelect: true lets you scroll through options without opening the floater.

trigger: 'press' - hold and drag to select
—
scrollSelect: true - scroll wheel on button (no open)
—
number-picker scrollSelect preset mode - scroll cycles 5/10/25/50/100
10
number-picker scrollSelect step mode - scroll ±5; click to open
50
date scrollSelect - scroll input to change day; open picker and scroll cols to change per-col
—
time scrollSelect - scroll input to change minute; open picker and scroll cols to change per-col
—
datetime scrollSelect - scroll input to change minute; open picker and scroll cols to change per-col
—
date scrollPanel: true - open picker and scroll anywhere on it to change day; cols also scroll per-col
—
time scrollCols: false - open picker; cols do not respond to scroll; input still scrolls minute
—
datetime scrollPanel: true + scrollCols: true - scroll input, panel, or individual cols
—
datetime step: 15 - seconds column appears; each col scrolls its own unit incl. seconds
—

23. Animations

animateIn / animateOut presets, a custom registerAnimation(), and switching a floater's animation on demand via update().

animateIn/Out: 'fade'
Safe default. Nothing moves.
animateIn/Out: 'scale'
Feels like it came out of the button. Menus, pickers.
animateIn/Out: 'spring'
Playful and heavy. Save it for rare floaters.
animateIn/Out: 'flip'
Loud. Cards and reveals, not menus.
animateIn/Out: 'unfold'
Hinged to whatever sits above it. Nav drawers.
Floater.registerAnimation('swing', {...})
Two CSS classes the page owns.
Fixed direction: 'slide-down' / 'slide-up'
One direction, whatever side it lands on. Forced above, it slides into the anchor.
Anchored: 'shift-away'
Direction follows the side it lands on. Always pulls away, returns on close.
Anchored: 'shift-toward'
Mirror of shift-away: settles in toward the anchor.
instance.update({ animateIn, animateOut, animateInDuration, animateOutDuration }) - switch a floater's in/out animation and per-direction durations on demand, no recreate
-

24. Event hooks

Floater.on() observing every instance globally, custom events via emit(), and querying open floaters.

Floater.on() - global counts across every basic event, every floater on this page (since page load)
-
instance.emit('ping') - instance.on() only sees the instance it's attached to; Floater.on() sees every instance
-
Floater.getOpen(filter) - query open floaters by type / id prefix
-

25. Warnings & errors - misuse is logged, not silent

API misuse and crashed handlers go to the browser console with a [Floater] prefix, and to Floater.logger() as warn/error levels - both appear in the log below.

Unknown animation preset - warns once, opens without animation
—
bind() misuse - empty selector / unknown trigger
—
Throwing event handler - logged, later handlers still run
—
fetch with no url - shows "No URL set" instead of loading forever
—

26. Colour picker - color type

Defaults - no opts at all
—
Bound to a text input - reads it on open, writes back, follows typing; anchorVar drives the thumbnail
—
Everything on - swatches, recent, formatToggle, eyedropper (Chromium), Clear / Done
—
swatchesOnly + press trigger - hold, drag over a swatch, release
—
format: 'hsl', alpha: false, changeOn + inputOn: 'close' - silent while open, one input and one change per visit; Esc reverts
—
scrollSelect - wheel over the button, open or closed; auto follows the control under the pointer; Shift for big steps; hue 10, the rest 5
—

27. Events - all instances, plus warnings & errors