tippyjs

Tooltip, popover, dropdown, and menu library

MIT License

Downloads
8.9M
Stars
11.6K
Committers
36

Bot releases are hidden (Show)

tippyjs - v4.0.2

Published by atomiks over 5 years ago

Fixes

  • Fix UC Browser support: animateFill being broken, and onShown / onHidden not being called (#427)
  • Fix distance option when setting it as a default (#429)
  • Fix edge case error if no nodes are in <head> when injecting CSS
tippyjs - v4.0.1

Published by atomiks over 5 years ago

Fixes

  • Fix content as a function (#424)
tippyjs - v4.0.0

Published by atomiks over 5 years ago

Install

npm i tippy.js@latest

Overall minzipped size has been reduced by about 400 bytes

Features / Improvements

  • followCursor: 'initial' works on touch devices too

  • scale animation has its transform origin sticking to the reference element

  • tippy.hideAll() method is now actually useful!

It no longer checks hideOnClick unexpectedly. In other words:

tippy.hideAll()

Does exactly what it says. All tippys will be hidden no matter what (using their own specified durations).

There's even options now too, for example, make all of them hide instantly (useful for scrolling):

tippy.hideAll({ duration: 0 })

Or exclude a particular instance from hiding:

tippy.hideAll({ exclude: tippyInstance })

  • tippy.group() method - finally, a built-in solution to #81!

How to use it:

const instances = tippy('button', { delay: 1000 })
tippy.group(instances)

// Optional:
tippy.group(instances, {
  duration: number | [number, number], // instead of 0 between each show,
  delay: number | [number, number], // if you didn't specify a delay in the instances
})

As simple as that, the tooltips will now instantly show when hovering between items in the group instead of waiting for the delay sluggishly.


  • updateDuration option is now 0 instead of 200 by default

Flips don't have the weird transition if using flipOnUpdate: true (note that it's off by default now). Specifying a number now uses true smooth flips, where the popper element smoothly transitions from one placement to another. The effect is quite satisfying.


  • role option - instead of tooltip, certain popovers may need to be menu or dialog for accessibility reasons.
tippy('button', {
  role: 'menu'
})

Breaking changes

  • 🔴 You no longer need to import CSS when using modules. It gets injected to the very top of the <head>, like the UMD version.
// v3
import tippy from 'tippy.js'
import 'tippy.js/dist/tippy.css'

// v4
import tippy from 'tippy.js'

Also, files renamed to index instead of tippy.

In case you don't want this, you still can import it separately:

import tippy from 'tippy.js/esm'
import 'tippy.js/index.css'

Additionally, everything exists in the root directory instead of under dist/. For example, themes:

import 'tippy.js/themes/light.css'

Why: cleaner import experience for consumers


  • 🔴 popper.js is external in all formats, so no more tippy.standalone. This means CDN includes must reference it separately.
<script src="https://unpkg.com/popper.js@1/dist/umd/popper.min.js"></script>
<script src="https://unpkg.com/tippy.js@4"></script>
<script>
  tippy('button')
</script>

Why: to prevent popper.js from becoming outdated and lagging behind popper releases, works like a dependency should


  • 🔴 Remove tippy.one(). Instead, an instance will be returned in two cases: if the reference is either an element or a virtual element
// v3
const collection = tippy('.btn')
const collection = tippy(document.querySelector('.btn'))
const instance = tippy.one('.btn')
const instance = tippy.one(document.querySelectorAll('.btn'))

// v4
// Instance
const instance = tippy(document.querySelector('.btn'))
const instance = tippy(virtualElement)
// Instance[]
const instances = tippy('#btn')
const instances = tippy('.btn')
const instances = tippy(document.querySelectorAll('.btn'))

Why: collections are not useful and don't need to exist. But we want to avoid an opaque return value if using a certain type. Therefore even if only a single element is given an instance when using a CSS selector, it always returns an array.


  • 🔴 Remove tippy.useCapture() and tippy.disableAnimations()

Why: .useCapture() is now default in v3 anyway. .disableAnimations() was for testing environments however the new delay value causes async behavior when hiding. In testing environments, use the following call to make everything synchronous:

tippy.setDefaults({ duration: 0, delay: 0 })

  • 🔴 Remove arrowTransform option

Why: Since introducing a bordered theme (light-border), using transforms distorts the shape of the arrow border. Arrow transforms should be specified in CSS using a theme.


  • 🔴 Remove autoFocus and shouldPopperHideOnBlur options. Remove all focus handling and leave it up to the developer.

Why: The recommended accessible popover is to append it directly after the reference element which requires no focus handling. This will ensure focusable elements inside the tippy can be tabbed to afterwards. Sometimes this can be problematic though, with regards to z-index /stacking issues and the popover needs to be appended to the body. In this case it's up to the developer to create modal-like behavior with a close button and focus trapping, they can programmatically call .focus() where need be.


  • 🔴 The AJAX image example (smooth transition): you must explicitly define transition styles on the tooltip now.

e.g. in the onShow() lifecycle:

onShow(instance) {
  const { tooltip } = instance.popperChildren
  tooltip.style.transitionDuration = '0.2s'
  tooltip.style.transitionProperty = 'visibility, opacity, height'
}

  • ⚠️ Rename performance to ignoreAttributes

Why: ignoreAttributes describes what it's doing, performance described the side effect of doing it


  • ⚠️ Rename tippy.hideAllPoppers() to tippy.hideAll()

Why: Shorter and more concise and conveys the same meaning


  • ⚠️ In event delegation mode, content and appendTo as functions receive the target element rather than the delegate reference

Why: It's actually useful this way


  • ⚠️ Rename livePlacement to flipOnUpdate, with much better behavior. It's now false by default. The tippy will still update its position on scroll, but flipping while scrolling/resizing, etc is disabled by default.

Why: Better UX (subjective) to keep the placement static after showing and prevent flips while scrolling


  • ⚠️ If your content is an element, and you change elements inside it, you must call instance.popperInstance.scheduleUpdate().

Why: MutationObserver is not as robust as a ResizeObserver and has been removed. If you want complete accuracy consider using a polyfill for ResizeObserver.


  • ⚠️ Opinioned CSS like font-smoothing has been removed. Tippy elements should inherit the global styles as much as possible.

Why: Let the developer have more control and not introduce unexpected styling


Fixes

  • Fix for tippys with delays when leaving the popper, it sometimes would not close

  • Correctly parse null in attributes

tippyjs - v4.0.0-alpha.0

Published by atomiks over 5 years ago

Install

npm i tippy.js@next

Overall minzipped size has been reduced by about 400 bytes

Breaking changes

  • 🔴 You no longer need to import CSS when using modules. It gets injected to the very top of the <head>, like the UMD version.
// v3
import tippy from 'tippy.js'
import 'tippy.js/dist/tippy.css'

// v4
import tippy from 'tippy.js'

Also, files renamed to index instead of tippy.

In case you don't want this, you still can import it separately:

import tippy from 'tippy.js/esm'
import 'tippy.js/index.css'

Additionally, everything exists in the root directory instead of under dist/. For example, themes:

import 'tippy.js/themes/light.css'

Why: cleaner import experience for consumers


  • 🔴 popper.js is external in all formats, so no more tippy.standalone. This means CDN includes must reference it separately.
<script src="https://unpkg.com/popper.js@1/dist/umd/popper.min.js"></script>
<script src="https://unpkg.com/tippy.js@4"></script>
<script>
  tippy('button')
</script>

Why: to prevent popper.js from becoming outdated and lagging behind popper releases, works like a dependency should


  • 🔴 Remove tippy.one(). Instead, an instance will be returned in two cases: if the reference is either an element or a virtual element
// v3
const collection = tippy('.btn')
const collection = tippy(document.querySelector('.btn'))
const instance = tippy.one('.btn')
const instance = tippy.one(document.querySelectorAll('.btn'))

// v4
// Instance
const instance = tippy(document.querySelector('.btn'))
const instance = tippy(virtualElement)
// Instance[]
const instances = tippy('#btn')
const instances = tippy('.btn')
const instances = tippy(document.querySelectorAll('.btn'))

Why: collections are not useful and don't need to exist. But we want to avoid an opaque return value if using a certain type. Therefore even if only a single element is given an instance when using a CSS selector, it always returns an array.


  • 🔴 Remove tippy.useCapture() and tippy.disableAnimations()

Why: .useCapture() is now default in v3 anyway. .disableAnimations() was for testing environments however the new delay value causes async behavior when hiding. In testing environments, use the following call to make everything synchronous:

tippy.setDefaults({ duration: 0, delay: 0 })

  • 🔴 Remove arrowTransform option

Why: Since introducing a bordered theme (light-border), using transforms distorts the shape of the arrow border. Arrow transforms should be specified in CSS using a theme.


  • 🔴 Remove autoFocus and shouldPopperHideOnBlur options. Remove all focus handling and leave it up to the developer.

Why: The recommended accessible popover is to append it directly after the reference element which requires no focus handling. This will ensure focusable elements inside the tippy can be tabbed to afterwards. Sometimes this can be problematic though, with regards to z-index /stacking issues and the popover needs to be appended to the body. In this case it's up to the developer to create modal-like behavior with a close button and focus trapping, they can programmatically call .focus() where need be.


  • ⚠️ Rename performance to ignoreAttributes

Why: ignoreAttributes describes what it's doing, performance described the side effect of doing it


  • ⚠️ Rename tippy.hideAllPoppers() to tippy.hideAll()

Why: Shorter and more concise and conveys the same meaning


  • ⚠️ In event delegation mode, content and appendTo as functions receive the target element rather than the delegate reference

Why: It's actually useful this way


  • ⚠️ Rename livePlacement to flipScroll, with much better behavior. It's now false by default. The tippy will still update its position on scroll, but flipping while scrolling is disabled.

Why: Better UX (subjective) to keep the placement static after showing and prevent flips while scrolling


  • ⚠️ If your content is an element, and you change elements inside it, you must call instance.popperInstance.scheduleUpdate().

Why: MutationObserver is not as robust as a ResizeObserver and has been removed. If you want complete accuracy consider using a polyfill for ResizeObserver.


  • ⚠️ Opinioned CSS like font-smoothing has been removed. Tippy elements should inherit the global styles as much as possible.

Why: Let the developer have more control and not introduce unexpected styling


Features / Improvements

  • followCursor: 'initial' works on touch devices too

  • updateDuration option is now 0 instead of 200 by default

Flips don't have the weird transition if using flipScroll: true (note that it's off by default now). Specifying a number now uses true smooth flips, where the popper element smoothly transitions from one placement to another. The effect is quite satisfying.


  • ✅ Padding between popper and container/viewport edges has been reduced to 2px from 5px by default

  • scale animation has its transform origin sticking to the reference element

  • tippy.hideAll() method is now actually useful!

It no longer checks hideOnClick unexpectedly. In other words:

tippy.hideAll()

Does exactly what it says. All tippys will be hidden no matter what (using their own specified durations).

There's even options now too, for example, make all of them hide instantly (useful for scrolling):

tippy.hideAll({ duration: 0 })

Or exclude a particular instance from hiding:

tippy.hideAll({ exclude: tippyInstance })

  • tippy.group() method - finally, a built-in solution to #81!

How to use it:

const instances = tippy('button', { delay: 1000 })
tippy.group(instances)

// Optional:
tippy.group(instances, {
  duration: number | [number, number], // instead of 0 between each show,
  delay: number | [number, number], // if you didn't specify a delay in the instances
})

As simple as that, the tooltips will now instantly show when hovering between items in the group instead of waiting for the delay sluggishly.


  • role option - instead of tooltip, certain popovers may need to be menu or dialog for accessibility reasons.
tippy('button', {
  role: 'menu'
})
tippyjs - v3.4.1

Published by atomiks almost 6 years ago

Fixes

  • Make touchstart passive for touchHold: true listeners (#344)
  • Ability to specify null / false for the aria option to disable the attribute completely (#402)
  • Improved inertia animation a bit
  • followCursor: 'initial' edge case fix with delays
tippyjs - v3.4.0

Published by atomiks almost 6 years ago

Features

  • Add followCursor: 'initial' option (#374)
  • Add boundary option (#394)
  • autoFocus and aria options (#393)

Fixes

  • Typings improvements (#395)
tippyjs - v3.3.0

Published by atomiks almost 6 years ago

Features

Fixes

  • popper.js^1.14.6 includes position rounding fixes
  • Fix slight text blurriness on Safari when animateFill: true
  • google theme adheres to size option
  • Arrow centering fixes for light-border.css due to Popper.js issue
tippyjs - v3.2.0

Published by atomiks almost 6 years ago

Deprecations

Fixes

tippyjs - v3.1.1

Published by atomiks almost 6 years ago

Fixes

  • 🐛 Fix regression with data-tippy-content attribute (#341)
tippyjs - v3.0.0

Published by atomiks about 6 years ago

It's here! 🎈

It's now on npm as the default @latest.

npm i tippy.js

View the docs: https://atomiks.github.io/tippyjs/

Breaking changes

This version changes the API on a fundamental level, so you will need to update all your call sites. You no longer use a title attribute, and there is no longer an html option. Instead, both of them have been streamlined into a single option called content.

In addition, tooltips can now be auto-initialized.

Method 1 (auto)

<button data-tippy="Tooltip">Text</button>
<button data-tippy="<strong>HTML</strong>">Text</button>

Elements with a data-tippy attribute are automatically given a tooltip. You no longer need to touch JavaScript. (Note on performance: It takes <1ms to do this check on a page if there are no tooltips).

Method 2 (function)

You can give elements a data-tippy-content attribute and use the function:

<button data-tippy-content="Tooltip">Text</button>
<script>tippy('button')</script>

Or specify the content as an option:

<button>Text</button>
<script>tippy('button', { content: 'Tooltip' })</script>

It can contain HTML, and you can use an HTMLElement as the value.

Collections

The object that is returned:

selector is now named targets
tooltips is now named instances

Instances

  • popperChildren: access the children of the popper element easily without needing to do query selections or manual DOM walking.
  • options has been renamed to props. Options are the props you supply optionally to tippy(), but it doesn't make sense for the tooltip configuration object to be called options.
  • state object properties are all prefixed with is because they are boolean values.
  • listeners made private

Option changes

  • multiple: Boolean can the tooltip have multiple tippy instances?
  • createPopperInstanceOnInit is now lazy: Boolean with the reverse behavior.
  • maxWidth removed - should be handled via a theme
  • hideOnClick changes "persistent" to "toggle" and it has the opposite behavior. This means that hideOnClick: false will prevent click-triggered tooltips from ever hiding unless you specify "toggle"

New features

  • Sourcemaps + ES module are now in the package. Latest Popper.js@1 version is used when importing tippy.

Methods

  • tippy.set(options)

This was previously impossible with v2: you can now change any option at runtime after the tippy instance was created.

  • tippy.setContent(newContent)

Shortcut for .set({ content: newContent })

Options

  • followCursor: Boolean|"horizontal"|"vertical"

You can now make the tooltip follow the cursor on a single axis instead of both by specifying a string.

  • touch: Boolean

An option to effortlessly disable a tooltip from displaying on touch devices.

  • a11y: Boolean

An option to ensure the reference element can receive focus.

  • showOnInit: Boolean

Show the tooltip immediately?

  • Cancel lifecycles for onShow / onHide

You can now cancel lifecycles by returning the boolean false.

  • shouldPopperHideOnBlur: Function

Added due to improved focus handling

  • interactiveDebounce: Number (ms)

Debounce on the internal onMouseMove handler for interactive inline text tooltips that span 2+ lines

Fixes

  • #193

  • #139

  • Accessibility/focus handling

Focus handling and accessibility has been improved. Tabbing/keyboard nav around interactive tooltips now works better.

Official React Wrapper

A lightweight wrapper is now officially published under @tippy.js org.

npm i @tippy.js/react

View the docs for the React component here


I hope you like it! 🎉

tippyjs - v2.6.0

Published by atomiks about 6 years ago

  • #289: Add tippy.useCapture() method to change the global click listener to use capture before bubbling. Call it before any calls to tippy()
  • Fixes rare issue where onTransitionEnd does not fire with onHidden by removing an internal hack, and using delay: [0, 20] as the default instead of delay: 0
tippyjs - v3.0.0-beta.1

Published by atomiks about 6 years ago

  • "module" field points to the dist/esm/tippy.standalone.js file for latest 1.x Popper.js dependency
  • Fixes issue with focusable element requiring two show calls after being focused
  • Adds shouldPopperHideOnBlur option, closes #285
  • Adds missing role attribute from popper element
tippyjs - v3.0.0-beta.0

Published by atomiks about 6 years ago

tippyjs - v3.0.0-alpha.3

Published by atomiks about 6 years ago

  • Add interactiveDebounce (number in ms) option (#274)
  • Add sourcemaps
  • tippy.hideAllPoppers static method exposed
  • Fix #278, #279 regressions in alpha.2
  • Remove unnecessary code in set() method which now integrates better with React, etc.
  • Other misc edge cases resolved
tippyjs - v3.0.0-alpha.2

Published by atomiks about 6 years ago

  • Fixes #276 popperOptions not being used
  • Fixes #193 finally (but need to attend to #274)
  • Fixes #275 tippy.defaults not updating when using tippy.setDefaults()
  • Fixes potential errors being thrown on window resize
  • Adds .setContent() method as a shortcut to .set({ content })
  • autoInit() function is deferred with setTimeout() to allow the defaults to be changed before auto-initialization
tippyjs - v3.0.0-alpha.1

Published by atomiks about 6 years ago

  • Include IE11 transitionend fix
  • Subtler animations for scale and perspective
  • Popper.js 1.14.4
  • Include esm directory and module field in package.json
tippyjs - v3.0.0-alpha.0

Published by atomiks over 6 years ago

Tippy.js 3: alpha 0

The first alpha release of Tippy.js v3.

npm i tippy.js@next

What's changed?

You no longer use a title attribute on elements, and the html option has been removed. Both functionalities have been streamlined into a single prop: content.

Method 1: Auto

<button data-tippy="Tooltip">Text</button>
<button data-tippy="<strong>HTML</strong>">Text</button>

Elements with a data-tippy attribute are automatically given a tooltip. You no longer need to touch JavaScript. (Note on performance: It takes <0.5ms to do this check on a page if there are no tooltips).

Method 2: content

You can give elements a data-tippy-content attribute and use the function:

<button data-tippy-content="Tooltip">Text</button>
<script>tippy('button')</script>

Or specify the content as an option.

<button>Text</button>
<script>tippy('button', { content: 'Tooltip' })</script>

It can contain HTML, and you can use an HTMLElement as the value.

tippy() call

The object that is returned:

  • selector is now named targets
  • tooltips is now named instances

Instances

  • New popperChildren property: access the children of the popper element easily without needing to do query selections or manual DOM walking.
  • options has been renamed to props. Options are the props you supply optionally to tippy(), but it doesn't make sense for the tooltip configuration object to be called options. Instead, it's now props, inspired by React.
  • state object properties are all prefixed with is because they are boolean values.
  • listeners made private

Methods

The most useful new method is set(). It allows you to update the props of a tooltip after it has been created.

const tip = tippy.one('.btn', { 
  content: 'Hello!', 
  arrow: true,
  delay: 500
})

// Later on...
tip.set({ 
  content: 'Bye!', 
  arrow: false,
  delay: 0
})

Static

  • tippy.setDefaults(props) - pass an object in to change the default configuration
  • tippy.browser removed

Options/Props

  • a11y: true - (accessibility shorthand): ensure the reference element can receive focus
  • allowTitleHTML: true is now allowHTML
  • hideOnClick changes "persistent" to "toggle" and it has the opposite behavior. This means that hideOnClick: false will prevent click-triggered tooltips from ever hiding unless you specify "toggle". Not 100% sure on this, however.
  • touch: true- display the tooltip on touch devices?
  • createPopperInstanceOnInit: false is now lazy: true. More concise name.
  • showOnInit: false - show the tooltip immediately?
  • maxWidth removed. Should be handled by a theme in CSS because of narrow mobile devices.
  • multiple: false - can the reference have multiple tippys? Currently each new instance overrides the previous _tippy property. It most likely needs to be turned into an array on the second init, but I fear this causes unpredictable behavior. (The previous option which allowed multiple click triggered tooltips to be open is now gone. I don't see much demand for it, but it might return as a different name).
  • duration/delay: you can now leave a value undefined in the array and the default value is used. e.g. duration: [, 100] (use default for show) or duration: [100] (use default for hide). It's more concise than an object: duration: { hide: 100 } but the syntax is kind of messy. Might change this.

Accessibility/focus handling

Focus handling and accessibility has been improved. Tabbing/keyboard nav around interactive tooltips now works better.

Handling fallback for unsupported browsers

I believe this should be handled by the user, not the library.

The best way is to inline the tooltip content next to the reference element on old mobile & desktop browsers for interactive HTML popovers. For simple tooltips on desktop, manually set the title property on the element.

Unsupported = <0.5% worldwide usage or nonstandard browsers.

TODO before final release

  • Fix #193
  • More configurable animations. Currently can be handled with theme but requires lots of boilerplate selectors. Would be nice to make it declarative as an option.
  • #174: Cooler animations someday?
  • Finish writing tests. There's over 200, but I probably need to move back to a browser from Jest because there are too many things that need to be tested in a real browser using a layout engine.
  • Package: sourcemaps, ES module (maybe, since there's not much to treeshake)
tippyjs - v2.5.3

Published by atomiks over 6 years ago

  • fix: #249
  • fix: remove problematic iOS code that autoclicks which sometimes causes doubles clicks