Popper.js - Documentation

What is Popper.js?

Popper.js is a lightweight JavaScript library that provides a robust solution for positioning tooltips, dropdowns, popovers, and other similar elements relative to a reference element. It handles various scenarios gracefully, including those involving complex layouts, scrolling, and viewport boundaries. Unlike many simpler positioning libraries, Popper.js accounts for many edge cases and offers fine-grained control over placement adjustments. At its core, it’s a sophisticated positioning engine designed for flexibility and accuracy.

Why use Popper.js?

You should use Popper.js when you need reliable and predictable positioning of elements relative to a reference element, especially in dynamic contexts. Here’s why it excels:

Setting up Popper.js

Popper.js can be included in your project in several ways:

<script src="https://unpkg.com/@popperjs/core@2/dist/umd/popper.min.js"></script>
npm install @popperjs/core
# or
yarn add @popperjs/core

Then, you can import it in your JavaScript code (using a module bundler like Webpack or Parcel):

import { createPopper } from '@popperjs/core';

Remember to include Popper.js before your code that uses it.

Basic Usage Example

This example shows how to position a tooltip (#tooltip) relative to a button (#reference).

<button id="reference">Click me</button>
<div id="tooltip">This is a tooltip</div>

<script>
  import { createPopper } from '@popperjs/core';

  const reference = document.getElementById('reference');
  const tooltip = document.getElementById('tooltip');

  createPopper(reference, tooltip, {
    placement: 'top', // other options: bottom, left, right, top-start, etc.
  });
</script>

This code creates a Popper instance, linking the #tooltip to the #reference. The placement option specifies that the tooltip should appear above the button. More advanced configurations and options are detailed in subsequent sections of this manual. Remember to adjust CSS to appropriately style your tooltip and reference elements. For example, ensure the #tooltip is initially hidden with display: none; and styled to your liking.

Core Concepts

The Popper Instance

The core of Popper.js is the Popper instance, created using the createPopper function. This instance manages the positioning logic and updates the positioned element’s position whenever necessary (e.g., on window resize, scroll, or reference element movement). The createPopper function takes two main arguments: the reference element (the element to which the positioned element is relative) and the popper element (the element being positioned). It returns an object representing the Popper instance, allowing you to access methods and properties for controlling its behavior. This instance should be stored to allow for later cleanup (using destroy()).

import { createPopper } from '@popperjs/core';

const reference = document.getElementById('reference');
const popper = document.getElementById('popper');

const popperInstance = createPopper(reference, popper, { /* options */ });

// ... later, when you no longer need the Popper instance ...
popperInstance.destroy();

Data Attributes

While not strictly required, Popper.js supports several data attributes that can simplify configuration and customize behavior without needing JavaScript. These attributes are prefixed with data-popper-. For example:

Example using data-popper-placement:

<button id="reference">Reference</button>
<div id="popper" data-popper-placement="bottom-end">Popper</div>

Modifiers

Modifiers are plugins that extend Popper.js’s core functionality. They allow you to add custom logic or modify the default positioning behavior. Modifiers are added to the modifiers option within the createPopper function’s options object. Each modifier is an object with specific properties, most importantly name and fn. The fn property is the function which alters the position. Modifiers are executed sequentially in the order they appear in the array.

createPopper(reference, popper, {
  modifiers: [
    {
      name: 'offset',
      options: {
        offset: [0, 8] // Example offset modifier
      }
    },
    {
      name: 'preventOverflow', // Example preventOverflow modifier
      options: {
        padding: 10 // example padding
      }
    }
  ]
});

Popper.js includes many built-in modifiers (like offset, preventOverflow, flip, hide, arrow, computeStyles and applyStyles). Refer to the API documentation for details on each modifier.

Placement

The placement option determines the position of the popper relative to the reference element. Valid values include: top, bottom, left, right, and their variations with -start and -end suffixes (e.g., top-start, bottom-end). These suffixes specify alignment along the axis perpendicular to the primary placement. top-start means the popper is placed above and aligned to the beginning (left) of the reference, while top-end aligns it to the end (right).

Offsets

The offset modifier allows you to apply an offset to the popper’s position. The offset is specified as an array [x, y], where x is the horizontal offset and y is the vertical offset. Positive values move the popper in the positive direction of the respective axis.

// Moves the popper 10px to the right and 5px down.
{ name: 'offset', options: { offset: [10, 5] } }

Boundaries

The preventOverflow modifier prevents the popper from overflowing the boundaries of its container. By default, it uses the viewport as the container. You can specify a different element using the boundary option. The padding option adds extra space between the popper and its boundaries.

{
  name: 'preventOverflow',
  options: {
    boundary: 'clippingParents', // or 'scrollParent', 'viewport'
    padding: 10
  }
}

Arrow Element

For tooltips and similar elements, an arrow element visually connects the popper to the reference element. To use it, you need an element with the class popper-arrow inside your popper element. The arrow modifier will automatically position this arrow element. You can control the arrow’s element via the element option.

<div id="popper">
  <div class="popper-arrow"></div>
  <div>Tooltip content</div>
</div>

//In the createPopper options
{name: 'arrow', options: { element: '.popper-arrow' } }

Remember to style the .popper-arrow class appropriately.

Modifiers in Detail

This section details the built-in modifiers provided by Popper.js. Remember that you can combine these modifiers to achieve complex positioning behaviors. The order in which modifiers are listed in the modifiers array affects their execution order.

offset

Applies an offset to the final calculated position of the popper.

{
  name: 'offset',
  options: {
    offset: [10, 5] // 10px right, 5px down
  }
}

//Example with function
{
  name: 'offset',
  options: {
    offset: ({ state, rect }) => [state.placement.startsWith('top') ? -rect.height : 0, 0],
  }
}

preventOverflow

Prevents the popper from overflowing its boundaries.

{
  name: 'preventOverflow',
  options: {
    boundary: 'clippingParents',
    padding: 10,
    tether: true,
    tetherOffset: [0, 10],
  }
}

flip

Flips the popper’s placement to an alternative side if it overflows its boundaries.

{
  name: 'flip',
  options: {
    fallbackPlacements: ['bottom', 'right'],
    padding: 5,
  }
}

hide

Hides the popper if it overflows its boundaries and no flip is possible.

{ name: 'hide' }

shift

Shifts the popper slightly to ensure it remains within its boundaries.

{
  name: 'shift',
  options: {
    maxShift: 10,
  }
}

arrow

Positions the arrow element to visually connect the popper and reference.

{
  name: 'arrow',
  options: {
    element: '.popper-arrow'
  }
}

computeStyles

Computes the styles for positioning the popper. This modifier is essential and usually should not be modified or removed.

{ name: 'computeStyles' } //Usually included by default

inner

Positions the popper within the reference element. This is useful for tooltips that have to be fully contained inside a parent container.

{ name: 'inner' }

popperOffsets

This modifier doesn’t modify the popper’s position; instead, it adds the calculated popper offsets (x and y) to the state.modifiersData object. This information can be used by other modifiers or custom logic.

{ name: 'popperOffsets' } // Usually included by default

Remember to consult the official Popper.js documentation for the most up-to-date information and advanced usage details. The examples provided here are simplified for clarity.

Advanced Usage

This section covers more advanced scenarios and techniques when using Popper.js.

Customizing Placement

Beyond the standard placements (top, bottom, left, right, and their variations), you can achieve highly customized placement using a combination of modifiers and the placement option.

Working with Different DOM Structures

Popper.js adapts to various DOM structures, but understanding how it interacts with different elements is crucial.

Handling Complex Layouts

Popper.js handles complex layouts reasonably well, but certain situations may require special attention.

Integrating with other Libraries

Integrating Popper.js with other libraries often requires careful consideration.

Performance Optimization

For optimal performance, especially with many Popper instances, consider these strategies:

Accessibility Considerations

Accessibility is crucial. Consider the following points:

By carefully considering these advanced usage scenarios and best practices, you can leverage Popper.js’s full potential to create robust and accessible user interfaces. Remember that the official documentation is an invaluable resource for further details and advanced examples.

API Reference

This section details the Popper.js API, providing a comprehensive guide to its functions and options.

createPopper()

The primary function to create a Popper instance.

Signature:

createPopper(reference, popper, options?)

Return Value:

A Popper instance object. This object contains methods for managing the Popper instance (see “Instance Methods” below).

Example:

import { createPopper } from '@popperjs/core';

const reference = document.getElementById('reference');
const popperElement = document.getElementById('popper');

const popperInstance = createPopper(reference, popperElement, {
  placement: 'top',
  modifiers: [
    { name: 'offset', options: { offset: [0, 8] } }
  ]
});

Instance Methods

The Popper instance object provides the following methods:

Example:

popperInstance.update(); // Update position
popperInstance.destroy(); // Clean up
console.log(popperInstance.state); // Access positioning data

Modifier Options

Modifiers are configurable objects within the options parameter of createPopper(). Each modifier has specific options (detailed in the “Modifiers in Detail” section of this manual). The general structure is:

{
  name: 'modifierName', //Name of the modifier
  options: {
    // Modifier-specific options
    option1: value1,
    option2: value2
  },
  fn: functionName //optional custom function to replace the default function of the modifier.
}

Modifiers are executed in the order they are specified in the modifiers array within the options object. The order is significant; some modifiers depend on the output of others.

Data Attributes API

Popper.js supports setting options via HTML data attributes. These attributes are prefixed with data-popper-. While less flexible than using JavaScript, data attributes can simplify basic configurations.

Example:

<button id="reference">Reference</button>
<div id="popper" data-popper-placement="bottom-end" data-popper-modifiers='[{"name": "offset", "options": {"offset": [10, 10]}}]'></div>

This example uses data-popper-placement and data-popper-modifiers to specify the placement and an offset without needing JavaScript configuration (except for initializing the createPopper function). However, complex configurations should generally be handled using the JavaScript API for better readability and maintainability. The data-popper-reference-hidden is helpful when dealing with visibility changes of the reference element.

Remember to consult the official Popper.js documentation for the most up-to-date and comprehensive API reference. The information provided here is a summary intended to aid in understanding the core functionalities.

Troubleshooting

This section provides guidance on resolving common issues and debugging techniques when using Popper.js.

Common Issues and Solutions

Debugging Tips

Error Messages and Handling

Popper.js generally throws errors when it encounters critical problems (e.g., invalid options, missing elements). These errors typically include informative messages explaining the cause.

try {
  const popperInstance = createPopper(reference, popperElement, options);
} catch (error) {
  console.error("Popper.js error:", error);
  // Add your error handling logic here (e.g., display a user-friendly message).
}

By following these troubleshooting steps and utilizing debugging techniques, you can effectively resolve most issues encountered while using Popper.js. Remember to consult the official documentation for more detailed explanations and examples.

Examples and Use Cases

This section presents examples and best practices for using Popper.js with various UI components.

Tooltips

Tooltips provide brief information when hovering over an element. Popper.js excels at positioning tooltips accurately, even when dealing with scrolling or complex layouts.

Implementation:

  1. HTML: Create a reference element (e.g., a button) and a tooltip element. The tooltip should initially be hidden (e.g., display: none;). Include an arrow element (optional) with the class popper-arrow.

    <button id="tooltip-reference">Hover over me</button>
    <div id="tooltip" class="tooltip">This is a tooltip</div>
  2. CSS: Style the tooltip to your liking. Add styling for the arrow (optional).

    .tooltip {
      background-color: #333;
      color: white;
      padding: 8px 12px;
      border-radius: 4px;
      box-shadow: 0 2px 5px rgba(0,0,0,0.3);
      display: none; /* Initially hidden */
      position: absolute; /* Required for Popper.js */
    }
    
    .tooltip.popper-arrow::before {
        content: "";
        position: absolute;
        top: 50%;
        left: 0;
        transform: translate(-50%, -50%) rotate(45deg);
        width: 10px;
        height: 10px;
        background-color: inherit;
    }
    
    #tooltip-reference:hover + .tooltip {
      display: block; /* Show on hover */
    }
  3. JavaScript: Use createPopper to position the tooltip relative to the reference. Add event listeners to show and hide the tooltip.

    import { createPopper } from '@popperjs/core';
    
    const reference = document.getElementById('tooltip-reference');
    const tooltip = document.getElementById('tooltip');
    
    const popperInstance = createPopper(reference, tooltip, {
      placement: 'top',
      modifiers: [{ name: 'arrow', options: { element: '.popper-arrow' }}]
    });
    
    reference.addEventListener('mouseenter', () => {
      tooltip.style.display = 'block';
    });
    reference.addEventListener('mouseleave', () => {
      tooltip.style.display = 'none';
    });

