# How the scroll-to.js Utility Implements Smooth Scrolling to Anchor Elements in Vue-Element-Admin

> Discover how scroll-to.js in vue-element-admin creates smooth scrolling to anchor elements using a quadratic ease-in-out function and requestAnimationFrame for seamless page navigation.

- Repository: [花裤衩/vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
- Tags: how-to-guide
- Published: 2026-02-27

---

**The [`scroll-to.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/scroll-to.js) utility achieves smooth scrolling by combining a quadratic ease-in-out easing function with a `requestAnimationFrame` loop that incrementally updates the scroll position across all possible browser scroll containers.**

The [`scroll-to.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/scroll-to.js) module in the [PanJiaChen/vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) repository provides a lightweight, cross-browser solution for animating scroll movements to anchor elements. Unlike native CSS `scroll-behavior`, this utility offers precise control over duration, easing curves, and completion callbacks, making it ideal for Vue.js applications that require programmatic navigation.

## Core Implementation in scroll-to.js

The entire logic resides in [`src/utils/scroll-to.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/utils/scroll-to.js), where the utility handles browser inconsistencies through a series of helper functions before executing the main animation loop.

### Quadratic Easing with Math.easeInOutQuad

The utility extends the global `Math` object with a custom easing function that calculates the interpolated scroll position using a quadratic ease-in-out algorithm:

```javascript
Math.easeInOutQuad = function(t, b, c, d) {
  t /= d / 2
  if (t < 1) {
    return c / 2 * t * t + b
  }
  t--
  return -c / 2 * (t * (t - 2) - 1) + b
}

```

This function accepts the current time (`t`), start value (`b`), total change (`c`), and duration (`d`), returning the eased position that creates the characteristic smooth acceleration and deceleration effect.

### Cross-Browser Animation Frame Shim

To ensure compatibility with older browsers, the utility creates a `requestAnimFrame` shim that falls back to `setTimeout` when the native API is unavailable:

```javascript
const requestAnimFrame = (function() {
  return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
})()

```

This maintains approximately 60 frames per second regardless of browser capabilities.

### Unified Scroll Position Management

Different browsers expose the scroll position on different DOM elements. The utility solves this by writing to and reading from three possible locations simultaneously:

**The `move` function** updates all potential scroll containers:

```javascript
function move(amount) {
  document.documentElement.scrollTop = amount
  document.body.parentNode.scrollTop = amount
  document.body.scrollTop = amount
}

```

**The `position` function** retrieves the current scroll offset by checking the same elements:

```javascript
function position() {
  return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
}

```

This approach ensures the animation works consistently across WebKit, Gecko, and legacy Internet Explorer environments.

### The scrollTo Function and Animation Loop

The main `scrollTo` function orchestrates the animation by calculating initial values and executing a recursive loop:

```javascript
export function scrollTo(to, duration, callback) {
  const start = position()
  const change = to - start
  const increment = 20
  let currentTime = 0
  
  const animateScroll = function() {
    currentTime += increment
    const val = Math.easeInOutQuad(currentTime, start, change, duration)
    move(val)
    
    if (currentTime < duration) {
      requestAnimFrame(animateScroll)
    } else {
      if (callback && typeof callback === 'function') {
        callback()
      }
    }
  }
  
  animateScroll()
}

```

The function accepts three parameters: the target scroll position (`to`), the animation duration in milliseconds (`duration`), and an optional `callback` executed upon completion. The animation increments every 20 milliseconds, applying the eased value to all scroll containers until the duration elapses.

## Practical Usage Examples

### Scrolling to a Specific Element

To scroll to an anchor element manually, calculate its offset and invoke the utility:

```javascript
import { scrollTo } from '@/utils/scroll-to'

function scrollToHeader() {
  const header = document.getElementById('section-header')
  if (header) {
    // Scroll to element's top position over 800ms with completion callback
    scrollTo(header.offsetTop, 800, () => {
      console.log('Smooth scroll completed')
    })
  }
}

```

### Integrating with Vue Router Hash Navigation

The utility commonly handles hash-based navigation in Vue Router guards:

```javascript
import { scrollTo } from '@/utils/scroll-to'

router.afterEach((to, from) => {
  if (to.hash) {
    const target = document.querySelector(to.hash)
    if (target) {
      // Animate to hash target over 600ms
      scrollTo(target.offsetTop, 600)
    }
  }
})

```

This pattern ensures that clicking anchor links triggers the smooth animation rather than instantaneous jumps.

## Summary

- The [`scroll-to.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/scroll-to.js) utility in [`src/utils/scroll-to.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/utils/scroll-to.js) provides a framework-agnostic smooth scrolling solution for Vue-element-admin.
- It implements **quadratic ease-in-out easing** through `Math.easeInOutQuad` to create natural acceleration and deceleration curves.
- A **cross-browser shim** ensures 60fps animation via `requestAnimFrame`, falling back to `setTimeout` for older browsers.
- The utility writes to **three scroll containers simultaneously** (`documentElement`, `body.parentNode`, and `body`) to handle browser inconsistencies.
- The main `scrollTo(to, duration, callback)` function accepts a target position, duration in milliseconds, and optional completion callback.

## Frequently Asked Questions

### What easing function does scroll-to.js use?

The utility uses a **quadratic ease-in-out** algorithm defined as `Math.easeInOutQuad`. This function calculates intermediate scroll positions by interpolating between the start and target values using a parabolic curve, creating smooth acceleration at the beginning and deceleration at the end of the animation.

### How does scroll-to.js handle cross-browser compatibility?

The module addresses browser differences through two primary mechanisms. First, it provides a `requestAnimFrame` shim that falls back to `setTimeout` when native animation frames are unavailable. Second, it reads from and writes to three potential scroll containers—`document.documentElement`, `document.body.parentNode`, and `document.body`—ensuring the animation works regardless of which element exposes the scroll position in a given browser.

### Can I use scroll-to.js without Vue Router?

Yes, the utility is completely framework-agnostic. While commonly integrated with Vue Router navigation guards, you can import and invoke `scrollTo` from any JavaScript context. Simply pass the target pixel offset (often obtained via `element.offsetTop`), the desired duration in milliseconds, and an optional callback function to execute upon completion.

### What is the default duration if not specified?

The `scrollTo` function requires an explicit duration parameter; there is no hardcoded default value within the utility itself. When calling the function, you must provide the duration in milliseconds (for example, `800` for 800ms). If you need a default behavior in your application, you should wrap the utility in a helper function that supplies a fallback value before invoking `scrollTo`.