데이터 레이지 로딩
by hohoya33
HTML
<div id="viewport"></div>
<div id="target">
<div class="card">
<figure class="card-image loading"></figure>
<div class="card-detail">
<div class="card-mall loading"></div>
<div class="card-title loading"></div>
<div class="card-price loading"></div>
</div>
</div>
</div>
<p id="statusBox" class="yes">
isIntersecting === <span id="statusText">true</span>
</p>
SCSS
html {
height: 200%;
min-height: 400px;
font-family: sans-serif;
padding-top: 3.5em; }
#viewport {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: 0;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
cursor: default;
background: #dae1e7;
}
#target {
position: relative;
width: 232px;
margin: 100vh auto 100vh;
}
#statusBox {
position: fixed;
top: 0;
left: 1em;
right: 1em;
font-family: monospace;
padding: 0.5em;
background-color: #ff8;
border: 3px solid #cc5;
opacity: .9;
text-align: center;
}
#statusBox.yes {
background: #8f8;
border-color: #5c5; }
#statusBox.no {
background: #f88;
border-color: #c55; }
.card {
overflow: hidden;
background: white;
border-radius: .25rem;
max-width: 232px;
width: 232px;
}
.card-detail {
padding: .5rem 1rem;
height: 84px;
}
.card-image {position:relative;display:block;padding-bottom:100%;overflow:hidden;margin:0;}
.card-image img {position:absolute;left:0;top:0;width:100%;height:auto}
.loading {
position: relative;
background-color: #E2E2E2;
&::after {
display: block;
content: '';
position: absolute;
width: 100%;
height: 100%;
transform: translateX(-100%);
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .2), transparent);
animation: loading 1.5s infinite;
}
}
.card-mall.loading {
width:100px;
height:10px;
margin:16px 0;
}
.card-title.loading {
width:180px;
height:8px;
margin-bottom:8px;
}
.card-price.loading {
width:180px;
height:8px;
margin-bottom:8px;
}
@keyframes loading {
100% {
transform: translateX(100%);
}
}
@keyframes Gradient {
0% {
background-position: 0% 50%
}
50% {
background-position: 100% 50%
}
100% {
background-position: 0% 50%
}
}
.lazyloading img{display:block}
JavaScript
const cardImage = document.querySelector('.card-image');
const cardMall = document.querySelector('.card-mall');
const cardTitle = document.querySelector('.card-title');
const cardPrice = document.querySelector('.card-price');
const renderCard = () => {
cardTitle.textContent = '국산 라지자 캔들 아로마 향초 캔들워머';
cardMall.textContent = '이마트몰';
cardPrice.textContent = '8,910 원~';
createImage();
cardTitle.classList.remove('loading');
cardMall.classList.remove('loading');
cardPrice.classList.remove('loading');
cardImage.classList.remove('loading');
};
function createImage() {
const img = new Image();
img.classList.add("image");
img.setAttribute('alt', '국산 라지자 캔들 아로마 향초 캔들워머');
img.setAttribute('src', 'http://item.ssgcdn.com/31/37/76/item/1000031763731_i2_500.jpg');
cardImage.appendChild(img);
}
function removeCard() {
cardImage.innerHTML = '';
cardTitle.textContent = '';
cardMall.textContent = '';
cardPrice.textContent = '';
cardTitle.classList.add('loading');
cardMall.classList.add('loading');
cardPrice.classList.add('loading');
cardImage.classList.add('loading');
}
var statusBox = document.getElementById("statusBox");
var statusText = document.getElementById("statusText");
function handler(entries, observer) {
for (entry of entries) {
console.log(entry);
statusText.textContent = entry.isIntersecting;
if (entry.isIntersecting) {
statusBox.className = "yes";
setTimeout(() => {
renderCard();
}, 1000);
} else {
statusBox.className = "no";
removeCard();
}
}
}
let observer = new IntersectionObserver(handler);
observer.observe(document.getElementById("target"));