删除与视口相交的元素
Delete elements that have intersected the viewport
我正在与 intersection observer 一起玩弄以创建一个无限滚动狗网站。当您滚动并出现 6 条狗时,api 会再发射 6 次以抓取更多狗以添加到 DOM。我希望狗在用户滚动时加载,但当一只已经查看过的狗离开视口并在页面上上升时,该元素然后从页面上删除。所以狗总是在向下滚动时加载,但向上滚动时你总是在页面顶部。我当前在名为 lastFunc
的函数中的实现导致它表现得非常奇怪。我怎样才能达到预期的效果。
class CardGenerator {
constructor() {
this.$cardContainer = document.querySelector('.card-container');
this.$allCards = undefined;
this.observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
entry.target.classList.toggle('show', entry.isIntersecting);
if (entry.isIntersecting) {
this.observer.unobserve(entry.target);
}
});
},
{
threshold: 1,
rootMargin: '150px',
}
);
this.loadNewCards();
}
cacheDOMElements() {
this.$allCards = document.querySelectorAll('.card');
}
loadNewCards() {
for (let index = 0; index < 6; index++) {
fetch('https://dog.ceo/api/breeds/image/random', { method: 'GET' })
.then((result) => {
return result.json();
})
.then((r) => {
console.log(r);
const card = document.createElement('div');
card.classList.add('card');
const imageElement = document.createElement('img');
imageElement.classList.add('forza-img');
imageElement.setAttribute('src', r.message);
card.appendChild(imageElement);
this.observer.observe(card);
this.$cardContainer.append(card);
this.cacheDOMElements();
if (this.$allCards.length % 6 === 0) this.lastFunc();
});
}
}
lastFunc() {
console.log(this.$allCards);
if (this.$allCards.length > 12) {
this.$allCards.forEach((item, idx) => {
if (idx < 6) {
item.remove();
}
});
}
this.$allCards.forEach((card, idx) => {
this.observer.observe(card);
});
const lastCardObserver = new IntersectionObserver((entries) => {
const $lastCard = entries[0];
if (!$lastCard.isIntersecting) return;
this.loadNewCards();
lastCardObserver.unobserve($lastCard.target);
});
lastCardObserver.observe(document.querySelector('.card:last-child'));
}
}
const cardGenerator = new CardGenerator();
html,
body {
height: 100%;
width: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
}
.card {
float: left;
width: 48vw;
margin: 1%;
transform: translateX(100px);
opacity: 0;
transition: 150ms;
}
.card.show {
transform: translateY(0);
opacity: 1;
}
.card img {
width: 100%;
border-radius: 15px;
height: 30vh;
object-fit: cover;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Dog Random Images</h1>
<div class="card-container"></div>
</body>
<script src="app.js" ></script>
</html>
删除元素时,容器的全部内容都会移动,观察者开始开火。为了删除元素时不触发观察者,需要在删除元素前将滚动条移动到该元素的高度。
示例如下:
class CardGenerator {
constructor() {
this.$cardContainer = document.querySelector('.card-container');
this.$allCards = undefined;
this.observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
entry.target.classList.add('show', entry.isIntersecting);
if (entry.isIntersecting) {
this.observer.unobserve(entry.target);
}
});
},
{
threshold: 1,
rootMargin: '150px',
}
);
this.loadNewCards();
}
cacheDOMElements() {
this.$allCards = document.querySelectorAll('.card');
}
loadNewCards() {
for (let index = 0; index < 6; index++) {
fetch('https://dog.ceo/api/breeds/image/random', { method: 'GET' })
.then((result) => {
return result.json();
})
.then((r) => {
console.log(r);
const card = document.createElement('div');
card.classList.add('card');
const imageElement = document.createElement('img');
imageElement.classList.add('forza-img');
imageElement.setAttribute('src', r.message);
card.appendChild(imageElement);
this.observer.observe(card);
this.$cardContainer.append(card);
this.cacheDOMElements();
if (this.$allCards.length % 6 === 0) this.lastFunc();
});
}
}
lastFunc() {
console.log(this.$allCards);
if (this.$allCards.length > 12) {
this.$allCards.forEach((item, idx) => {
if (idx < 6) {
const scrollTop = this.$cardContainer.scrollTop;
const height = item.offsetHeight;
this.$cardContainer.scrollTo(0, Math.max(0, scrollTop - height));
item.remove();
}
});
}
this.$allCards.forEach((card, idx) => {
this.observer.observe(card);
});
const lastCardObserver = new IntersectionObserver((entries) => {
const $lastCard = entries[0];
if (!$lastCard.isIntersecting) return;
this.loadNewCards();
lastCardObserver.unobserve($lastCard.target);
});
lastCardObserver.observe(document.querySelector('.card:last-child'));
}
}
const cardGenerator = new CardGenerator();
html,
body {
height: 100%;
width: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
}
.card {
float: left;
width: 48vw;
margin: 1%;
transform: translateX(100px);
opacity: 0;
transition: 150ms;
}
.card.show {
transform: translateY(0);
opacity: 1;
}
.card img {
width: 100%;
border-radius: 15px;
height: 30vh;
object-fit: cover;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Dog Random Images</h1>
<div class="card-container"></div>
</body>
<script src="app.js" ></script>
</html>
希望这对你有所帮助。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<style>
body {
height: 100%;
width: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
}
.card {
width: 48vw;
margin: 1%;
}
.card.show {
// opacity: 1;
}
.card img {
width: 100%;
border-radius: 15px;
height: 30vh;
object-fit: cover;
}
.card-container{
border: solid 1px #00f;
padding: 20px;
overflow-y:scroll;
}
</style>
<style>
#sentinel{
height:0px;
}
</style>
<h1>Dog Random Images</h1>
<div class="card-container">
<div id="sentinel"></div>
</div>
<script>
/* Question on */
class CardGenerator {
constructor() {
this.$cardContainer = document.querySelector('.card-container');
this.$allCards = undefined;
this.mysentinel = document.querySelector('#sentinel');
this.observer = new IntersectionObserver(
(entries) => {
let [entry] = entries; //destructure array, get first entry - should only be 1 - sentinel
if (entry.isIntersecting) {
this.observer.unobserve(entry.target);
this.loadNewCards();
}
}, {
threshold: 1,
rootMargin: '150px' /*expanded root/viewport(due to null) by 150px*/,
}
);
this.loadNewCards();
} // end constructor;
cacheDOMElements() {
//The Document method querySelectorAll() returns a static (not live) NodeList
this.$allCards = document.querySelectorAll('.card');
}
loadNewCards() {
/* , from peirix*/
this.mypromises = [];
this.mymessages = [];
this.urls = new Array(6).fill("https://dog.ceo/api/breeds/image/random", 0, 6);
//create array of promises
var promises = this.urls.map(url => fetch(url).then(y => y.json()));
//Promise.all() method takes an iterable of promises
//promise.all returns a single Promise that resolves to an array of the results of the input promises
Promise.all(promises)
.then(results => {
//accumulate all the urls from message property
results.forEach(v => this.mymessages.push(v.message));
})
.finally(() => {
let idx = 0;
for (let message of this.mymessages) {
const card = document.createElement('div');
card.classList.add('card');
const imageElement = document.createElement('img');
imageElement.setAttribute('src', message);
imageElement.setAttribute('title', `${idx++}:${message}`);
card.appendChild(imageElement);
this.$cardContainer.appendChild(card);
}// end for
this.cacheDOMElements();
//stop this sentinel possibly hitting the observer to loadnewcards as we (re)move cards
this.observer.unobserve(this.mysentinel);
//if number of cards is>12 then takeoff the first 6
if (this.$allCards.length > 12) {
for (let i = 0; i < 6; i++) {
this.$allCards[i].remove();
}
}
//already exists so move it to bottom of container div
this.$cardContainer.appendChild(this.mysentinel);
/*this should be outside the root so when it invokes observer it will not fire loadnewcards*/
this.observer.observe(this.mysentinel);
}); //end of finally end of Promise.all
} //end loadnewcards
} //class CardGenerator
const cardGenerator = new CardGenerator();
</script>
</body>
</html>
我正在与 intersection observer 一起玩弄以创建一个无限滚动狗网站。当您滚动并出现 6 条狗时,api 会再发射 6 次以抓取更多狗以添加到 DOM。我希望狗在用户滚动时加载,但当一只已经查看过的狗离开视口并在页面上上升时,该元素然后从页面上删除。所以狗总是在向下滚动时加载,但向上滚动时你总是在页面顶部。我当前在名为 lastFunc
的函数中的实现导致它表现得非常奇怪。我怎样才能达到预期的效果。
class CardGenerator {
constructor() {
this.$cardContainer = document.querySelector('.card-container');
this.$allCards = undefined;
this.observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
entry.target.classList.toggle('show', entry.isIntersecting);
if (entry.isIntersecting) {
this.observer.unobserve(entry.target);
}
});
},
{
threshold: 1,
rootMargin: '150px',
}
);
this.loadNewCards();
}
cacheDOMElements() {
this.$allCards = document.querySelectorAll('.card');
}
loadNewCards() {
for (let index = 0; index < 6; index++) {
fetch('https://dog.ceo/api/breeds/image/random', { method: 'GET' })
.then((result) => {
return result.json();
})
.then((r) => {
console.log(r);
const card = document.createElement('div');
card.classList.add('card');
const imageElement = document.createElement('img');
imageElement.classList.add('forza-img');
imageElement.setAttribute('src', r.message);
card.appendChild(imageElement);
this.observer.observe(card);
this.$cardContainer.append(card);
this.cacheDOMElements();
if (this.$allCards.length % 6 === 0) this.lastFunc();
});
}
}
lastFunc() {
console.log(this.$allCards);
if (this.$allCards.length > 12) {
this.$allCards.forEach((item, idx) => {
if (idx < 6) {
item.remove();
}
});
}
this.$allCards.forEach((card, idx) => {
this.observer.observe(card);
});
const lastCardObserver = new IntersectionObserver((entries) => {
const $lastCard = entries[0];
if (!$lastCard.isIntersecting) return;
this.loadNewCards();
lastCardObserver.unobserve($lastCard.target);
});
lastCardObserver.observe(document.querySelector('.card:last-child'));
}
}
const cardGenerator = new CardGenerator();
html,
body {
height: 100%;
width: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
}
.card {
float: left;
width: 48vw;
margin: 1%;
transform: translateX(100px);
opacity: 0;
transition: 150ms;
}
.card.show {
transform: translateY(0);
opacity: 1;
}
.card img {
width: 100%;
border-radius: 15px;
height: 30vh;
object-fit: cover;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Dog Random Images</h1>
<div class="card-container"></div>
</body>
<script src="app.js" ></script>
</html>
删除元素时,容器的全部内容都会移动,观察者开始开火。为了删除元素时不触发观察者,需要在删除元素前将滚动条移动到该元素的高度。
示例如下:
class CardGenerator {
constructor() {
this.$cardContainer = document.querySelector('.card-container');
this.$allCards = undefined;
this.observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
entry.target.classList.add('show', entry.isIntersecting);
if (entry.isIntersecting) {
this.observer.unobserve(entry.target);
}
});
},
{
threshold: 1,
rootMargin: '150px',
}
);
this.loadNewCards();
}
cacheDOMElements() {
this.$allCards = document.querySelectorAll('.card');
}
loadNewCards() {
for (let index = 0; index < 6; index++) {
fetch('https://dog.ceo/api/breeds/image/random', { method: 'GET' })
.then((result) => {
return result.json();
})
.then((r) => {
console.log(r);
const card = document.createElement('div');
card.classList.add('card');
const imageElement = document.createElement('img');
imageElement.classList.add('forza-img');
imageElement.setAttribute('src', r.message);
card.appendChild(imageElement);
this.observer.observe(card);
this.$cardContainer.append(card);
this.cacheDOMElements();
if (this.$allCards.length % 6 === 0) this.lastFunc();
});
}
}
lastFunc() {
console.log(this.$allCards);
if (this.$allCards.length > 12) {
this.$allCards.forEach((item, idx) => {
if (idx < 6) {
const scrollTop = this.$cardContainer.scrollTop;
const height = item.offsetHeight;
this.$cardContainer.scrollTo(0, Math.max(0, scrollTop - height));
item.remove();
}
});
}
this.$allCards.forEach((card, idx) => {
this.observer.observe(card);
});
const lastCardObserver = new IntersectionObserver((entries) => {
const $lastCard = entries[0];
if (!$lastCard.isIntersecting) return;
this.loadNewCards();
lastCardObserver.unobserve($lastCard.target);
});
lastCardObserver.observe(document.querySelector('.card:last-child'));
}
}
const cardGenerator = new CardGenerator();
html,
body {
height: 100%;
width: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
}
.card {
float: left;
width: 48vw;
margin: 1%;
transform: translateX(100px);
opacity: 0;
transition: 150ms;
}
.card.show {
transform: translateY(0);
opacity: 1;
}
.card img {
width: 100%;
border-radius: 15px;
height: 30vh;
object-fit: cover;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Dog Random Images</h1>
<div class="card-container"></div>
</body>
<script src="app.js" ></script>
</html>
希望这对你有所帮助。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<style>
body {
height: 100%;
width: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
}
.card {
width: 48vw;
margin: 1%;
}
.card.show {
// opacity: 1;
}
.card img {
width: 100%;
border-radius: 15px;
height: 30vh;
object-fit: cover;
}
.card-container{
border: solid 1px #00f;
padding: 20px;
overflow-y:scroll;
}
</style>
<style>
#sentinel{
height:0px;
}
</style>
<h1>Dog Random Images</h1>
<div class="card-container">
<div id="sentinel"></div>
</div>
<script>
/* Question on */
class CardGenerator {
constructor() {
this.$cardContainer = document.querySelector('.card-container');
this.$allCards = undefined;
this.mysentinel = document.querySelector('#sentinel');
this.observer = new IntersectionObserver(
(entries) => {
let [entry] = entries; //destructure array, get first entry - should only be 1 - sentinel
if (entry.isIntersecting) {
this.observer.unobserve(entry.target);
this.loadNewCards();
}
}, {
threshold: 1,
rootMargin: '150px' /*expanded root/viewport(due to null) by 150px*/,
}
);
this.loadNewCards();
} // end constructor;
cacheDOMElements() {
//The Document method querySelectorAll() returns a static (not live) NodeList
this.$allCards = document.querySelectorAll('.card');
}
loadNewCards() {
/* , from peirix*/
this.mypromises = [];
this.mymessages = [];
this.urls = new Array(6).fill("https://dog.ceo/api/breeds/image/random", 0, 6);
//create array of promises
var promises = this.urls.map(url => fetch(url).then(y => y.json()));
//Promise.all() method takes an iterable of promises
//promise.all returns a single Promise that resolves to an array of the results of the input promises
Promise.all(promises)
.then(results => {
//accumulate all the urls from message property
results.forEach(v => this.mymessages.push(v.message));
})
.finally(() => {
let idx = 0;
for (let message of this.mymessages) {
const card = document.createElement('div');
card.classList.add('card');
const imageElement = document.createElement('img');
imageElement.setAttribute('src', message);
imageElement.setAttribute('title', `${idx++}:${message}`);
card.appendChild(imageElement);
this.$cardContainer.appendChild(card);
}// end for
this.cacheDOMElements();
//stop this sentinel possibly hitting the observer to loadnewcards as we (re)move cards
this.observer.unobserve(this.mysentinel);
//if number of cards is>12 then takeoff the first 6
if (this.$allCards.length > 12) {
for (let i = 0; i < 6; i++) {
this.$allCards[i].remove();
}
}
//already exists so move it to bottom of container div
this.$cardContainer.appendChild(this.mysentinel);
/*this should be outside the root so when it invokes observer it will not fire loadnewcards*/
this.observer.observe(this.mysentinel);
}); //end of finally end of Promise.all
} //end loadnewcards
} //class CardGenerator
const cardGenerator = new CardGenerator();
</script>
</body>
</html>