使用JavaScript实现无限滚动
在本教程中,我们将看到一个使用无限滚动方法分解页面内容的简单实现。我们将使用 HTML、CSS 和 vanilla JavaScript 来构建无限滚动功能的高性能且可访问的版本。 什么是无限滚动?
无限滚动是一种用于在用户滚动到页面末尾时在页面上动态加载更多内容的功能。
无限滚动的概念用于以一种用户感觉"无缝"的方式从服务器加载数据,但不会因为一次请求太多数据而使服务器过载。
在之前的教程中,我们实现了分页功能,它允许我们将内容分解为称为页面的可导航部分。本教程将使用类似的实现。 原生 JavaScript 的好处
使用 JavaScript 的一个显着好处是我们的实现与框架无关,即它不依赖于任何框架,因此可以对其进行修改以在所有框架上工作。
此外,由于我们自己构建功能而不依赖于插件,因此我们可以确保实现是轻量级的并且完全适合我们的需求。
这是最终产品的外观,滚动到笔的底部以加载更多内容: 1.用 HTML 标记
我们将从在页面上放置卡片的容器开始。我们将使用 JavaScript 将卡片添加到容器中,因此 p 将为空。
我们还有一个加载器 p ,用于在添加下一批卡片之前显示动画,以及 p 用于显示卡片数量和卡片总数的卡片操作。
Showing
of
cards
加载器和卡片计数 p 的外观 2.使用 CSS 进行样式设置
我们将添加到 card-container p 中的卡片将有一个类名"card"。
#card-container {
display: flex;
flex-wrap: wrap;
}
.card {
height: 55vh;
width: calc((100% / 3) - 16px);
margin: 8px;
border-radius: 3px;
transition: all 200ms ease-in-out;
display: flex;
align-items: center;
justify-content: center;
}
.card:hover {
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.card-actions {
margin: 8px;
padding: 16px 0;
display: flex;
justify-content: space-between;
align-items: center;
}
我们还将通过动画 ::after 伪选择器为加载器 p 中的骨架卡创建加载动画:
#loader {
display: flex;
}
.skeleton-card {
height: 55vh;
width: calc((100% / 3) - 16px);
margin: 8px;
border-radius: 3px;
transition: all 200ms ease-in-out;
position: relative;
background-color: #eaeaea;
}
.skeleton-card::after {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
transform: translateX(-100%);
background-image: linear-gradient(90deg, rgba(255, 255, 255, 0) 0, rgba(255, 255, 255, 0.2) 20%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0));
animation: load 1s infinite;
}
@keyframes load {
100% {
transform: translateX(100%);
}
}
无障碍样式
每当我们在网页上包含动画时,重要的是要考虑可访问性的影响。有些用户可能更喜欢根本没有动画,我们可以通过使用媒体规则在样式中考虑这种偏好, prefers-reduced-motion
@media screen and (prefers-reduced-motion: reduce) {
.skeleton-card::after {
animation: none;
}
}
3.JavaScript 的功能
让我们分解无限滚动背后的逻辑。 定义要在页面上加载的内容的限制。 检测用户何时到达内容容器的末尾。 到达容器末尾后加载更多内容。 如果没有更多内容要加载,请停止无限滚动。 定义常量
首先,让我们从 DOM 中获取我们需要的所有元素:
const cardContainer = document.getElementById("card-container");
const cardCountElem = document.getElementById("card-count");
const cardTotalElem = document.getElementById("card-total");
const loader = document.getElementById("loader");
现在我们需要定义我们的全局变量。
我们需要一个要添加到页面的最大卡片数量的值。如果您从服务器获取数据,则此值是服务器响应的长度。让我们初始化一个 99 的卡片限制。
const cardLimit = 99;
cardTotalElem 是用于在页面上显示最大卡片数量的元素,因此我们可以将 设置为 innerHTML 值 cardLimit ;
cardTotalElem.innerHTML = cardLimit;
然后我们将定义一个变量来表示要增加页面的卡片数量:
const cardIncrease = 9;
我们想知道我们将拥有多少"页面",即我们可以增加多少次内容,直到达到最大限制。例如,使用我们定义的 cardLimit 和 cardIncrease 变量,我们可以将内容增加 10 倍(假设我们已经加载了前 9 个元素),直到达到限制。我们将通过除以 来做到这 cardLimit 一点 cardIncrease 。
const pageCount = Math.ceil(cardLimit / cardIncrease);
然后我们将定义一个值来确定我们在哪个页面上:
let currentPage = 1;
创建新卡
现在我们有了所有的常量,让我们创建一个函数来向卡片容器中添加一张新卡片。我们将 innerHTML 卡片的 设置为索引值,这样我们就可以跟踪我们正在添加的卡片数量。
此演示中的一个有趣功能是每张卡片都有随机生成的背景颜色。 如何使用 JavaScript 生成随机背景颜色
const getRandomColor = () => {
const h = Math.floor(Math.random() * 360);
return `hsl(${h}deg, 90%, 85%)`;
};
const createCard = (index) => {
const card = document.createElement("p");
card.className = "card";
card.innerHTML = index;
card.style.backgroundColor = getRandomColor();
cardContainer.appendChild(card);
};
将卡片添加到容器中
现在我们将使用与分页教程类似的功能将卡片添加到容器中。
首先,确定要添加到页面的卡片范围。该 addCards 函数将接受一个 pageIndex 参数,该参数将更新全局 currentPage 值。如果我们在第 1 页,我们将添加卡片 1 到 9。如果我们在第 2 页,我们将添加卡片 10 到 18,依此类推。
我们可以在数学上将其定义为:
const addCards = (pageIndex) => {
currentPage = pageIndex;
const startRange = (pageIndex - 1) * cardIncrease;
const endRange = pageIndex * cardIncrease;
for (let i = startRange + 1; i <= currRange; i++) {
createCard(i);
}
};
在这个函数中,我们的开始范围总是比我们试图获得的值小一(即在第 1 页,开始范围是 0,在第 2 页,开始范围是 9)所以我们将考虑这一点通过将我们的 for 循环索引的值设置为 startRange + 1 . 检测何时达到卡限制
我们必须注意的一个限制是 endRange 数量。如果我们在最后一页,我们希望结束范围与 cardLimit . 例如,如果我们有 cardLimit 75 和 cardIncrease 10 的 a 并且我们在第 8 页,我们的起始索引将是 70,我们的 endRange 值应该是 75。
我们将修改我们的 addCards 函数来解决这个问题:
const addCards = (pageIndex) => {
currentPage = pageIndex;
const startRange = (pageIndex - 1) * cardIncrease;
const endRange = currentPage == pageCount ? cardLimit : pageIndex * cardIncrease;
for (let i = startRange + 1; i <= endRange; i++) {
createCard(i);
}
};
我们的演示还包括一个 cardTotal 元素,该元素显示页面上当前显示的卡片数量,因此我们 innerHTML 将此元素的 设置为结束范围。
const addCards = (pageIndex) => {
currentPage = pageIndex;
const startRange = (pageIndex - 1) * cardIncrease;
const endRange = currentPage == pageCount ? cardLimit : pageIndex * cardIncrease;
cardCountElem.innerHTML = endRange;
for (let i = startRange + 1; i <= endRange; i++) {
createCard(i);
}
};
加载初始卡片
我们已经定义了将卡片添加到容器的功能,因此我们将包含一个 window.onload 函数来设置要添加到页面的初始卡片。
window.onload = function () {
addCards(currentPage);
};
处理无限滚动
currentPage 当我们到达页面末尾时,我们将通过增加向容器添加新卡片的数量来处理无限滚动。 innerHeight 我们可以通过将窗口的 值添加到滚动值 pageYOffset 并将其与 offsetHeight 作为页面总高度的文档进行比较来检测何时到达页面末尾。
这是它的外观的视觉表示:
一旦我们到达页面的末尾,我们想通过调用 addCards currentPage + 1 的函数来加载一个新页面。
const handleInfiniteScroll = () => {
const endOfPage = window.innerHeight + window.pageYOffset >= document.body.offsetHeight;
if (endOfPage) {
addCards(currentPage + 1);
}
};
然后我们为窗口滚动创建一个事件监听器,并将上面的函数传递给它:
window.addEventListener("scroll", handleInfiniteScroll);
性能优化
由于我们正在使用滚动事件侦听器,因此限制调用次数对我们网页的性能是有益的。我们可以使用节流功能减慢调用次数。 如何使用 JavaScript 实现 Debounce 和 Throttle 杰迈玛·阿布 2021 年 5 月 3 日
我们将这样定义我们的节流函数:
var throttleTimer;
const throttle = (callback, time) => {
if (throttleTimer) return;
throttleTimer = true;
setTimeout(() => {
callback();
throttleTimer = false;
}, time);
};
然后我们将油门函数传递给 handleInfiniteScroll 函数
const handleInfiniteScroll = () => {
throttle(() => {
const endOfPage =
window.innerHeight + window.pageYOffset >= document.body.offsetHeight;
if (endOfPage) {
addCards(currentPage + 1);
}
}, 1000);
};
停止无限滚动
至此,我们已经设置了函数,以便在到达页面末尾时添加更多内容。现在,让我们确保我们的函数在没有要添加的内容时停止运行,即 cardLimit 到达时。
首先,让我们定义我们的 removeInfiniteScroll 函数。在此函数中,我们将从 handleInfiniteScroll 滚动事件侦听器中删除该函数,并删除加载器 p。
const removeInfiniteScroll = () => {
loader.remove();
window.removeEventListener("scroll", handleInfiniteScroll);
};
现在我们将修改我们 handleInfiniteScroll 以考虑是否没有更多内容要添加,即我们在内容的最后一页。
const handleInfiniteScroll = () => {
throttle(() => {
const endOfPage =
window.innerHeight + window.pageYOffset >= document.body.offsetHeight;
if (endOfPage) {
addCards(currentPage + 1);
}
if (currentPage === pageCount) {
removeInfiniteScroll();
}
}, 1000);
};
结论
我们终于得到它了!我们已经构建了无限滚动功能的高性能且可访问的实现。
巴拿马猴已进入石器时代,科学家表示担忧它们会不会变成人类?一般认为,地球人类文明的出现充满了偶然性,这种偶然性发生在其他物种身上的可能性几乎为零。倘若有一天,地球上的某一物种突然间完成了进化,那么它们会不会成为地球上第二个拥有文明的物种呢
新时代,我在中国在烟台酿酒的法国人试图打造中国的波尔多位于烟台市蓬莱区丘山山谷的瓏岱酒庄,是拉菲罗斯柴尔德集团在全球的第八个酒庄。酒庄总经理CharlesTreutenaere在烟台已工作和生活了四个春秋,他认为,无论是对于葡萄酒产业
香港黄金绿叶陈万雷去世!曾出演金城武的父亲,王家卫发文悼念1月12日晚,香港娱乐圈传来一则噩耗,根据香港媒体的报道称,有有黄金绿叶之称的香港老戏骨演员陈万雷去世,享年78岁,这件事情一时间掀起了很大的舆论,也是登上了热搜,引来无数网友的热
后三国时代北齐北周陈朝,谁是最后赢家?北周北齐南陈是南北朝时期最后的分裂国家,常被称为后三国,其存在的时间并不长,最长的也仅30余年,就湮灭在南北统一的历史潮流中。在三国短暂的和平共处一段时间后,陈朝首先按耐不住,主动
穿越火线俄服停服,感谢这段12年的旅程就在2023年1月10号,CF俄服在其官网正式发布一则公告,宣布穿越火线项目停止运营。公告主要内容翻译如下亲爱的玩家们!我们非常遗憾地宣布crossfire项目结束,合同到期了,我
宝可梦朱紫将于2月下旬更新功能修复异常问题Switch游戏宝可梦朱紫凭借极高的人气,成为2022年日本游戏市场最畅销的游戏。但是这款游戏在上市之初,因为这样那样的性能问题,备受玩家批评。近日,该游戏的官方网站宣布,将在2月
DNF衍生动画阿拉德战记主题ProjectDW新游公布NEXON日前宣布,由NEXONGames负责开发的开放世界新游ProjectDW确定制作,预定登陆PC主机以及移动平台,本作是以DNF衍生动画阿拉德战记为主题的新游,敬请期待后续
喝汤比吃肉有营养?专家喝汤更要吃肉极目新闻记者曹洋通讯员王慧文李丹冬天是养生的季节,天气寒冷,很适合喝汤。于是很多家庭开始炖各种汤,尤其是鸡汤鱼汤等营养丰富的汤,甚至在粥中还放入党参等补气的中药饮片。不少人认为,喝
肠癌患者年关饮食要注意的几个方面新年将至,家家户户都备很多年货,准备过年迎客之用。作为一个肠癌患者,我有几条经验分享给同病人。希望能帮助到大家,以供参考。年关了,一般家庭都买点肉鱼虾之类食品,牛肉,猪肠也是应有的
肾爱黑豆心爱桂圆肝爱五脏各有一个最偏爱食物,照着吃无病扰俗话说,冬季进补,来年打虎。适当冬补,可以扶正固本培育元气,让人安然过冬,并为来年春夏阳气的顺利生发打下良好的基础。可具体该如何调养?古语有云,药补不如食补。大家不妨根据五脏的特性
失眠人的自救,或许有用最近有些失眠,不知道是因为新冠的后遗症还是更年期综合症,反正就是睁眼不是1点就是2点,没有正常早上哪怕是56点钟醒我也知足。百般无奈,医院又不敢去,只能自救,在网上找了找,看能不能
戴着面具起舞,人生才会更灿烂朋友说,没有人脉和背景,想要在体制内混得好,不得不选择走另外一条路。没有听他说完,我便拒绝了,我不喜欢做那样违心的人!他未置可否,只是笑笑,不喜欢就不要去做了,你开心就好,别让自己
那首歌多年前很喜欢一首歌,歌名也很好记,叫走过咖啡屋,经常为其中的故事而感慨。时过境迁,偶或在一期央视节目里又听到了这首歌曲,禁不住停下手中忙碌的活计,痴痴的听着这熟悉又陌生的歌曲,思绪
十年风雨,初心依旧十年,在历史的长河中,不过是沧海一粟。对于我们来说,十年树木长成林,十年也足以把生命分割成一个个能回眸的碎片。对于公路人来说,十年不仅意味着时间的更迭,更承载着刻骨铭心的成长历程。
十分简短的14个聪明人生哲理故事,快速教会为人处世的道理1。一个小伙子出差去酒店,躺在床上抽烟。他不小心在酒店的床单上烧了三个小洞。退房时,酒店服务员说按照规定,每个洞要赔偿200元。小伙子问她你确定一个洞200元?服务员回答可以。于是
和书为伴,与书为友书籍是智慧的结晶,是能给予人们未来的知识权者。和书为伴,和书为友,乃是人生一大乐事。好书读得越多,心胸越开阔,有助于敞开你那敏感的心扉,对五彩缤纷的世界了解得更深。读书可改变你的人
不管情人还是恋人,如果发生这些事就说明爱情结束了,缘分结束了不管是情人还是恋人,如果发生了这些事情,就说明爱情结束了,缘分结束了。在感情的世界里,相濡以沫的感情越来越和谐,越来越亲密,相爱的两个人越来越幸福。相向而行的感觉就像两个人在同一条
一辈子很短,要好好珍惜每一天日子,过一天,少一天,所以要珍惜每一分钟的时间,珍惜身边所有的遇见,别让余生充满了遗憾,好好活着,让日子变得充实又简单。一辈子,很短,要好好去珍惜每一天,很多时候,只有经历过了,才
一个人熬过了这三样苦,就会大彻大悟生命是一场修行,在忙碌磨练的日子里,我们会遇见很多人,会经历很多事,会有很多难忘的回忆,然后蜕变成为更好的自己。人生路上,很多跌跌撞撞,磕磕绊绊给我们带来了苦难,但也让我们变得更加
算不算可惜?调离三峡部队之际,我拒绝了多情女孩的爱意在部队驻地和三峡小学联欢留影多年前的一个下午,在长江西陵峡畔,江风徐徐吹来,天空下着蒙蒙细雨,我和她共撑一把伞,漫步江边,在雨中共同传唱着一首歌,算不算可惜但在离开之际,我却头也没
莫斯科等来坏消息,朋友武契奇公开表态不承认乌东公投结果据悉,自10月1日起科索沃当地新政策开始执行,意味着他们和塞尔维亚越走越远。在新规定实施后,塞尔维亚人抵达科索沃不能凭护照免签,两国车牌照也不再互认,已经开始走向了地缘意义上的分裂
国庆带娃该如何过?这3种爱国教育方式学起来,童年才更有意义昨天接孩子幼儿园的路上,孩子很兴奋地告诉我妈妈,今天绘画课上,老师教大家画了五星红旗,大家还给国旗敬礼了!看着孩子因为激动而红扑扑的小脸蛋,我禁不住油然而生了自傲之情。不得不说,学