← Back to Blog

Sticky Map for FacetWP and Results — with Viewport Height

When using FacetWP to display a filterable map and results layout, you often want the map to stay visible while users scroll through the results. In this post, we’ll show you how to make the left sidebar (map) stick to the viewport height and remain fixed during scroll, only on desktop screens.

Screenshot_2025-08-05_at_11.57.59.png

We’ll do this with clean vanilla JavaScript and responsive design principles — no external libraries or frameworks required.


Use Case

Imagine this layout:


HTML Structure

Here’s a basic version of the layout you might use on a FacetWP-powered results page:

<div class="buzy__results_container">
  <div class="buzy__left">
    <div class="buzy__left-inner">
      <div id="facetwp-map">
        <!-- Your interactive map or filter UI -->
      </div>
    </div>
  </div>

  <div class="buzy__right">
    <!-- FacetWP results go here -->
    <div class="facetwp-template">
      <div class="result">Result 1</div>
      <div class="result">Result 2</div>
      <div class="result">Result 3</div>
      <!-- ... -->
    </div>
  </div>
</div>

JavaScript for Sticky Behavior

Save this in a separate file or include it in your theme’s JS bundle:

(function () {
    const left = document.querySelector('.buzy__left-inner');
    const right = document.querySelector('.buzy__right');
    const container = document.querySelector('.buzy__results_container');

    if (!left || !right || !container) return;

    const spacer = document.createElement('div');
    spacer.style.height = `${left.offsetHeight}px`;
    spacer.style.display = 'none';
    left.parentNode.insertBefore(spacer, left);

    function isDesktop() {
        return window.innerWidth >= 992; // match CSS breakpoint
    }

    function resetPositioning() {
        left.style.position = 'static';
        left.style.top = '';
        left.style.width = '';
        left.style.height = '';
        spacer.style.display = 'none';
    }

    function updateLeftHeight() {
        if (isDesktop()) {
            left.style.height = `${window.innerHeight}px`;
        } else {
            left.style.height = ''; // Reset for mobile
        }
    }

    function onScroll() {
        if (!isDesktop()) {
            resetPositioning();
            return;
        }

        const containerRect = container.getBoundingClientRect();
        const leftHeight = left.offsetHeight;
        const rightHeight = right.offsetHeight;

        if (rightHeight <= leftHeight) {
            resetPositioning();
            return;
        }

        if (containerRect.top <= 0 && containerRect.bottom > leftHeight) {
            // Stick to top
            left.style.position = 'fixed';
            left.style.top = '0';
            left.style.width = `${left.parentElement.offsetWidth}px`;
            spacer.style.display = 'block';
        } else if (containerRect.bottom <= leftHeight) {
            // Stick to bottom of container
            const offsetTop = container.offsetTop;
            const bottomOffset = container.offsetHeight - leftHeight;
            left.style.position = 'absolute';
            left.style.top = `${bottomOffset}px`;
            left.style.width = `${left.parentElement.offsetWidth}px`;
            spacer.style.display = 'block';
        } else {
            resetPositioning();
        }
    }

    window.addEventListener('scroll', () => {
        onScroll();
        updateLeftHeight(); // Optional: if height can change on scroll
    });

    window.addEventListener('resize', () => {
        spacer.style.height = `${left.offsetHeight}px`;
        onScroll();
        updateLeftHeight();
    });

    onScroll();
    updateLeftHeight();
})();

Key Features of the Script

Why We Did This for FacetWP

When using FacetWP with a map and result list, users expect the map (and possibly filters) to remain visible. Instead of relying on position: sticky (which lacks control over container boundaries), we:

This provides a smooth user experience, especially for long result lists where users want to refer back to the map while scrolling.


Final Tip

If you use a fixed header, you may want to offset the sticky position by adjusting this line:

if (containerRect.top <= 0 && scrollBottom < containerBottom)

To:

if (containerRect.top <= headerHeight && scrollBottom < containerBottom)

Then define:

const headerHeight = 80; // or whatever your fixed header height is