Logo

Ultra Tiny, Opinionated Positioning Engine

gzip size brotli size Build Status Download count No dependencies JSDelivr download count Current version Support me


NanoPop is an ultra-tiny positioning engine. But **wait** isn't there [PopperJS](https://github.com/popperjs/popper-core)? Yeah - and PopperJS is great! But there are tons of features you might not need in most cases. This library is brotlied only ~ 700 Bytes (PopperJS is around 3kB). #### What are the use-cases compared to PopperJS? 1. Situations where you want **full controll** over positioning, including handling events such as scrolling / resize manually. 2. **Performance-critical** cases with lots of elements [...] nanopop will only makes changes if you say so. 3. Poppers with **minimal footprint** such as drop-downs and tooltips which don't require much configurability. 4. You might have some special needs about how your popper behaves. NanoPop could be used as super-class and you can, based on what's required, extend NanoPop as you will :) This library was originally part of [pickr](https://github.com/Simonwep/pickr) - now ported to TS with tests and a few updates / bug-fixes. ## Getting Started Install via npm: ```shell $ npm install nanopop ``` Install via yarn: ```shell $ yarn add nanopop ``` Include directly via jsdelivr: ```html ``` Using [JavaScript Modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules): ````js import {NanoPop} from 'https://cdn.jsdelivr.net/npm/nanopop/lib/nanopop.min.mjs' ```` ## Usage ```js const reference = document.querySelector('.btn'); const popper = document.querySelector('.dropdown'); const nanopop = new NanoPop(reference, popper); // Updating the popper-position nanopop.update(); ``` > The popper-element must have set `position` to `fixed`. #### All options ```js const nanopop = new NanoPop(reference, popper, { // The DOMRect of the container, this is the default: container: document.documentElement.getBoundingClientRect(), // Margin between the popper element and the reference margin: 8, // Preferred position, any combination of [top|right|bottom|left]-[start|middle|end] is valid. position: 'bottom-start', // Sometimes there's no way to position the popper element without clipping it. // Turn this on if, in case there's no non-clipping position, want to apply the wanted position forcefully. // The .update() function will return false in any case it fails so you can handle this separately. // Attention: If this is set to false and you do not take care about handling the clipped element yourself it'll be positioned on the top-left corner of the container-element (most of the time this is the document element itself). forceApplyOnFailure: false, // In case the variant-part (start, middle or end) cannot be applied you can specify what (and if) // should be tried next. variantFlipOrder: { start: 'sme', // In case of -start try 'start' first, if that fails 'middle' and 'end' if both doesn't work. middle: 'mse', end: 'ems' }, // The same as variantFlipOrder, but if all variants fail you might want to try other positions. positionFlipOrder: { top: 'tbrl', // Try 'top' first, 'bottom' second, 'right' third and 'left' as latest position. right: 'rltb', bottom: 'btrl', left: 'lrbt' } }); ``` ### Functions * `nanopop.update(newOptions?: Partial)` _- Update the position and optionally update the options of this NanoPop instance. It'll return a position-pair (For example `te` for **T**op-**E**nd) or `null` based on if it was possible to find a position for the popper without clipping it._ > Tip: The returned position-pair is perfect for tool-tips to give them a little arrow! ### Properties * `nanopop.version` _- Current version._ These are static default-values used in case you're not specifying something else: * `NanoPop.defaultVariantFlipOrder` _- Default `variantFlipOrder` values._ * `NanoPop.defaultPositionFlipOrder` _- Default `positionFlipOrder`._ ### Caveats 1. The popper-element must have `position` set to `fixed`. 2. ~~`window` is (currently) the only bounding-element supported.~~ 3. The library does not perform any automatic updates if the window gets resized, or the user scrolls, you have to take care of that yourself and call `update()` in the case. 4. You might have to fiddle around with `z-index` to make it work inside of complex, nested, scrollable containers.