<!DOCTYPE html> 
<html lang=\"zh-cn\">
<head> 
<  charset=\"UTF-8\"> 
< >Pull to Refresh</ >
<style>
* {
	margin: 0;
	padding: 0;
}
.scroll-container {
	position: relative;
	/** 拖动到最下面的时候滚动条会突然消失,造成页面resize,所以通过overflow隐藏滚动条 */
	overflow-y: hidden;
	/** height不能设置为100%,因为它是根据内容而定的,而100vh表示浏览器满高度 */
	height: 100vh;
}
.scroll-container>ul {
	list-style: none;
	text-align: center;
	line-height: 300%;
	color: white;
	position: absolute;
	width: 100%;
	background-color: #999;
}
.scroll-container>ul>li {
	border: 1px solid white;
}
.scroll-container>div:first-child, .scroll-container>div:last-child {
	justify-content: center;
	align-items: center;
	color: black;
	background-color: red;
	position: absolute;
	/** absolute的元素,如果不设置宽度,即使是block的也会变成wrap_content */
	width: 100%;
	overflow-y: hidden;
}
</style> 
< >
function fullData(count, type) {
	console.log(\"refreshType: \" + type);
	const container = document.querySelector(\".scroll-container>ul\");
	for(let i=0; i<count; i++) {
		const node = document.createElement(\"li\");
		node.textContent = \"第 \" + container.children.length + \" 条测试数据\";
		container.appendChild(node);
	}
}
class PullToRefresh {
	constructor(scrollContainer, callback) {
		const container = scrollContainer.firstElementChild;
		const contentBefore = document.createElement(\"div\");
		const contentEnd = document.createElement(\"div\");
		scrollContainer.insertBefore(contentBefore, scrollContainer.firstChild);
		scrollContainer.appendChild(contentEnd);
		
		this.container = container;
		container.tips = [\"Pull Down To Refresh\", \"Pull Up To Refresh\", \"Release To Refresh\", \"Loading...\", \"All Data Has Been Loaded\"];
		//拉伸出多大的空间说明刷新有效
		container.effectDistance = 80;
		//拉伸出的空间的极限值
		container.tensileLimit = 100;
		//0表示不刷新,1表示下拉刷新,2表示上拉刷新
		container.refreshType = 0;
		//每次滚动的距离
		container.scrollStep = 20;
		container.pullToRefreshCallback = callback;
		container.siblingPrev = contentBefore;
		container.siblingNext = contentEnd;
		this.setListener(container);
	}
	setListener(container) {
		container.addEventListener(\'touchstart\', this.touchStart, false);
		container.addEventListener(\'touchmove\', this.touchMove, false);
		container.addEventListener(\'touchend\', this.touchEndFunc, false);
		container.addEventListener(\'mousedown\', this.touchStart, false);
		container.addEventListener(\'mousemove\', this.touchMove, false);
		container.addEventListener(\'mouseup\', this.touchEnd, false);
		//火狐使用DOMMouseScroll,其它浏览器使用mousewheel事件
		container.addEventListener(\"DOMMouseScroll\", this.scroll, false);
		container.  = this.scroll;
	}
	touchStart(event) {
		/*
		event.button
		其它浏览器:0鼠标左键;1鼠标中键;2鼠标右键
		IE浏览器: 1鼠标左键;4鼠标中键;2鼠标右键
		*/
		if(event.button === 0) {
			const container = event.currentTarget;
			container.isTouch = true;
			container.startY = event.y;
			container.startTop = container.offsetTop;
			container.refreshType = 0;
		}
	}
	touchMove(event) {
		const container = event.currentTarget;
		if(event.button === 0 && container.isTouch) {
			//从鼠标左键按下的点开始计算的下拉的距离
			const diff = event.y - container.startY;
			//容器与页面左上角的相对位移
			const containerTop = container.startTop + diff;
			if(diff > 0) {
				//下拉
				if(containerTop > container.tensileLimit) {
					return;
				} else if(containerTop > container.effectDistance) {
					container.siblingPrev.textContent = container.tips[2];
					container.siblingPrev.style.height = containerTop + \"px\";
					container.refreshType = 1;
				} else if(containerTop > 0) {
					container.siblingPrev.textContent = container.tips[0];
					container.siblingPrev.style.display = \"flex\";
					container.siblingPrev.style.height = containerTop + \"px\";
					container.refreshType = 0;
				} else {
					container.siblingPrev.style.display = \"none\";
					container.refreshType = 0;
				}
				container.style.top = containerTop + \"px\";
				container.siblingNext.style.display = \"none\";
			} else {
				//上拉
				/*
				container.clientHeight这个是容器完整的高度(包括不可见部分)
				document.documentElement.clientHeight这个是窗口实际高度
				*/
				//超出屏幕可见区域的高度值(正数)
				const exceedHeight = container.clientHeight - document.documentElement.clientHeight;
				//下面这个逻辑有点复杂,可以先分开写,然后寻找共同点并合并
				/*
				if(exceedHeight < 0) {
					//小于0表示内容未填满整个窗口
					if(containerTop + container.tensileLimit < 0) {
						return;
					} else if(containerTop + container.effectDistance < 0) {
						container.siblingNext.textContent = container.tips[2];
						container.siblingNext.style.height = -containerTop + \"px\";
						container.siblingNext.style.top = containerTop + container.clientHeight + \"px\";
						container.refreshType = 2;
					} else if(containerTop < 0) {
						container.siblingNext.textContent = container.tips[1];
						container.siblingNext.style.display = \"flex\";
						container.siblingNext.style.height = -containerTop + \"px\";
						container.siblingNext.style.top = containerTop + container.clientHeight + \"px\";
						container.refreshType = 0;
					} else {
						container.siblingNext.style.display = \"none\";
						container.refreshType = 0;
					}
				} else {
					if(containerTop + exceedHeight + container.tensileLimit < 0) {
						return;
					} else if(containerTop + exceedHeight + container.effectDistance < 0) {
						container.siblingNext.textContent = container.tips[2];
						container.siblingNext.style.height = containerTop + exceedHeight + \"px\";
						container.siblingNext.style.top = containerTop + container.clientHeight + \"px\";
						container.refreshType = 2;
					} else if(containerTop + exceedHeight < 0) {
						container.siblingNext.textContent = container.tips[1];
						container.siblingNext.style.display = \"flex\";
						container.siblingNext.style.height = containerTop + exceedHeight + \"px\";
						container.siblingNext.style.top = containerTop + container.clientHeight + \"px\";
						container.refreshType = 0;
					} else {
						container.siblingNext.style.display = \"none\";
						container.refreshType = 0;
					}
				}
				*/
				const tmpExceedHeight = exceedHeight < 0 ? 0 : exceedHeight;
				if(containerTop + tmpExceedHeight + container.tensileLimit < 0) {
					return;
				} else if(containerTop + tmpExceedHeight + container.effectDistance < 0) {
					container.siblingNext.textContent = container.tips[2];
					container.siblingNext.style.height = -containerTop - exceedHeight + \"px\";
					container.siblingNext.style.top = containerTop + container.clientHeight + \"px\";
					container.refreshType = 2;
				} else if(containerTop + tmpExceedHeight < 0) {
					container.siblingNext.textContent = container.tips[1];
					container.siblingNext.style.display = \"flex\";
					container.siblingNext.style.height = -containerTop - exceedHeight + \"px\";
					container.siblingNext.style.top = containerTop + container.clientHeight + \"px\";
					container.refreshType = 0;
				} else {
					container.siblingNext.style.display = \"none\";
					container.refreshType = 0;
				}
				
				container.style.top = containerTop + \"px\";
				container.siblingPrev.style.display = \"none\";
			}
		}
	}
	touchEnd(event) {
		if(event.button == 0) {
			const container = event.currentTarget;
			container.isTouch = false;
			if(container.offsetTop > 0) {
				container.style.top = \"0\";
			} else {
				const containerTop = container.clientHeight - document.documentElement.clientHeight;
				if(containerTop < 0) {
					//内容未填满整个窗口
					container.style.top = \"0\";
				} else if(container.offsetTop + containerTop < 0) {
					container.style.top = -containerTop + \"px\";
				}
			}
			container.siblingPrev.style.display = \"none\";
			container.siblingNext.style.display = \"none\";
			if(container.refreshType != 0) {
				container.pullToRefreshCallback(container.refreshType);
			}
		}
	}
	scroll(event) {
		const container = event.currentTarget;
		const containerTop = container.clientHeight - document.documentElement.clientHeight;
		if(containerTop < 0 || container.isTouch) {
			//小于0表示内容未填满整个窗口
			return;
		}
		const minTop = document.documentElement.clientHeight - container.clientHeight;
		let top = 0;
		//firefox用的是event.detail,+3表示为向下滚动,-3表示向上滚动;其它浏览器用的是wheelDelta,+120表示向上,-120表示向下
		if((event.detail && event.detail > 0) || (event.wheelDelta && event.wheelDelta < 0)) {
			top = container.offsetTop - container.scrollStep;
		} else {
			top = container.offsetTop + container.scrollStep;
		}
		top = Math.min(0, Math.max(minTop, top));
		container.style.top = top + \"px\";
	}
}
window.  = ()=>{
	fullData(5, 0);
	const scrollContainer = document.querySelector(\'.scroll-container\');
	new PullToRefresh(scrollContainer, (type)=>fullData(5, type));
}
</ >
</head>
<body>
<div class=\"scroll-container\"><ul></ul></div>
</body> 
</html>
收藏 打印