You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Nuxt 3中创建带URL索引的灯箱模态框且打开时不丢失滚动位置

Nuxt模态框打开时父页面滚动位置重置问题

核心冲突需求

  • 打开模态框时保持父页面滚动位置不变
  • 打开模态框时修改URL(支持通过URL直接进入模态框状态)

当前采用pages目录自动生成嵌套路由的方案,模态框功能正常,但存在一个问题:打开模态框时父页面的滚动位置会被重置到顶部;关闭模态框时滚动位置能正常保留(符合预期)。

问题复现步骤

  1. 将/pages/index.vue中的列表项复制约50次,让页面出现滚动条
  2. 访问根路径/,向下滚动页面后点击任意Thing链接
  3. 模态框弹出,但父页面滚动位置被重置到顶部
  4. 模态框打开状态下滚动页面,实际滚动的是父页面
  5. 点击模态框的关闭按钮,父页面滚动位置保留(符合预期)

相关代码

/pages/index.vue

<template>
  <ol>
    <li><NuxtLink :href="'/session/1'">Thing 1</NuxtLink></li>
    <li><NuxtLink :href="'/session/2'">Thing 2</NuxtLink></li>
    <li><NuxtLink :href="'/session/3'">Thing 3</NuxtLink></li>
    <li><NuxtLink :href="'/session/4'">Thing 4</NuxtLink></li>
    <li><NuxtLink :href="'/session/5'">Thing 5</NuxtLink></li>
    <!-- 复制多次直至页面出现滚动条 -->
  </ol>
  <LazyNuxtPage />
</template>

/pages/index/session/[id].vue

<script setup lang="ts">
import { useRoute, useRouter } from 'vue-router';

const route = useRoute();
const router = useRouter();
</script>

<template>
  <div :style="{ position: 'fixed', top: '16px', right: '16px', backgroundColor: '#cccccc', padding: '8px' }">
    Thing {{ route.params.id }}.
    <button @click="router.push('/')">(Close overlay)</button>
  </div>
</template>

解决方案

方法1:自定义路由滚动行为(推荐)

在nuxt.config.ts中配置scrollBehavior,针对模态框路由特殊处理,保留父页面滚动位置:

export default defineNuxtConfig({
  router: {
    scrollBehavior(to, from, savedPosition) {
      // 从根页面进入模态框路由时,保留当前滚动位置
      if (from.path === '/' && to.path.startsWith('/session/')) {
        return { left: window.scrollX, top: window.scrollY };
      }
      // 其他场景遵循默认行为,关闭模态框时用savedPosition恢复
      return savedPosition || { top: 0 };
    },
  },
});

方法2:父页面手动保存/恢复滚动位置

在index.vue中监听路由变化,打开模态框前保存滚动位置,必要时恢复:

<template>
  <ol>
    <li><NuxtLink :href="'/session/1'">Thing 1</NuxtLink></li>
    <!-- 其他列表项 -->
  </ol>
  <LazyNuxtPage />
</template>

<script setup lang="ts">
import { useRoute } from 'vue-router';
import { ref, watch } from 'vue';

const route = useRoute();
const savedScrollTop = ref(0);

// 进入模态框路由时保存当前滚动位置
watch(() => route.path, (newPath) => {
  if (newPath.startsWith('/session/')) {
    savedScrollTop.value = window.scrollY;
  }
});

// 直接访问模态框URL后返回根页面时,恢复滚动位置
watch(() => route.path, (newPath) => {
  if (newPath === '/' && savedScrollTop.value > 0) {
    // 延迟执行确保DOM已更新
    setTimeout(() => window.scrollTo(0, savedScrollTop.value), 0);
  }
});
</script>

方法3:模态框页面主动恢复滚动

在模态框页面挂载时,恢复之前保存的滚动位置(需配合父页面保存逻辑):

<script setup lang="ts">
import { useRoute, useRouter } from 'vue-router';
import { onMounted } from 'vue';

const route = useRoute();
const router = useRouter();

onMounted(() => {
  // 读取父页面保存的滚动位置并恢复
  const scrollTop = sessionStorage.getItem('parentScrollTop');
  if (scrollTop) {
    window.scrollTo(0, parseInt(scrollTop));
    sessionStorage.removeItem('parentScrollTop');
  }
});
</script>

<template>
  <div :style="{ position: 'fixed', top: '16px', right: '16px', backgroundColor: '#cccccc', padding: '8px' }">
    Thing {{ route.params.id }}.
    <button @click="router.push('/')">(Close overlay)</button>
  </div>
</template>

同时在父页面的NuxtLink点击事件中保存滚动位置:

<!-- 在index.vue的列表项中修改 -->
<li>
  <NuxtLink 
    :href="'/session/1'" 
    @click="sessionStorage.setItem('parentScrollTop', window.scrollY.toString())"
  >
    Thing 1
  </NuxtLink>
</li>

内容的提问来源于stack exchange,提问作者Jessica Knight

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 05:37:03