Scroll
Lenis Toggle Scroll

Info

Controls Lenis scrolling using simple data attributes, allowing you to lock, unlock, or toggle scroll based on clicks or element visibility. It also handles the scrollbar gap automatically to prevent layout shift when the scrollbar is hidden.

Scroll
April 27, 2026

Required

Setup

00

Add External Scripts

Place the scripts before the body tag of your project. If you have added them before for another setup, skip this step.

<script src="https://cdn.jsdelivr.net/npm/tua-body-scroll-lock"></script>
00

Add Custom Javascript

Paste the script through Elementor → Custom Code and set it to load after the closing body tag.

document.addEventListener("DOMContentLoaded", () => {
  if (document.body.classList.contains("elementor-editor-active")) return;

  const lenis = window.ouLenis;
  const bodyLock = window.bodyScrollLock || window.tuaBodyScrollLock;

  if (!lenis || !bodyLock) return;

  const gap = "data-lenis-scrollbar-gap";
  const visibleSelector = "[data-lenis-visible]";

  let locked = false;
  let manualOverride = false;

  const visibleEls = document.querySelectorAll(visibleSelector);

  const shouldUseGap = el =>
    !el || el.getAttribute(gap) !== "false";

  const setScroll = (shouldLock, el, manual = false) => {
    if (manual) manualOverride = !shouldLock;

    if (locked === shouldLock) return;

    if (shouldLock) {
      bodyLock.lock(el || document.body, {
        withPaddingRight: shouldUseGap(el)
      });

      lenis.stop();
      locked = true;
      return;
    }

    bodyLock.clearBodyLocks();
    lenis.start();
    locked = false;
  };

  const updateVisible = el => {
    if (manualOverride) return;

    const hidden = getComputedStyle(el).display === "none";
    const rect = el.getBoundingClientRect();
    const inView = rect.top < window.innerHeight && rect.bottom > 0;

    setScroll(!hidden && inView, el);
  };

  document.addEventListener("click", e => {
    const start = e.target.closest("[data-lenis-start]");
    const stop = e.target.closest("[data-lenis-stop]");
    const toggle = e.target.closest("[data-lenis-toggle]");

    if (start) {
      setScroll(false, start, true);
      return;
    }

    if (stop) {
      manualOverride = false;
      setScroll(true, stop, true);
      return;
    }

    if (toggle) {
      setScroll(!locked, toggle, true);
      return;
    }
  });

  if (!visibleEls.length) return;

  const observer = new IntersectionObserver(entries => {
    entries.forEach(entry => {
      if (manualOverride) return;

      const el = entry.target;
      const hidden = getComputedStyle(el).display === "none";

      setScroll(entry.isIntersecting && !hidden, el);
    });
  });

  visibleEls.forEach(el => observer.observe(el));

  new MutationObserver(() => {
    manualOverride = false;
    visibleEls.forEach(updateVisible);
  }).observe(document.body, {
    attributes: true,
    subtree: true,
    attributeFilter: ["style", "class", gap]
  });
});
00

Publish and preview live

Some solutions only work on the live site. Always publish and test after each change, as results may not appear in the editor.

Stop Scrolling

Use data-lenis-stop="true" on the element that should pause page scrolling when clicked. This is usually best for modal open buttons, menu triggers, popup buttons, or any interaction that opens an overlay where the background page should not move.

Start Scrolling

Use data-lenis-start="true" on the element that should resume page scrolling when clicked. This is usually used on close buttons, modal overlays, menu close icons, or any element that exits the locked-scroll state.

Toggle Scrolling

Use data-lenis-toggle="true" when the same element controls both opening and closing, such as a hamburger menu button or custom toggle button. Each click switches page scrolling between locked and unlocked state.

Pause When Visible

Use data-lenis-visible="true" on the element that should control scroll locking based on visibility. When this element is visible in the viewport, scrolling pauses. When it leaves the viewport or becomes hidden with display: none, scrolling resumes. This is best for modals, overlays, panels, or any dynamic element.

Scrollbar Gap

Use data-lenis-scrollbar-gap="false" only when you do not want the script to reserve space for the browser scrollbar. By default, the gap fix is enabled to prevent the layout from shifting sideways when the scrollbar disappears. This attribute can be added to any element that triggers or controls the scroll state.