Muscardinus
Scroll Spy 본문
728x90
Vanilla JS
<div id="app">
<ul id="nav">
<li class="on"><button>1</button></li>
<li><button>2</button></li>
<li><button>3</button></li>
<li><button>4</button></li>
<li><button>5</button></li>
<li><button>6</button></li>
<li><button>7</button></li>
<li><button>8</button></li>
</ul>
<div id="contents">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
</div>
</div>
const navElem = document.querySelector("#nav");
const navItems = Array.from(navElem.children);
const contentsElem = document.querySelector("#contents");
const contentItems = Array.from(contentsElem.children);
const scrollSpyObserver = new IntersectionObserver(
entries => {
// 최초에는 observe가 다 되지만
// 추후에는 intersect가 되는 el, 이제 안 되는 el 두개만 나온다.
const { target } = entries.find(entry => entry.isIntersecting) || {};
const index = contentItems.indexOf(target);
Array.from(navElem.children).forEach((c, i) => {
if (i === index) c.classList.add("on");
else c.classList.remove("on");
});
},
{
root: null,
rootMargin: "0px",
threshold: 0.5
// 50%만 보여도 true
}
);
contentItems.forEach(item => scrollSpyObserver.observe(item));
navElem.addEventListener("click", e => {
const targetElem = e.target;
if (targetElem.tagName === "BUTTON") {
const targetIndex = navItems.indexOf(targetElem.parentElement);
contentItems[targetIndex].scrollIntoView({
block: "start",
behavior: "smooth"
});
}
});
React
import React, { useState, useRef, useEffect } from "react";
import Nav from "./Nav";
import Content from "./Content";
import "./style.css";
const pages = Array.from({ length: 8 }).map((_, i) => i + 1);
const App = () => {
const [viewIndex, setViewIndex] = useState(0);
const contentRef = useRef([]);
const moveToPage = index => () => {
contentRef.current[index].scrollIntoView({
block: "start",
behavior: "smooth"
});
};
const scrollSpyObserver = new IntersectionObserver(
entries => {
const { target } = entries.find((entry) => entry.isIntersecting) || {};
const index = contentRef.current.indexOf(target);
setViewIndex(index);
},
{
root: null,
rootMargin: "0px",
threshold: 0.5
}
);
useEffect(() => {
contentRef.current.forEach((item) => scrollSpyObserver.observe(item));
return () => {
contentRef.current.forEach((item) => scrollSpyObserver.unobserve(item));
};
}, []);
return (
<div id="app">
<Nav pages={pages} viewIndex={viewIndex} moveToPage={moveToPage} />
<div id="contents">
{pages.map((p, i) => (
<Content key={p} ref={(r) => (contentRef.current[i] = r)} page={p} />
))}
</div>
</div>
);
};
export default App;
import React, { forwardRef } from "react";
const Content = ({ page }, ref) => <div ref={ref}>{page}</div>;
export default forwardRef(Content);
728x90
'FrontEnd > Basic Skills' 카테고리의 다른 글
Dropdown Menu (0) | 2022.03.13 |
---|---|
Auto Complete (0) | 2022.03.13 |
Search bar (0) | 2022.02.09 |
Scroll Top (0) | 2022.01.31 |
Infinite Scroll (0) | 2022.01.31 |
Comments