205 lines
9.6 KiB
HTML
205 lines
9.6 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>滚动触底固定</title>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* 父容器:弹性布局,左右分栏 */
|
||
.matter {
|
||
display: flex;
|
||
width: 1200px;
|
||
margin: 0 auto;
|
||
gap: 20px; /* 左右间距 */
|
||
padding: 20px 0;
|
||
}
|
||
|
||
/* 左侧容器:高度较短(示例) */
|
||
.left {
|
||
width: 30%;
|
||
background: #f0f8fb;
|
||
padding: 20px;
|
||
border-radius: 8px;
|
||
/* 默认静态定位,滚动触底后改为 fixed */
|
||
position: static;
|
||
top: auto; /* 初始重置 */
|
||
bottom: 20px; /* 固定时距离底部的间距 */
|
||
}
|
||
|
||
/* 右侧容器:高度较长(示例) */
|
||
.right {
|
||
width: 70%;
|
||
background: #fef7fb;
|
||
padding: 20px;
|
||
border-radius: 8px;
|
||
position: static;
|
||
top: auto;
|
||
bottom: 20px;
|
||
}
|
||
|
||
/* 触底固定的样式 */
|
||
.fixed-bottom {
|
||
position: fixed;
|
||
/* 固定宽度(与默认布局一致) */
|
||
width: calc(30% - 10px); /* 30%宽度 - 一半gap(避免布局偏移) */
|
||
max-height: calc(100vh - 40px); /* 最大高度不超过视口(可选) */
|
||
overflow: hidden; /* 固定后不再滚动 */
|
||
}
|
||
|
||
/* 右侧固定时的宽度修正(与默认70%一致) */
|
||
.right.fixed-bottom {
|
||
width: calc(70% - 10px);
|
||
}
|
||
|
||
/* 页面足够长,确保能滚动 */
|
||
body {
|
||
min-height: 200vh;
|
||
background: #f8f9fa;
|
||
}
|
||
|
||
/* 示例内容样式 */
|
||
.content-item {
|
||
margin-bottom: 16px;
|
||
line-height: 1.6;
|
||
color: #333;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="matter">
|
||
<div class="left" id="leftContainer">
|
||
<!-- 左侧内容:较短高度 -->
|
||
<h3>左侧内容(较短)</h3>
|
||
<div class="content-item">这是左侧内容的第一行。</div>
|
||
<div class="content-item">左侧内容相对较短,所以在滚动时会先触底。</div>
|
||
<div class="content-item">当滚动到一定程度时,左侧会固定在底部。</div>
|
||
<div class="content-item">此时右侧内容可以继续正常滚动。</div>
|
||
<div class="content-item">固定后,左侧内容不再随页面滚动。</div>
|
||
<div class="content-item">这是左侧的最后一行内容。</div>
|
||
</div>
|
||
|
||
<div class="right" id="rightContainer">
|
||
<!-- 右侧内容:较长高度 -->
|
||
<h3>右侧内容(较长)</h3>
|
||
<div class="content-item">这是右侧内容的第一行。</div>
|
||
<div class="content-item">右侧内容相对较长,可以滚动更多内容。</div>
|
||
<div class="content-item">右侧内容继续...这是第3行。</div>
|
||
<div class="content-item">右侧内容继续...这是第4行。</div>
|
||
<div class="content-item">右侧内容继续...这是第5行。</div>
|
||
<div class="content-item">右侧内容继续...这是第6行。</div>
|
||
<div class="content-item">右侧内容继续...这是第7行。</div>
|
||
<div class="content-item">右侧内容继续...这是第8行。</div>
|
||
<div class="content-item">右侧内容继续...这是第9行。</div>
|
||
<div class="content-item">右侧内容继续...这是第10行。</div>
|
||
<div class="content-item">右侧内容继续...这是第11行。</div>
|
||
<div class="content-item">右侧内容继续...这是第12行。</div>
|
||
<div class="content-item">右侧内容继续...这是第13行。</div>
|
||
<div class="content-item">右侧内容继续...这是第14行。</div>
|
||
<div class="content-item">右侧内容继续...这是第15行。</div>
|
||
<div class="content-item">右侧内容继续...这是第16行。</div>
|
||
<div class="content-item">右侧内容继续...这是第17行。</div>
|
||
<div class="content-item">右侧内容继续...这是第18行。</div>
|
||
<div class="content-item">右侧内容继续...这是第19行。</div>
|
||
<div class="content-item">右侧内容继续...这是第20行。</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// 获取DOM元素
|
||
const leftContainer = document.getElementById('leftContainer');
|
||
const rightContainer = document.getElementById('rightContainer');
|
||
const matter = document.querySelector('.matter');
|
||
|
||
// 记录容器原始样式
|
||
const originalLeftStyles = {
|
||
position: leftContainer.style.position,
|
||
width: leftContainer.style.width,
|
||
marginLeft: leftContainer.style.marginLeft
|
||
};
|
||
|
||
const originalRightStyles = {
|
||
position: rightContainer.style.position,
|
||
width: rightContainer.style.width,
|
||
marginRight: rightContainer.style.marginRight
|
||
};
|
||
|
||
// 处理滚动事件的核心函数
|
||
function handleScroll() {
|
||
// 获取matter容器的位置和尺寸
|
||
const matterRect = matter.getBoundingClientRect();
|
||
const matterLeft = matterRect.left;
|
||
const windowHeight = window.innerHeight;
|
||
|
||
// 获取左右容器的位置信息
|
||
const leftRect = leftContainer.getBoundingClientRect();
|
||
const rightRect = rightContainer.getBoundingClientRect();
|
||
|
||
// 计算左侧容器底部与视口底部的距离
|
||
const leftBottomToViewportBottom = windowHeight - leftRect.bottom;
|
||
|
||
// 计算右侧容器底部与视口底部的距离
|
||
const rightBottomToViewportBottom = windowHeight - rightRect.bottom;
|
||
|
||
// 左侧容器触底检测:当左侧底部即将离开视口(小于0)且容器完全可见时,固定左侧
|
||
if (leftBottomToViewportBottom < 20 && leftRect.top <= 0 && !leftContainer.classList.contains('fixed-bottom')) {
|
||
// 固定左侧,保持原位置水平对齐
|
||
leftContainer.classList.add('fixed-bottom');
|
||
leftContainer.style.left = `${matterLeft}px`;
|
||
leftContainer.style.bottom = '20px';
|
||
} else if (leftRect.top > 0 && leftContainer.classList.contains('fixed-bottom')) {
|
||
// 当左侧容器顶部重新进入视口时,取消固定
|
||
leftContainer.classList.remove('fixed-bottom');
|
||
// 恢复原始样式
|
||
Object.assign(leftContainer.style, originalLeftStyles);
|
||
}
|
||
|
||
// 右侧容器触底检测:当右侧底部即将离开视口(小于0)且容器完全可见时,固定右侧
|
||
if (rightBottomToViewportBottom < 20 && rightRect.top <= 0 && !rightContainer.classList.contains('fixed-bottom')) {
|
||
// 固定右侧,保持原位置水平对齐
|
||
rightContainer.classList.add('fixed-bottom');
|
||
rightContainer.style.right = `${window.innerWidth - (matterLeft + matterRect.width)}px`;
|
||
rightContainer.style.bottom = '20px';
|
||
} else if (rightRect.top > 0 && rightContainer.classList.contains('fixed-bottom')) {
|
||
// 当右侧容器顶部重新进入视口时,取消固定
|
||
rightContainer.classList.remove('fixed-bottom');
|
||
// 恢复原始样式
|
||
Object.assign(rightContainer.style, originalRightStyles);
|
||
}
|
||
}
|
||
|
||
// 窗口大小变化时的处理
|
||
function handleResize() {
|
||
// 重置所有固定状态
|
||
if (leftContainer.classList.contains('fixed-bottom')) {
|
||
leftContainer.classList.remove('fixed-bottom');
|
||
Object.assign(leftContainer.style, originalLeftStyles);
|
||
}
|
||
|
||
if (rightContainer.classList.contains('fixed-bottom')) {
|
||
rightContainer.classList.remove('fixed-bottom');
|
||
Object.assign(rightContainer.style, originalRightStyles);
|
||
}
|
||
|
||
// 重新触发滚动处理
|
||
handleScroll();
|
||
}
|
||
|
||
// 初始化
|
||
window.addEventListener('DOMContentLoaded', () => {
|
||
// 立即执行一次以初始化
|
||
handleScroll();
|
||
// 监听滚动事件
|
||
window.addEventListener('scroll', handleScroll);
|
||
// 监听窗口大小变化事件
|
||
window.addEventListener('resize', handleResize);
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|