Await scroll completion with Programmatic Scroll Promises

Post on X
Copy URL
Share

JavaScript provides methods such as scrollTo() and scrollBy() for scrolling to a target position. These methods are often used with the behavior property set to "smooth" to animate the scroll smoothly to the destination. A common example is smooth scrolling to the relevant section when a table of contents item is clicked.

The main drawback of smooth scrolling is that it has been difficult to run code after scrolling finishes. With Programmatic Scroll Promises, available from Chrome and Edge 150, scroll completion that previously had to be detected with the scrollend event or setTimeout() can be controlled with a Promise.

Handling scroll completion with Promise

A Promise is an object for controlling asynchronous processing. It is commonly used to handle the completion or failure of operations that take time, such as fetching data from a server or running animations. Combined with await, a Promise lets you wait until a process finishes before running the next step. For more about Promise and await, see the article JavaScriptのモダンな書き方- ES2017〜ES2018のawait・async, includes(), padStart()等を解説.

Just await the scroll

Until now, methods such as scrollTo() were specified to return undefined. With Programmatic Scroll Promises, scroll-related methods return a Promise. This makes it possible to control the sequence of waiting for scrolling to finish and then running the next process.

The following code waits for smooth scrolling with scrollTo() to finish by using await, then displays a message. Since all it requires is adding await, the implementation is very straightforward.

Note: The following demos require Chrome or Edge 150 or later.

// Omitted
const message = document.querySelector("#message");

const onClick = async () => {
  message.textContent = "";
  // Wait for scrolling to finish with await
  await window.scrollTo({
    top: target.offsetTop,
    behavior: "smooth",
  });
  // Display a message after scrolling finishes
  message.textContent = "scroll end!";
};

Covered methods

The explainer explainers-by-googlers/promisify-scroll, which describes Programmatic Scroll Promises, lists the following methods as covered.

  • scroll(): Scrolls the page or an element to a specified position
  • scrollTo(): Equivalent to scroll()
  • scrollBy(): Scrolls by a specified amount from the current position
  • scrollIntoView(): Scrolls the page or parent container so that the element on which the method is called enters the visible area

As of July 2026, in the author’s environment, scrollIntoView() resolved its Promise before scrolling had completed. Future improvements are expected. Test environment: Chrome 150.0.7871.46 / macOS 26.5.1 / MacBook Air M1, 2020

Why is this useful?

What are the benefits of controlling scrolling with Promise? Consider a case where clicking a table of contents item scrolls to the relevant section and then highlights the section heading. Assume the following requirements.

  • When a table of contents item is clicked, smoothly scroll to the section and animate a highlight on the heading
  • Do not animate the highlight for scrolling triggered by anything other than clicking the table of contents

Previous approaches and their issues

Monitoring the scrollend event

You need to determine whether the scroll was triggered by clicking the table of contents or by something else. The scrollend event tells you that scrolling has completed on the page as a whole or on a specific DOM element, but it does not tell you what triggered that scroll.

The following code branches the process with a shouldHighlight flag to determine what caused the scroll. However, flag-based control can lead to unintended display states if you forget to turn the flag on or off. In a single-page application, forgetting to remove the event listener when navigating to another page can also cause unnecessary scrollend events to keep firing.

let shouldHighlight = false;

// Process when a table of contents item is clicked
const onClick = () => {
  // Set the flag
  shouldHighlight = true;
  window.scrollTo({
    top: targetSection.offsetTop,
    behavior: "smooth",
  });
};

window.addEventListener("scrollend", () => {
  if (!shouldHighlight) {
    // Do nothing unless the table of contents was clicked
    return;
  }
  shouldHighlight = false;
  highlightTitle(targetSection);
});

Using setTimeout()

After a table of contents item is clicked, setTimeout() waits for a fixed amount of time before running the animation. Because the scroll duration changes depending on the distance, the animation may start too early or too late.

const onClick = () => {
  window.scrollTo({
    top: section.offsetTop,
    behavior: "smooth",
  });

  // Highlight after 0.5 seconds
  window.setTimeout(() => {
    highlightTitle(section);
  }, 500);
};

Using Intersection Observer

When using Intersection Observer, the issue is the same as with scrollend. You still need to branch the process depending on whether the table of contents was clicked or the scroll was triggered by something else.

let shouldHighlight = false;

const observer = new IntersectionObserver((entries) => {
  if (!shouldHighlight) {
    // Do nothing unless the table of contents was clicked
    return;
  }
  entries.forEach((entry) => {
    if (entry.target === targetSection && entry.isIntersecting) {
      // Highlight once the target intersects
      shouldHighlight = false;
      highlightTitle(targetSection);
    }
  });
});

For more about Intersection Observer, see the article JSでのスクロール連動エフェクトにはIntersection Observerが便利.

Using Programmatic Scroll Promises

When scroll completion can be controlled with a Promise, the process becomes simpler. There is no need to monitor unnecessary events.

const onClick = async (event) => {
  // Omitted
  await window.scrollTo({
    top: section.offsetTop,
    behavior: "smooth",
  });
  // Highlight after scrolling
  highlightTitle(section);
};

Application: guide tour demo

This demo implements a guide tour that explains how to use an application. Programmatic Scroll Promises are a good fit for use cases where the UI scrolls to a specific element, displays an explanation, and then moves to the next element.

After scrolling finishes, the target section is highlighted. Applying CSS after scrolling completes naturally draws the user’s attention to the target section.

const showStep = async (stepIndex) => {
  const step = steps[stepIndex];
  const target = document.querySelector(step.selector);

  // Omitted

  await window.scrollTo({
    behavior: "smooth",
    top: centerY,
  });

  // Add is-active to the target after scrolling to highlight it
  target.classList.add("is-active");
};

The demo also includes a process that detects the end of the tour and re-enables the “Start tour” button. By waiting until scrolling finishes before re-enabling the button, duplicate execution during the tour can be avoided.

const endTour = async () => {
  // Omitted

  await window.scrollTo({
    behavior: "smooth",
    top: 0,
  });
  // Enable the button after scrolling
  startButton.disabled = false;
};

When scrolling is interrupted

With Programmatic Scroll Promises, if scrolling is interrupted, the Promise is resolved immediately and the next process always runs. In the guide tour, repeatedly pressing the “Next” button can result in multiple panels being highlighted.

To avoid this, use the value returned from the scroll method. When scrolling is interrupted, the returned value’s interrupted property becomes true, so you can branch the process and skip the next step.

const showStep = async (stepIndex) => {
  // Omitted
  const result = await window.scrollTo({
    behavior: "smooth",
    top: centerY,
  });

  if (result.interrupted) {
    // Do nothing if scrolling was interrupted
    return;
  }

  // Activate after scrolling
  target.classList.add("is-active");
};

Browser support

Programmatic Scroll Promises are available in Chrome and Edge 150 or later, as of July 2026.

Reference: Can I use…

Conclusion

This article introduced Programmatic Scroll Promises. Being able to control scroll completion with a Promise makes the code much simpler and removes the need for extra flags or event monitoring.

Support in browsers other than Chrome and Edge is expected in the future, and this feature may become a standard part of scroll-related implementation.

Share on social media
Your shares help us keep the site running.
Post on X
Copy URL
Share
KITAGAWA Kyoko

Started in apparel and office work, then transitioned into engineering. Joined ICS after full-stack experience in both backend and frontend development. Special skill: English.

Articles by this staff