背景
未超过一屏时,底部按钮跟随内容
超过一屏时,底部按钮固定在底部
原理
我们需要同时监听DOM变动函数以及窗口大小变化,大于一屏时,添加底部按钮fixed布局
小于一屏时移除
实现
import React, { useState, useRef, useEffect, useCallback } from "react";
import throttle from "lodash/throttle";
import cx from "classnames";
import "./index.scss";
interface IProps {
content: React.ReactNode;
footer: React.ReactNode;
containerClassName?: string;
contentClassName?: string;
footerClassName?: string;
throttleTime?: number;
}
const FloatFooter: React.FC<IProps> = (props) => {
const {
content,
footer,
containerClassName,
contentClassName,
footerClassName,
throttleTime = 500,
} = props;
const containerRef = useRef<HTMLDivElement>(null);
const contentRef = useRef<HTMLDivElement>(null);
const footerRef = useRef<HTMLDivElement>(null);
const [fixed, setFixed] = useState<boolean>(false);
const [footerHeight, setFooterHeight] = useState<number>(0);
const handleHeightChange = useCallback(
throttle(
() => {
const contentElement = contentRef.current;
const footerElement = footerRef.current;
if (!contentElement || !footerElement) return;
const contentElementHeight = contentElement.clientHeight;
const viewportHeight = document.documentElement.clientHeight;
if (contentElementHeight > viewportHeight) {
setFixed(true);
setFooterHeight(footerElement.clientHeight);
} else {
setFixed(false);
setFooterHeight(0);
}
},
throttleTime,
{ trailing: false }
),
[]
);
useEffect(() => {
handleHeightChange();
window.addEventListener("resize", handleHeightChange);
const observer = new MutationObserver(handleHeightChange);
const observerConfig = { childList: true, subtree: true };
if (containerRef.current) {
observer.observe(containerRef.current, observerConfig);
}
return () => {
window.removeEventListener("resize", handleHeightChange);
observer.disconnect();
};
}, []);
return (
<article
className={cx("i-container", containerClassName)}
ref={containerRef}
>
<section className={cx("i-content", contentClassName)} ref={contentRef}>
{content}
{fixed && <div style={{ height: `${footerHeight}px` }}></div>}
</section>
<footer
className={cx("i-footer", footerClassName, { "i-fixed-footer": fixed })}
ref={footerRef}
>
{footer}
</footer>
</article>
);
};
export default FloatFooter;
.i-fixed-footer { position: fixed; left: 0; right: 0; bottom: 0; }