首页
/
每日頭條
/
職場
/
前端變量提升面試題
前端變量提升面試題
更新时间:2025-06-20 00:43:16
前言

下拉刷新和上拉加載這兩種交互方式通常出現在移動端中

本質上等同于PC網頁中的分頁,隻是交互形式不同

開源社區也有很多優秀的解決方案,如iscroll、better-scroll、pulltorefresh.js庫等等

這些第三方庫使用起來非常便捷

我們通過原生的方式實現一次上拉加載,下拉刷新,有助于對第三方庫有更好的理解與使用

實現原理

上拉加載及下拉刷新都依賴于用戶交互

最重要的是要理解在什麼場景,什麼時機下觸發交互動作

上拉加載

首先可以看一張圖

前端變量提升面試題(前端面試題上拉加載)1

上拉加載的本質是頁面觸底,或者快要觸底時的動作

判斷頁面觸底我們需要先了解一下下面幾個屬性

  • scrollTop:滾動視窗的高度距離window頂部的距離,它會随着往上滾動而不斷增加,初始值是0,它是一個變化的值
  • clientHeight:它是一個定值,表示屏幕可視區域的高度;
  • scrollHeight:頁面不能滾動時也是存在的,此時scrollHeight等于clientHeight。scrollHeight表示body所有元素的總長度(包括body元素自身的padding)

offsetTop 元素的上外邊框至包含元素的上内邊框之間的像素距離,其他方向相同

offsetWidth 元素兩端算上外邊框的寬度,其他方向相同

scrollLeft 和 scrollTop 既可以确定當前元素的滾動狀态,也可以設置元素的滾動位置

scrollWidth 和 scrollHeight 确定元素内容的實際大小

clientWidth 元素内容區寬度加上左右内邊距寬度,即clientWidth = content padding

clientHeight 元素内容區高度加上上下内邊距高度,即clientHeight = content padding

綜上我們得出一個觸底公式:

scrollTop clientHeight >= scrollHeight

簡單實現

let clientHeight = document.documentElement.clientHeight; //浏覽器高度 let scrollHeight = document.body.scrollHeight; let scrollTop = document.documentElement.scrollTop; let distance = 50; //距離視窗還用50的時候,開始觸發; if ((scrollTop clientHeight) >= (scrollHeight - distance)) { console.log("開始加載數據"); }

下拉刷新

下拉刷新的本質是頁面本身置于頂部時,用戶下拉時需要觸發的動作

關于下拉刷新的原生實現,主要分成三步:

  • 監聽原生touchstart事件,記錄其初始位置的值,e.touches[0].pageY;
  • 監聽原生touchmove事件,記錄并計算當前滑動的位置值與初始位置值的差值,大于0表示向下拉動,并借助CSS3的translateY屬性使元素跟随手勢向下滑動對應的差值,同時也應設置一個允許滑動的最大值;
  • 監聽原生touchend事件,若此時元素滑動達到最大值,則觸發callback,同時将translateY重設為0,元素回到初始位置

舉個例子:

HTML結構如下:

<main> <p class="refreshText"></p > <ul id="refreshContainer"> <li>111</li> <li>222</li> <li>333</li> <li>444</li> <li>555</li> ... </ul> </main>

監聽touchstart事件,記錄初始的值

var _element = document.getElementById('refreshContainer'), _refreshText = document.querySelector('.refreshText'), _startPos = 0, // 初始的值 _transitionHeight = 0; // 移動的距離 _element.addEventListener('touchstart', function(e) { _startPos = e.touches[0].pageY; // 記錄初始位置 _element.style.position = 'relative'; _element.style.transition = 'transform 0s'; }, false);

監聽touchmove移動事件,記錄滑動差值