Dropdowns present a list of options when a button is clicked. Popper.js ensures the dropdown remains within the viewport.

Implementation: Similar to tooltips, but use a click event to show/hide the dropdown, and consider adding functionality to close the dropdown when clicking outside of it.

Menus are similar to dropdowns but can have more complex structures (nested items, multiple levels). Popper.js helps maintain accurate positioning for nested menu items. Consider using the flip modifier to handle scenarios where the menu overflows the viewport.

Implementation: Recursive approach needed, recursively creating popper instances for submenus. Event listeners are key for managing the opening and closing of the submenus on different mouse events.

Popovers

Popovers are similar to tooltips but often contain more content. Popper.js handles the positioning and overflow gracefully, preventing the popover from being cut off or overlapping elements.

Implementation: Similar to tooltips, but with more elaborate styling and potentially more complex content within the popover element. You may want to add functionality to automatically close the popover after a certain delay.

Custom Components

Popper.js is highly versatile and can be integrated into any custom component requiring precise positioning relative to a reference. Utilize modifiers to customize positioning based on component-specific needs. Ensure that you handle events correctly, particularly if dynamic content updates might require a repositioning.

Implementation: Design your custom component in HTML and CSS. Use createPopper to position the component correctly, adding custom modifiers as required to fulfill the custom logic and behavior.

These examples illustrate the core usage. Advanced scenarios might require more sophisticated strategies, including custom modifiers or advanced modifier options. Remember to always consult the official Popper.js documentation for the most up-to-date information and detailed examples.