๊ณ ์ ๋ ํค๋์ ์ปจํ ์ธ ๊ฐ๋ ค์ง ํด๊ฒฐ
zoomkoding๋์ด ๋ฐฐํฌํด์ฃผ์ ๊ฐ์ธ ๋น ๋ธ๋ก๊ทธ ํ ๋ง๋ฅผ ์กฐ๊ธ์ฉ ์ปค์คํ ํด์ ์ฌ์ฉํ๊ณ ์๋ค. ๊น๋ํ ์๋ณธ ํ ๋ง ๋๋ถ์ ๋ฐ๋ก ์์ด ๊ฐ ์ผ์ ๋ณ๋ก ์์ง๋ง ๊ทธ๋๋ ์ ๊ทธ๋ ์ด๋ ๋๋ฉด ์ข์ ๊ฒ ๊ฐ์ ๋ถ๋ถ์ด ๋ณด์ฌ ์ง์ ๋ฆฌ๋ชจ๋ธ๋ง์ ํ๋ค. ๋ธ๋ก๊ทธ ์ปค์คํ ์ ๋ง์ ๋ถ๋ถ์ devhac ๋ธ๋ก๊ทธ๋ฅผ ์ฐธ๊ณ ํ์๋ค. hakyung๋๊ป ๊ฐ์ฌ๋๋ฆฝ๋๋ค!!
์ด๋ฒ์ ์์ ํ ๋ถ๋ถ์ ํค๋๋ถ๋ถ์ด๋ค. ์คํฌ๋กค์ ๋ด๋ ค๊ฐ๋ฉด์ ๊ธ์ ์ฝ๋ค๋ณด๋ฉด ๋น ๋ฅด๊ฒ ์๋ก ์ฌ๋ผ๊ฐ๊ณ ์ถ๊ฑฐ๋ ๋ค๋น๊ฒ์ด์ ๋ฉ๋ด๋ฐ๋ฅผ ์ ํํ๊ณ ์ถ์ ๋๊ฐ ์์ด ์๋๋ Top ๋ฒํผ์ ๋ง๋ค๋ ค๊ณ ํ๋ค. ๊ทธ๋ฐ๋ฐ Top ๋ฒํผ๋ณด๋ค๋ ์๋จ์ ๊ณ ์ ๋์ด ์ธ์ ๋ ์ง ์ ๊ทผํ ์ ์๋ ํค๋๊ฐ ๋ ์ค์ฉ์ ์ผ ๊ฒ ๊ฐ์ ๊ณ ์ ๋ ํค๋๋ฅผ ๋ง๋๋ ๊ฒ์ผ๋ก ๋ฐฉํฅ์ ๋ฐ๊พธ์๋ค.
์ด ๊ธ์์๋ ๋ฆฌ์กํธ
๋ก ๊ณ ์ ๋ ํค๋๋ฅผ ๋ง๋๋ ๊ณผ์ ๊ณผ, css
๋ก ๊ณ ์ ๋ ํค๋์ ๊ณ ์ง์ ์ธ ํ๊ณ์ธ ์ปจํ
์ธ ๊ฐ๋ ค์ง ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ด์ฉ์ ์๊ฐํ๋ค.
์คํฌ๋กค ์ ๊ณ ์ ๋ ํค๋(Sticky Header on Scroll) ๋ง๋ค๊ธฐ
์๋ฆฌ
๊ณ ์ ๋ ํค๋๋ฅผ ๋ง๋๋ ์๋ฆฌ๋ ๋ค์๊ณผ ๊ฐ๋ค.
- ์คํฌ๋กค์ด ๋์๋์ง ์ฌ๋ถ(
true
,false
)๋ฅผ ๋ณ์๋ก ์ ์ฅํด๋๋ค. ์ต์ด์๋false
๋ก ์ ์ฅํ๋ค. - 1์์ ์ง์ ํ ๋ณ์์ ์คํฌ๋กค์ด ์๋์ด์๋ ์ํ(
false
)๊ฐ ์ ์ฅ๋์ด ์๊ณ , ํ์ฌ ์๋์ฐ์์ ์คํฌ๋กค์ด ๊ฐ์ง๋๋ฉด ํค๋๋ฅผ ์๋จ์ ๊ณ ์ ์ํจ๋ค. ์ดํ 1์์ ์ง์ ํ ๋ณ์์๋ ์คํฌ๋กค์ด ๋์ด์์(true
)์ ์ ์ฅํ๋ค. - ๋ง์ฝ 1์์ ์ง์ ํ ๋ณ์์ ์คํฌ๋กค์ด ๋์ด์๋ ์ํ(
true
)๊ฐ ์ ์ฅ๋์ด ์๊ณ , ํ์ฌ ์๋์ฐ์์ ์คํฌ๋กค์ด ๊ฐ์ง๋์ง ์์ผ๋ฉด ํค๋๋ฅผ ๊ณ ์ ํ์ง ์๋๋ค. ์ดํ 1์์ ์ง์ ํ ๋ณ์์๋ ์คํฌ๋กค์ด ์๋์ด์์(false
)์ ์ ์ฅํ๋ค.
๊ตฌํ
์์์ ์ค๋ช ํ ๋ด์ฉ์ ๋ฆฌ์กํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ์ฌ ๋จ๊ณ๋ณ๋ก ๊ตฌํํ๋ค.
1. ์คํฌ๋กค ์ฌ๋ถ ๋ณ์ ์ง์
import React, { useState, useEffect } from 'react';
const [scrolled, setScrolled] = useState(false);
useState
ํจ์๋ฅผ ์ด์ฉํ์ฌ ํ์ฌ ์คํฌ๋กค ์ฌ๋ถ ๋ณ์(scrolled
)๋ฅผfalse
๋ก ์ด๊ธฐํํ๊ณ ์ด๋ฅผ ์ ๋ฐ์ดํธํ๋ ํจ์(scrolled
)๋ฅผ ์ ์ธํ๋ค.
2. ํ์ฌ ์คํฌ๋กค ์ํ์ ๋ฐ๋ผ ์คํฌ๋กค ์ฌ๋ถ ๋ณ์ ์ฌ์ง์
import React, { useState, useEffect } from 'react';
useEffect(() => {
const handleScroll = () => {
if (!scrolled && window.pageYOffset > 30) {
setScrolled(true);
} else if (scrolled && window.pageYOffset <= 30) {
setScrolled(false);
}
};
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, [scrolled]);
// return ์๋ต
...
};
useEffect
ํจ์๋ฅผ ์ด์ฉํ๋ค. ์ด๋ ์ฒซ๋ฒ์งธ ์ธ์๋ ์คํฌ๋กค ์ฌ๋ถ ๋ณ์์ ํค๋ ๊ณ ์ ์ ์ฌ์ง์ ํ๋ ํจ์๋ฅผ, ๋๋ฒ์งธ ์ธ์๋useEffect
ํจ์๋ฅผ ์คํํ ์ง ๋ง์ง ๊ฒฐ์ ํ๊ธฐ ์ํ ์กฐ๊ฑด ๋ณ์๋ฅผ ์ง์ ํ๋ค.์ฒซ๋ฒ์งธ ์ธ์ -
handleScroll
ํจ์์ clean-up ๋ถ๋ถ
handleScroll
ํจ์
- ์คํฌ๋กค ๊ฐ์ง๋ฅผ ์ํด์๋ ํ๋ฉด ์๋จ์ผ๋ก๋ถํฐ ์คํฌ๋กคํ ๊ฑฐ๋ฆฌ๋ฅผ ๊ตฌํ๋
pageYOffset
์์ฑ์ ์ด์ฉํ์ฌ ์ด ๊ฑฐ๋ฆฌ ๊ฐ์ด 30์ ๋๋์ง ์๋๋์ง๋ฅผ ํ์ธํจ- if๋ฌธ์ ํตํด ํ๋ฉด ์๋จ์ผ๋ก๋ถํฐ ์คํฌ๋กค์ด ์ด๋ฃจ์ด์ง๊ณ ์๋ ๊ฒฝ์ฐ๋ฅผ ํ๋ณ
- else if๋ฌธ์ ํตํด ํ๋ฉด ์ค๊ฐ๋ถ๋ถ์์ ์๋จ์ผ๋ก ์ฌ๋ผ๊ฐ๋ ์คํฌ๋กค์ ํ๋ ๊ฒฝ์ฐ๋ฅผ ํ๋ณ
clean-up
- ์คํฌ๋กค ์ด๋ฒคํธ ๋ฐ์ ์
handleScroll
ํจ์๋ฅผ ํธ์ถuseEffect
ํจ์๋ฅผ ๊ณ์ํด์ ์ฌ์คํํด์ผํ๋ฏ๋ก ์ด์ ์ ๋ฐ์ํ๋ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ ๊ฑฐํ๊ณ ์ ๋ฆฌํจ๋๋ฒ์งธ ์ธ์ -
[scrolled]
๋ณ์
- ๊ณ์ํด์ ์ฌ์คํ๋๋
useEffect
ํจ์๋ ์ฑ๋ฅ์ ์ ํ์ํฌ ์ ์์- ์ด๋ฅผ ๋ง๊ธฐ์ํด ์ธ์๋ก
[scrolled]
๋ณ์๋ฅผ ๋๊ฒจ์ฃผ๊ณ ๋ ๋๋ง ์ด์ ๊ณผ ์ดํ์[scrolled]
๊ฐ์ ๋น๊ตํ์ฌ ๋์ผํ ๊ฒฝ์ฐuseEffect
ํจ์๋ฅผ ๊ฑด๋๋ฐ๋๋ก ํจ
3. ํค๋๋ฅผ ๊ณ ์ ์ํค๋ ํจ๊ณผ ๋ํ๋ด๊ธฐ
ํด๋์ค ๋ช ์ ๋ฐ๋ผ style์ ๋ค๋ฅด๊ฒ ์ค์ ํด์ฃผ๋ฉด ์คํฌ๋กค ์ฌ๋ถ์ ๋ฐ๋ผ ํค๋๋ฅผ ๊ณ ์ ์ํค๋ ํจ๊ณผ๋ฅผ ๋ํ๋ผ ์ ์๋ค.
<header className={scrolled ? "page-header-wrapper scrolled" : "page-header-wrapper"}>
< !-- html ๊ตฌํ ์๋ต -- >
...
</header>
.page-header-wrapper {
position: fixed;
display: flex;
justify-content: center;
top: 0;
width: 100%;
background-color: #fff;
z-index: 1;
@include content-horizontal-padding;
// ์คํฌ๋กค ์ด๋ฒคํธ ์
&.scrolled {
box-shadow: 1px 2px 18px rgba(0,0,0,.1);
.page-header {
height: 75px;
transition: height 0.3s ease;
}
}
// ์คํฌ๋กค ์ด๋ฒคํธ ์์ ์
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
height: 100px;
@include content-max-width;
}
// css ์๋ต
...
}
- ์ผํญ ์กฐ๊ฑด ์ฐ์ฐ์๋ฅผ ์ด์ฉํ์ฌ
scrolled
๋ณ์์ ์ฐธ ๊ฑฐ์ง ์ฌ๋ถ์ ๋ฐ๋ฅธ<header>
ํ๊ทธ์ ํด๋์ค๋ช ์ ์ง์ ํด์ค๋ค.- ์คํฌ๋กค ์ด๋ฒคํธ ๋ฐ์ ์ ํค๋์ ๊ธธ์ด๋ฅผ ์ค์ด๋ ํจ๊ณผ๋ฅผ ์ฃผ๊ณ ๊ทธ๋ฆผ์ ํจ๊ณผ๋ฅผ ๋ฃ์๋ค.
์ ์ฒด ์ฝ๋
import { Link } from 'gatsby';
import PropTypes from 'prop-types';
import React, { useState, useEffect } from 'react';
import headerTitleImg from '../../assets/capo-profile-img.png'
import './style.scss';
const PageHeader = ({ siteTitle }) => {
const [scrolled, setScrolled] = useState(false);
useEffect(() => {
const handleScroll = () => {
if (!scrolled && window.pageYOffset > 30) {
setScrolled(true);
} else if (scrolled && window.pageYOffset <= 30) {
setScrolled(false);
}
};
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, [scrolled]);
return (
<header className={scrolled ? "page-header-wrapper scrolled" : "page-header-wrapper"}>
<div className="page-header">
<div className="front-section">
<Link className="link" to="/">
<div className="front-section-img-title-wrapper">
<img className="front-section-img" src={headerTitleImg} />
<div className="front-section-title">
{siteTitle}
</div>
</div>
</Link>
</div>
<div className="trailing-section">
<Link className="link" to="/about">
about
</Link>
<Link className="link" to="/posts">
posts
</Link>
</div>
</div>
</header>
);
};
PageHeader.propTypes = {
siteTitle: PropTypes.string,
};
PageHeader.defaultProps = {
siteTitle: ``,
};
export default PageHeader;
๊ณ ์ ๋ ํค๋์ ๊ฐ๋ ค์ง ์ปจํ ์ธ ๊ตฌ์ถํ๊ธฐ
โ??? : ํค๋๊ฐ ์์๊ฒ ์ฌ๋ผ๊ฐ๋ฉด ๋ญํด,,, ๋ด์ฉ์ด ์๋ณด์ด๋๋ฐ;;โ
์ด๋ด๋๋ ํค๋ ๋ฐ๋ก ๋ค์์ ์์นํ ์ปจํ ์ธ ๊ฐ ํค๋ ๋์ด๋งํผ ๋ด๋ ค์ค๊ฒ ํ๋ฉด ๋๋ค. ๋จ, ๋ธ๋ก๊ทธ ํ์ด์ง ์ค ํค๋ ๋ค์์ ์์นํ ๋ชจ๋ ์ปจํ ์ธ ํ๊ทธ์ ํจ๊ณผ๋ฅผ ์ ์ฉํด์ผ ํ๋ค.
ํด๋น ๋ธ๋ก๊ทธ๋ ํฌ์คํธ์ ํค๋, about ํ์ด์ง, index ํ์ด์ง, table-of-contents(๋ชฉ์ฐจ)์ ์คํ์ผ ๋ถ๋ถ์ padding-top: 100px;
์ ์ ์ฉํ์ฌ ํด๊ฒฐํ๋ค.
๊ณ ์ ๋ ํค๋์ ๊ฐ๋ ค์ง ์ต์ปค(์ ๋ชฉ ํ๊ทธ) ๊ตฌ์ถํ๊ธฐ
์์ง ๋๋๊ฒ ์๋๋ค ^^;;; ๋ชจ๋ ์ปจํ
์ธ ๊ฐ ์ ๋ณด์ด๋๊ฐ ํ๋๋ table-of-contents์์ ์ต์ปค๋ก ์ฐ๊ฒฐ๋ <h1>
~ <h6>
ํ๊ทธ๋ก ์ด๋ ์ ํด๋น ํ๊ทธ ๋ถ๋ถ์ด ์ ํํ ํ๋ฉด ์๋จ์ ๋ฑ ๋ง๊ฒ ์ด๋๋๋ ๋ฐ๋์ ์ ๋งคํ๊ฒ ์ ๋ชฉ์ด ๋ณด์ด์ง ์๋ ๋ฌธ์ ๊ฐ ๋จ์์์๋ค.
ํ์ง๋ง ํ๋ ์๋ ์๋ก์ด ๊ฒ์ ์๋ค๊ณ ์ญ์ ์คํ์ค๋ฒํ๋ก์ฐ์ ์ด๋ฐ ๋ฌธ์ ๋ฅผ ํธ์ํ๋ ์ฌ๋์ด ์์๋ค. ํด๊ฒฐ๋ฒ์ ์ ๋ชฉ ํ๊ทธ์ ::before
๊ฐ์ ์์๋ฅผ ์ถ๊ฐํ๊ณ ๊ทธ ๊ธธ์ด๋ฅผ ๊ณ ์ ๋ ํค๋์ ๊ธธ์ด๋งํผ์ผ๋ก ์ง์ ํ์ฌ ๋์์์ ํ๋ ๊ฒ์ด๋ค.
h1::before, h2::before, h3::before, h4::before, h5::before, h6::before {
display: block;
content: " ";
height: 90px;
margin-top: -90px;
visibility: hidden;
}
๊ณ ์ ๋ ํค๋์ ๊ธธ์ด๋งํผ ์ ๋ชฉ ํ๊ทธ ์์ ๊ฐ์์ ์์๋ฅผ ์ค์ ํ๊ณ ํ๊ทธ์ ๊ฐ์์ฑ์ ์จ๊น์ผ๋ก ์ค์ ํ๋ฉด ํ๋ฉด์ ๋ณด์ผ๋๋ ์ด์ ๊ณผ ๋์ผํ ๊ตฌ์ฑ์ด์ง๋ง table-of-contents์ ์ฐ๊ฒฐ๋ ์ต์ปค๋ก ์ด๋ํ ์ ๋ชฉ ํ๊ทธ๋ ํค๋์ ๊ฐ๋ ค์ง์ง ์๊ณ ์ ๋ณด์ธ๋ค!
References
hakyung๋์ ๋ธ๋ก๊ทธ devhak
Dale Seo๋์ ๋ธ๋ก๊ทธ < DaleSeo />
React ๊ณต์๋ฌธ์ - Hook ๊ฐ์
React ๊ณต์๋ฌธ์ - Using the Effect Hook
MDN Web Docs - Window.pageYOffset
stack overflow - offsetting an html anchor to adjust for fixed header