_element.addEventListener('touchmove', function(e) { // e.touches[0].pageY 當前位置 _transitionHeight = e.touches[0].pageY - _startPos; // 記錄差值 if (_transitionHeight > 0 && _transitionHeight < 60) { _refreshText.innerText = '下拉刷新'; _element.style.transform = 'translateY(' _transitionHeight 'px)'; if (_transitionHeight > 55) { _refreshText.innerText = '釋放更新'; } } }, false);

最後,就是監聽touchend離開的事件

_element.addEventListener('touchend', function(e) { _element.style.transition = 'transform 0.5s ease 1s'; _element.style.transform = 'translateY(0px)'; _refreshText.innerText = '更新中...'; // todo... }, false);

從上面可以看到,在下拉到松手的過程中,經曆了三個階段:

  • 當前手勢滑動位置與初始位置差值大于零時,提示正在進行下拉刷新操作
  • 下拉到一定值時,顯示松手釋放後的操作提示
  • 下拉到達設定最大值松手時,執行回調,提示正在進行更新操作
案例

在實際開發中,我們更多的是使用第三方庫,下面以better-scroll進行舉例:

HTML結構

<div id="position-wrapper"> <div> <p class="refresh">下拉刷新</p > <div class="position-list"> <!--列表内容--> </div> <p class="more">查看更多</p > </div> </div>

實例化上拉下拉插件,通過use來注冊插件

import BScroll from "@better-scroll/core"; import PullDown from "@better-scroll/pull-down"; import PullUp from '@better-scroll/pull-up'; BScroll.use(PullDown); BScroll.use(PullUp);

實例化BetterScroll,并傳入相關的參數

let pageNo = 1,pageSize = 10,dataList = [],isMore = true; var scroll= new BScroll("#position-wrapper",{ scrollY:true,//垂直方向滾動 click:true,//默認會阻止浏覽器的原生click事件,如果需要點擊,這裡要設為true pullUpLoad:true,//上拉加載更多 pullDownRefresh:{ threshold:50,//觸發pullingDown事件的位置 stop:0//下拉回彈後停留的位置 } }); //監聽下拉刷新 scroll.on("pullingDown",pullingDownHandler); //監測實時滾動 scroll.on("scroll",scrollHandler); //上拉加載更多 scroll.on("pullingUp",pullingUpHandler); async function pullingDownHandler(){ dataList=[]; pageNo=1; isMore=true; $(".more").text("查看更多"); await getlist();//請求數據 scroll.finishPullDown();//每次下拉結束後,需要執行這個操作 scroll.refresh();//當滾動區域的DOM結構有變化時,需要執行這個操作 } async function pullingUpHandler(){ if(!isMore){ $(".more").text("沒有更多數據了"); scroll.finishPullUp();//每次上拉結束後,需要執行這個操作 return; } pageNo ; await this.getlist();//請求數據 scroll.finishPullUp();//每次上拉結束後,需要執行這個操作 scroll.refresh();//當滾動區域的dom結構有變化時,需要執行這個操作 } function scrollHandler(){ if(this.y>50) $('.refresh').text("松手開始加載"); else $('.refresh').text("下拉刷新"); } function getlist(){ //返回的數據 let result=....; dataList=dataList.concat(result); //判斷是否已加載完 if(result.length<pageSize) isMore=false; //将dataList渲染到html内容中 }

注意點:

使用better-scroll實現下拉刷新、上拉加載時要注意以下幾點:

  • wrapper裡必須隻有一個子元素
  • 子元素的高度要比wrapper要高
  • 使用的時候,要确定DOM元素是否已經生成,必須要等到DOM渲染完成後,再new BScroll()
  • 滾動區域的DOM元素結構有變化後,需要執行刷新 refresh()
  • 上拉或者下拉,結束後,需要執行finishPullUp()或者finishPullDown(),否則将不會執行下次操作
  • better-scroll,默認會阻止浏覽器的原生click事件,如果滾動内容區要添加點擊事件,需要在實例化屬性裡設置click:true
Vue 實現下拉刷新加載

!<template> <!-- 使用vant組件來寫下來刷新加載 --> <div> <van-list v-model="loading" :finished="finished" finished-text="沒有更多了" @load="onLoad"> <div v-for="(item, index) in list" :key="index"> <div @click="btn(item.id)">{{ item.title }}</div> </div> </van-list> </div> </template> <script> import { GetCode } from "../api/index"; export default { data() { return { list: [], limit: 16,//代表每頁數量 page: 1,//代表頁數 total: 1000, //總數 loading: false,//vant中的loading效果 finished: false//代表滑到底總數 }; }, created() {}, methods: { onLoad() { this.page = 1;//依次遞加1 this.getmain(); }, btn(e) { console.log(e); this.$router.push({ name: "homeList", params: { id: e } }); }, getmain() { let params = {//拿到對應的入參 limit: this.limit, page: this.page }; GetCode(params).then(res => { this.loading = true;//在請求時打開loading setTimeout(() => {//這個定時器可寫可不寫 這邊是更好看到loading效果 if (res.data.success === true) {//判斷請求成功狀态 this.loading = false;//請求成功關閉loading效果 this.list =this.page == 1 ? res.data.data : this.list.concat(res.data.data); // 以上是對應數據 如果頁數為1 對應相對數據 否則每次加載後對應的頁數數量添加到以上頁數對應的請求 this.total = this.total;//這個是對應的總數 this.finished = this.list.length >= this.total;//這個判斷當接口數據長度與總數相同不加載顯示對應文字 } else { this.finished = true;//錯誤loading打開 } }, 800); }) .catch(err => { this.loading = true;//錯誤loading打開 }); } } }; </script> <style lang="scss" scoped> div { div { div { margin-top: 10px; } } } </style>

上拉刷新下拉加載

!<template> <div> <van-pull-refresh success-text="刷新成功" v-model="isLoading" @refresh="onRefresh"> <van-list v-model="loading" :finished="finished" finished-text="沒有更多了" @load="onLoad"> <div v-for="(item, index) in list" :key="index"> <div @click="btn(item.id)">{{ item.title }}</div> </div> </van-list> </van-pull-refresh> </div> </template> <script> import { GetCode } from "../api/index"; export default { data() { return { list: [], limit: 16, //代表每頁數量 page: 0, //代表頁數 total: 1000, //總數 loading: false, //vant中的loading效果 finished: false, count: 0, isLoading: false }; }, created() {}, methods: { onLoad() { // 下拉刷新加載 this.getmain(); }, btn(e) { console.log(e); this.$router.push({ name: "homeList", params: { id: e } }); }, onRefresh() { // 上拉刷新 setTimeout(() => { this.isLoading = false; this.page = 0; this.getmain(); this.loading = false; }, 800); }, getmain() { let params = { //拿到對應的入參 limit: this.limit, page: this.page }; GetCode(params) .then(res => { setTimeout(() => { this.total = this.total; //這個是總數 this.list = this.page == 0 ? res.data.data : [...this.list, ...res.data.data]; this.page ; this.finished = this.list.length >= this.total; this.loading = false; }, 800); if (this.page >= this.total) { this.finished = true; this.loading = false; } }) .catch(err => { this.loading = true; //錯誤loading打開 }); } } }; </script> <style lang="scss" scoped> div { div { div { margin-top: 10px; } } } </style>

小結

下拉刷新、上拉加載原理本身都很簡單,真正複雜的是封裝過程中,要考慮的兼容性、易用性、性能等諸多細節

給大家分享我收集整理的各種學習資料,前端小白交學習流程,入門教程等回答-下面是學習資料參考。

前端學習交流、自學、學習資料等推薦 - 知乎

,
Comments
Welcome to tft每日頭條 comments! Please keep conversations courteous and on-topic. To fosterproductive and respectful conversations, you may see comments from our Community Managers.
Sign up to post
Sort by
Show More Comments
推荐阅读
dnf槍劍士哪個轉職pk更強(4圖流告訴你DNF槍劍士轉什麼職業)
dnf槍劍士哪個轉職pk更強(4圖流告訴你DNF槍劍士轉什麼職業)
     突然發現傭兵的視頻距離其他三個日期比較久遠 所以補一個比較靠近的      艾肯6 精煉套石碑   再補同樣UP主的特工      再補個比較接近其他三個職業的特工      18分鐘   當然了 這隻是單純從虛區跟安逸程度來看 如果帥這種很主觀的 就靠各位心裡的第一感覺了   ps:7分鐘的特工裝備打造比較好 無盡手镯(比其他3個提升高) 魔獸戒...
2025-06-20
好看的職場類電視劇有哪些(9部堪比宮鬥劇的職場劇)
好看的職場類電視劇有哪些(9部堪比宮鬥劇的職場劇)
  大家有開始追孫俪和趙又廷主演的劇《理想之城》了嗎?這部職場劇非常精彩,高能劇情堪比宮鬥劇,被網友稱為現代版的《甄嬛傳》。以往職場劇往往會淪為批着職場皮的瑪莉蘇偶像劇,但小編發現近期有不少好看的職場題材劇,像是《怪你過分美麗》、《我是真的愛你》、《安家》等等,以下網友評論9部堪比宮鬥劇的「職場劇」,《平凡的榮耀》号稱男版《如懿傳》!      圖片來源:微...
2025-06-20
夢見自己工作很忙碌周公解夢(夢見自己在工作)
夢見自己工作很忙碌周公解夢(夢見自己在工作)
  工作在現實生活中是很平常的事情,而在做夢中卻有着不一樣的寓意,有時候是帶來好運的征兆,但是有時候是提醒你應該注意一些意外的征兆,到底該怎麼判斷呢,一起了解一下解夢說裡面對于做夢夢見工作的說法吧。  上班族夢見自己工作,說明你最近工作壓力可能比較大,而且總是不順利,要麼和同事發生争吵,要麼做的事情不如老闆的意,這可就是你自身的問題了,如果再不好好反省,恐怕...
2025-06-20
進入職場租房的感受(職場初哥想便宜租房)
進入職場租房的感受(職場初哥想便宜租房)
  職場初哥想便宜租房?除了通過網絡或者街邊的“牛皮癬”找房東直接租房以外,你還可以找街邊的街坊經紀幫忙。   在廣州城中村内,這種沒有中介經營牌照的街坊經紀有很多,路邊的士多店主可能是中介、出租屋的門房可能是中介、路邊的保安可能是中介,甚至在大榕樹下聊天的阿姨也可能是中介。      街坊中介往往和“牛皮癬”共存   記者體驗   免中介費自助看房   街...
2025-06-20
好的職場遊戲(一場遊戲一場夢)
好的職場遊戲(一場遊戲一場夢)
  ——人生的底色是悲涼的——   之前一直打算認認真真寫一篇關于職場的勾心鬥角之事,以飨讀者,結果總是忙前忙後抽不開時間去創作,當然我指的創作不是胡編亂造,是真實發生在我身上的事情,感覺自己在職場上也摸爬滾打十餘年了,寫這些是足夠有信服力,再加上自己的文字功底不賴,自然就是信手拈來的事情。   好希望頭條能開通個語音輸入功能,這樣就避免了一個字一個字去打,...
2025-06-20
Copyright 2023-2025 - www.tftnews.com All Rights Reserved