https://github.com/codemonster-ru/floater.js
Open source js library to position floating elements
https://github.com/codemonster-ru/floater.js
dom dropdown floating-ui popover positioning tooltip ui
Last synced: about 2 months ago
JSON representation
Open source js library to position floating elements
- Host: GitHub
- URL: https://github.com/codemonster-ru/floater.js
- Owner: codemonster-ru
- License: mit
- Created: 2024-12-27T16:35:43.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2026-01-27T17:47:35.000Z (5 months ago)
- Last Synced: 2026-01-28T04:16:16.051Z (5 months ago)
- Topics: dom, dropdown, floating-ui, popover, positioning, tooltip, ui
- Language: TypeScript
- Homepage:
- Size: 283 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.md
Awesome Lists containing this project
README
# Floater.js




A tiny JS/TS library for positioning floating UI elements (tooltips, popovers, menus).
## Install
```bash
npm i @codemonster-ru/floater.js
```
## Usage
```ts
import { computePosition, offset, shift, flip, arrow, autoUpdate } from '@codemonster-ru/floater.js';
const reference = document.querySelector('#reference') as HTMLElement;
const floating = document.querySelector('#floating') as HTMLElement;
const arrowEl = document.querySelector('#arrow') as HTMLElement;
const update = () => {
computePosition(reference, floating, {
placement: 'right',
middleware: [shift(), offset(8), arrow(arrowEl)],
}).then(({ x, y, middlewareData }) => {
floating.style.left = `${x}px`;
floating.style.top = `${y}px`;
if (middlewareData.arrow) {
arrowEl.style.left = `${middlewareData.arrow.x}px`;
arrowEl.style.top = `${middlewareData.arrow.y}px`;
}
});
};
const cleanup = autoUpdate(reference, update);
update();
// Later: cleanup();
```
## API
### computePosition(reference, floating, options?)
Returns a Promise that resolves to `{ x, y, placement, middlewareData }`.
- `reference`: `HTMLElement | VirtualElement`
- `floating`: `HTMLElement`
- `options.placement`: one of `placementTypes` (default: `bottom`)
- `options.middleware`: array of middleware
- `options.strategy`: `'absolute' | 'fixed'` (default: `'absolute'`)
#### Stability contract
- With `strategy: 'absolute'`, `x` and `y` are coordinates in the floating element's offset parent coordinate space (suitable for `style.left/top`).
- With `strategy: 'fixed'`, `x` and `y` are viewport coordinates (suitable for `position: fixed` elements teleported to `body`).
- Middleware runs in the order provided and can change `x`, `y`, and `placement`.
- `middlewareData[name]` stores the final result returned by that middleware.
- Reserved middleware names are `flip`, `offset`, `shift`, `arrow` (custom middleware with these names is ignored with a warning).
- When `arrow(...)` is used, `middlewareData.arrow` includes:
- `x` / `y`: arrow coordinates relative to the floating element
- `baseX` / `baseY`: floating coordinates used for arrow calculation
#### Fixed strategy example (teleport to body)
```ts
floating.style.position = 'fixed';
document.body.appendChild(floating);
// With strategy: 'fixed', flip() checks space in viewport coordinates
// and picks the first fitting placement (or the side with the most space).
computePosition(reference, floating, {
placement: 'bottom',
strategy: 'fixed',
middleware: [offset(8), flip({ placements: ['bottom', 'top'] }), shift()],
}).then(({ x, y }) => {
floating.style.left = `${x}px`;
floating.style.top = `${y}px`;
});
```
### placementTypes
Array of supported placements:
```
top, top-start, top-end,
right, right-start, right-end,
bottom, bottom-start, bottom-end,
left, left-start, left-end
```
### offset(value)
Offsets the floating element from the reference by `value` pixels.
### shift(params?)
Keeps the floating element inside the visible area.
- `params.parent`: optional container element. If provided, clamping uses that container; otherwise it uses the scroll parent.
### flip(params?)
If the placement is not visible, tries other placements.
When used together with `shift()`, the fit check ignores `shift()` to avoid picking placements that only fit after shifting.
When used together with `offset()`, put `offset()` before `flip()` so the fit check includes the offset.
- `params.placements`: optional list of placements to try, in order. Useful to restrict flipping (e.g. only `top`/`bottom`).
Example: restrict flipping to vertical directions only.
```ts
computePosition(reference, floating, {
placement: 'bottom',
middleware: [offset(8), flip({ placements: ['bottom', 'top'] }), shift()],
});
```
### arrow(arrowEl)
Computes arrow position and exposes it through `middlewareData.arrow`.
- `middlewareData.arrow.x/y`: arrow coordinates relative to the floating element
- `middlewareData.arrow.baseX/baseY`: floating coordinates used for arrow calculation
### autoUpdate(reference, callback, floatingOrOptions?, options?)
Watches scroll/resize and calls `callback`.
Returns a cleanup function to remove listeners.
#### Stability contract
- By default (`animationFrame` is not enabled), listeners are attached to:
- all scrollable ancestors of `reference` (and optional `floating`)
- `window` scroll
- `window` resize
- `ResizeObserver` for `reference` and optional `floating` when available
- Optional `animationFrame: true` switches to a per-frame update loop (useful for transform/layout-shift driven movement) and disables the event/observer watchers above.
- Optional `maxFps` limits update frequency in animation-frame mode (for example, `maxFps: 30`). When omitted, animation-frame mode defaults to `30 FPS`.
- In animation-frame mode, updates pause on hidden tabs and resume when the tab becomes visible.
- The returned cleanup function removes all listeners/observers added by `autoUpdate`.
### VirtualElement
Use when you need a virtual reference (e.g. mouse position).
```ts
const virtualEl: VirtualElement = {
offsetTop: 100,
offsetLeft: 200,
getBoundingClientRect() {
return {
x: 200,
y: 100,
width: 0,
height: 0,
top: 100,
right: 200,
bottom: 100,
left: 200,
};
},
};
```
## TypeScript
The package ships with types generated at build time via `tsc`. See `index.ts` for full exports.
## Migration notes (0.x -> 1.0)
- `autoUpdate(...)` now returns a cleanup function. Call it when the floating UI unmounts.
- `middlewareData.arrow` now keeps arrow coordinates (`x/y`) and also exposes `baseX/baseY`.
- Positioning and clamping behavior was hardened for scroll containers, viewport checks, and `shift + offset` interactions.
## License
[MIT](https://github.com/codemonster-ru/floater.js/blob/main/LICENSE)
## Author
[@KolesnikovKirill](https://github.com/kolesnikovKirill)