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

vanilla-calendar.pro弹窗日历不随滚动容器同步滚动问题求助

问题:Vanilla Calendar Pro弹窗无法随滚动容器同步滚动

我正在使用Vanilla Calendar Pro日历组件,实现代码如下:

JavaScript 代码

document.addEventListener('DOMContentLoaded', () => {
  const calendar = new VanillaCalendar('#calendar', {
    // Settings
    input: true,
    actions: {
      changeToInput(e, self) {
        if (!self.HTMLInputElement) return;
        if (self.selectedDates[0]) {
          self.HTMLInputElement.innerHTML = self.selectedDates[0];
          // 选择日期后隐藏日历
          self.hide();
        } else {
          self.HTMLInputElement.textContent = '';
        }
      },
    },
    settings: {
      visibility: {
        positionToInput: 'center',
      },
    },
  });
  calendar.init();
});

HTML 代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link href="https://cdn.jsdelivr.net/npm/vanilla-calendar-pro/build/vanilla-calendar.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/vanilla-calendar-pro/build/vanilla-calendar.min.js" defer></script>
    <title>test page</title>
</head>

<body>
    <h1 style =" font-size: 40px;" >Test Project</h1>

    <div style="height: 50rem; background-color: rgb(182, 142, 215); overflow-y: scroll; display: flex; justify-content: center; flex-direction: column;">
        
        <div style="margin-bottom: 90rem;" > some components 1</div>
        
        <input id="calendar" type="text" placeholder="Choose date" readonly />
        
        <div style="margin-bottom: 90rem;" > some components 2</div>

    </div>
 
  </body>
</html>

问题描述

点击输入框唤起日历弹窗后,滚动设置了overflow-y:scroll的内部div时,日历弹窗无法随容器同步滚动。尝试过将弹窗包裹在div中、调整position和z-index样式属性,均未解决问题。


解决方案

方案1:绑定弹窗到滚动容器内部

Vanilla Calendar Pro默认将弹窗挂载到body下,脱离了滚动容器的上下文。我们可以通过配置让弹窗挂载到滚动容器内,并监听滚动事件更新位置:

  1. 修改初始化配置,指定弹窗容器并添加滚动监听:
document.addEventListener('DOMContentLoaded', () => {
  const scrollContainer = document.querySelector('div[style*="overflow-y:scroll"]');
  const calendar = new VanillaCalendar('#calendar', {
    input: true,
    // 将弹窗挂载到滚动容器内
    container: scrollContainer,
    actions: {
      changeToInput(e, self) {
        if (!self.HTMLInputElement) return;
        if (self.selectedDates[0]) {
          self.HTMLInputElement.innerHTML = self.selectedDates[0];
          self.hide();
        } else {
          self.HTMLInputElement.textContent = '';
        }
      },
      // 初始化时添加滚动监听,实时更新弹窗位置
      init(self) {
        scrollContainer.addEventListener('scroll', () => {
          if (self.isVisible) {
            self.updatePosition();
          }
        });
      }
    },
    settings: {
      visibility: {
        positionToInput: 'center',
        // 弹窗使用绝对定位(相对于滚动容器)
        position: 'absolute'
      },
    },
  });
  calendar.init();
});
  1. 给滚动容器添加相对定位:
    绝对定位需要依赖相对定位的父容器,修改滚动容器的样式:
<div style="height: 50rem; background-color: rgb(182, 142, 215); overflow-y: scroll; display: flex; justify-content: center; flex-direction: column; position: relative;">

方案2:手动计算并同步弹窗位置

如果方案1不生效,可直接监听滚动事件,手动计算输入框的相对位置来更新弹窗:

document.addEventListener('DOMContentLoaded', () => {
  const scrollContainer = document.querySelector('div[style*="overflow-y:scroll"]');
  const calendarInput = document.getElementById('calendar');
  const calendar = new VanillaCalendar('#calendar', {
    input: true,
    actions: {
      changeToInput(e, self) {
        if (!self.HTMLInputElement) return;
        if (self.selectedDates[0]) {
          self.HTMLInputElement.innerHTML = self.selectedDates[0];
          self.hide();
        } else {
          self.HTMLInputElement.textContent = '';
        }
      },
      // 弹窗显示时添加滚动监听并初始化位置
      show(self) {
        updateCalendarPosition(self);
        scrollContainer.addEventListener('scroll', () => updateCalendarPosition(self));
      },
      // 弹窗隐藏时移除监听
      hide(self) {
        scrollContainer.removeEventListener('scroll', () => updateCalendarPosition(self));
      }
    },
    settings: {
      visibility: {
        positionToInput: 'center',
      },
    },
  });
  calendar.init();

  // 自定义更新弹窗位置的函数
  function updateCalendarPosition(calendarInstance) {
    const inputRect = calendarInput.getBoundingClientRect();
    const containerRect = scrollContainer.getBoundingClientRect();
    // 计算输入框相对于滚动容器的坐标
    const relativeTop = inputRect.top - containerRect.top + scrollContainer.scrollTop;
    const relativeLeft = inputRect.left - containerRect.left;
    
    // 同步弹窗位置(保持居中对齐输入框)
    calendarInstance.HTMLElement.style.top = `${relativeTop + inputRect.height + 10}px`;
    calendarInstance.HTMLElement.style.left = `${relativeLeft + (inputRect.width / 2) - (calendarInstance.HTMLElement.offsetWidth / 2)}px`;
  }
});

同时通过CSS强制弹窗使用绝对定位:

.vanilla-calendar {
  position: absolute !important;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 04:46:18