J'ai essayé plusieurs codes de plusieurs ressources pour détecter si l'utilisateur atteint déjà le bas de la page, puis récupère plus de données. Cela fonctionne mais la fonction récupère toujours les données chaque fois que je fais défiler, pas chaque fois que l'utilisateur atteint le bas. Voici mon code:
import React, { Component } from 'react';
class DataView extends Component {
constructor(props) {
super(props);
this.state = {
displayedData: [],
initialData: [],
loadingState: false,
};
this.fetchMoreData = this.fetchMoreData.bind(this);
this.handleScroll = this.handleScroll.bind(this);
}
componentDidMount() {
window.addEventListener('scroll', this.handleScroll, true);
}
componentWillReceiveProps(props) {
const initialData = props.initialData;
this.setState({
initialData,
displayedData: initialData.slice(0, 3),
});
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll, true);
}
fetchMoreData() {
console.log('fetch');
this.setState({ loadingState: true });
if (this.state.displayedData.length >= this.state.initialData.length) {
this.setState({
loadingState: false,
});
return;
}
const size = 3;
const initialData = this.state.initialData;
const copiedinitialData = [...initialData];
const prevDisplayedData = this.state.displayedData;
const copiedPrevDisplayedData = [...prevDisplayedData];
const copiedPrevDisplayedDataLength = copiedPrevDisplayedData.length;
const nextItem = copiedinitialData.slice(copiedPrevDisplayedDataLength, copiedPrevDisplayedDataLength + size);
copiedPrevDisplayedData.push(...nextItem);
setTimeout(() => {
this.setState({
displayedData: copiedPrevDisplayedData,
});
}, 2000);
// use set timeout because fetching data still use mock data (not hitting API)
}
handleScroll() {
// FIRST TRIAL
// const scrollTop = document.getElementById('isScrolling').scrollTop;
// const clientHeight = document.getElementById('isScrolling').clientHeight;
// const scrollHeight = document.getElementById('react-element').scrollHeight; // the id is written in another component as the outer element/wrapper
// console.log('============= ### =============');
// console.log('scrollTop: ', scrollTop);
// console.log('clientHeight: ', clientHeight);
// console.log('scrollHeight: ', scrollHeight);
// console.log('scrollHeight2: ', scrollHeight2);
// console.log('============= ### =============');
// if (scrollTop + clientHeight >= scrollHeight) {
// this.fetchMoreData();
// }
// above working but always fetch when scrolled, not when user reach bottom
// SECOND TRIAL
// const lastDiv = document.querySelector('#isScrolling > div:last-child');
// const lastDivOffset = lastDiv.offsetTop + lastDiv.clientHeight;
// const pageOffset = window.pageYOffset + window.innerHeight;
// console.log('============= ### =============');
// console.log('lastDivOffset: ', lastDivOffset);
// console.log('pageOffset: ', pageOffset);
// console.log('clientHeight: ', document.getElementById('isScrolling').clientHeight);
// console.log('scrollHeight: ', document.getElementById('isScrolling').scrollHeight);
// console.log('lastDivOffsetTop: ', lastDiv.offsetTop);
// console.log((pageOffset / lastDivOffset) * 100);
// console.log('============= ### =============');
// const ratio = (pageOffset / lastDivOffset) * 100;
// let scenario = ratio < 60;
// let scenario = pageOffset > lastDivOffset - 10;
// if (scenario) {
// this.fetchMoreData();
// }
// both of the scenario behave like the first trial
console.log('============= ### =============');
console.log('win screen: ', window.screen);
console.log('win screenTop: ', window.screenTop);
console.log('win screenLeft: ', window.screenLeft);
console.log('win screenY: ', window.screenY);
console.log('win scrollY: ', window.scrollY);
console.log('win innerHeight: ', window.innerHeight); // always 580
console.log('win pageYOffset: ', window.pageYOffset);
console.log('docEl clientHeight: ', document.documentElement.clientHeight);
console.log('docEl clientLeft: ', document.documentElement.clientLeft);
console.log('docEl clientTop: ', document.documentElement.clientTop);
console.log('docEl offsetHeight: ', document.documentElement.offsetHeight);
console.log('docEl scrollHeight: ', document.documentElement.scrollHeight);
console.log('docEl scrollLeft: ', document.documentElement.scrollLeft);
console.log('docEl screenTop: ', document.documentElement.scrollTop);
console.log('docBody clientHeight: ', document.body.clientHeight);
console.log('docBody clientLeft: ', document.body.clientLeft);
console.log('docBody clientTop: ', document.body.clientTop);
console.log('docBody offsetHeight: ', document.body.offsetHeight);
console.log('docBody scrollHeight: ', document.body.scrollHeight);
console.log('docBody scrollLeft: ', document.body.scrollLeft);
console.log('docBody screenTop: ', document.body.scrollTop);
console.log('docId scrollHeight: ', document.getElementById('isScrolling').scrollHeight); // ==> A
console.log('docId screenLeft: ', document.getElementById('isScrolling').scrollLeft);
console.log('docId scrollTop: ', document.getElementById('isScrolling').scrollTop);
console.log('docId clientHeight: ', document.getElementById('isScrolling').clientHeight); // ==> B
console.log('docId offsetHeight: ', document.getElementById('isScrolling').offsetHeight); // ==> C
// the A value is always 20px greater than B and C. B and C is always same value. The others console.log result is zero
console.log('============= ### =============');
if ((window.scrollY + window.innerHeight) >= document.body.scrollHeight) {
// this if statement also behave like the first trial
this.fetchMoreData();
}
}
render() {
const displayedData = this.state.displayedData.map((item, index) => {
const itemIndex = index;
const dataName = item.dataName;
return <div key={itemIndex} style={{ marginBottom: 20 }}>
{dataName}
</div>;
});
return (
<div>
<div className="class">
<div className="name">
{/* {another code} */}
</div>
<div className="classes">
{/* {another code} */}
</div>
<div id="isScrolling">
{displayedData}
{this.state.loadingState
? <div> Loading ....</div>
: <div> No more data available</div>
}
</div>
</div>
</div>
);
}
}
export default DataView;
Mon objectif: lorsque l'utilisateur à certains px du bas, this.fetchMoreData sera déclenché. Toute aide serait très utile.
3 Réponses :
Pour y parvenir, il vous suffit d'ajouter le code suivant
$(document).on('scroll', function() {
if($(document).height() - $(document).scrollTop() - $(window).height() < 50) {
this.fetchMoreData();
}
});
Voici un lien vers un violon .
Bien entendu, vous devez remplacer 50 par la distance souhaitée.
merci pour vos réponses. Cependant, je voudrais atteindre mon objectif en utilisant simplement du javascript. Est-ce possible? Si oui, savez-vous comment? Si ce n'est pas possible, alors je dois essayer en utilisant JQuery. Anw, où dois-je mettre le code ci-dessus? À l'intérieur de componentDidMount?
Mettez ceci dans votre componentWillMount ()
window.addEventListener('scroll', function() {
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
console.log("you're at the bottom of the page");
//show loading spinner and make fetch request to api
}
});
N'oubliez pas de tuer vos écouteurs d'événements dans componentWillUnmount () pour éviter les fuites de mémoire.
constructor(props) {
super(props);
this.state = {
height: window.innerHeight,
message: 'not at bottom'
};
this.handleScroll = this.handleScroll.bind(this);
}
handleScroll() {
const windowHeight = "innerHeight" in window ? window.innerHeight : document.documentElement.offsetHeight;
const body = document.body;
const html = document.documentElement;
const docHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
const windowBottom = windowHeight + window.pageYOffset;
if (windowBottom >= docHeight) {
this.setState({
message: 'bottom reached'
});
} else {
this.setState({
message: 'not at bottom'
});
}
}
componentDidMount() {
window.addEventListener("scroll", this.handleScroll);
}
componentWillUnmount() {
window.removeEventListener("scroll", this.handleScroll);
}
Désolé, je vois maintenant que vous avez essayé la même chose qui devrait fonctionner. Êtes-vous sûr que votre page n'est pas trop courte et qu'elle se charge avec peu ou pas de barre de défilement? Vous dites que innerHeight vaut toujours 580. C'est vraiment petit
Son chargement avec une petite barre de défilement (à moins que j'augmente les données initiales que je veux afficher au premier chargement). le innerHeight est petit parce que j'essaie d'utiliser la taille de l'écran de l'iPhone 5S pour afficher l'application. Mais ce que je ne comprends pas, c'est pourquoi la fonction de récupération est toujours déclenchée chaque fois que je fais un défilement, au lieu de vérifier la distance. Exemple, quand je fais défiler (doucement) 5 fois. Ensuite, toutes mes données ont été récupérées, cependant, je ne vais pas loin du haut et le bas est encore loin de ma vue actuelle
Avez-vous essayé d'échanger document.body.scrollHeight pour document.body.offsetHeight ?? Ils sont un peu les mêmes, mais traitent le rembourrage et les bordures différemment. Essayez également de vous connecter à la console dans votre instruction if avec le résultat de ce calcul afin de voir ce qui ne va pas.