This is a solution to the Multi-step form challenge on Frontend Mentor. Frontend Mentor challenges help you improve your coding skills by building realistic projects.
์ ์์๋ ์๋์ ๋ด์ฉ์ ๋ฐ๋์ ๊ตฌํํด์ผ ํ๋ค.
- ๊ฐ ์คํ ์ ์์๋ฅผ ์์ฑํ ๊ฒ
- ๋ง์ง๋ง ์คํ ์ ์ ์ ์ ์ ํ์ ํ์ ๋ฐ๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ์์ฝํด ๋ณด์ฌ์ฃผ๊ณ , ๊ทธ๋ค์ ์์ฒญ์ ํ์ธํด ์ค ๊ฒ
- ๊ฐ ์ ์ ๊ฐ ์ฌ์ฉํ๋ ๊ธฐ๊ธฐ์ ์คํฌ๋ฆฐ์ ๋ง๋ ์ต์ ํ๋ ๋ ์ด์์์ ๋ณด์ฌ์ค ๊ฒ
- ํ์ด์ง๋ง๋ค ์ํธ ์์ฉ์ด ๊ฐ๋ฅํ ๋ชจ๋ ์์๋ hover๋ foucs ๋ฑ์ ์ ์ฉ์์ผ์ค ๊ฒ
๋ชจ๋ฐ์ผ ๋ฒ์
- HTML5
- CSS3
- JavaScript
- Tailwind CSS - Utility-first ๊ธฐ๋ฐ์ CSS ํ๋ ์์ํฌ
1๏ธโฃ ๋ฐฐ์ด๋ก ๋ณํ์์ผ์ฃผ๋ ๋ง๋ค์ด์ฃผ๋ Array.from()
Array.from()
๋ฉ์๋๋ ๋ฐฐ์ด์ฒ๋ผ ๋์ดํ ์ ์๋ ๊ฐ์ฒด๋ ๋ฌธ์์ด๊ณผ ๊ฐ์ โ์ ์ฌ ๋ฐฐ์ด(Array-like) ํํโ๋ฅผ ๊ฐ์ง ๊ฐ๋ค์ ๋ฐฐ์ด๋ก ๋ณํ์์ผ์ฃผ๋ ๋ฉ์๋์ด๋ค.
Array.from()
๋ฉ์๋๋ฅผ ์ฌ์ฉํ ์ ์๋ ์ฌ๋ก๋ ์๋์ ๊ฐ์ ๊ฒ๋ค์ด ์๋ค.
- Iterable Object, ์ฆ ๋ฐ๋ณต ๊ฐ๋ฅํ ๊ฐ์ฒด
- ๋ฌธ์์ด
- DOM ์์๋ค์ด ๋์ด๋ HTMLCollection
- ๋ฐฐ์ด์ ์ ์ฌํ(Array-like) ๊ฐ์ฒด
- ์์:
{0: โaโ, 1: โbโ, 2: โcโ, length: 3}
- ์์:
Array.from()
๋ฉ์๋๋ ์๋์ ํํ๋ฅผ ๊ฐ์ง๋ค.
Array.from(**์ ์ฌ ๋ฐฐ์ด**, Array.map(ํจ์), thisArg)
// Array.from(์ ์ฌ ๋ฐฐ์ด).map(ํจ์, thisArg) ์ ๋์ผํ๋ค.
- ์ ์ฌ ๋ฐฐ์ด
- ํ์ ์์์ด๋ค. ๋ฐฐ์ด๊ณผ ์ ์ฌํ ํํ๋ฅผ ๊ฐ์ง ๊ฐ์ด ์์ด์ผ, ํด๋น ๊ฐ์ ๊ธฐ์ค์ผ๋ก ๋ฐฐ์ด์ ๋ง๋ค์ด๋ผ ์ ์๋ค.
Array.map(ํจ์)
- ๋ถ๊ฐ ์์์ด๋ฉฐ, ๋ฐฐ์ด์ ์ฐ์ด๋
map()
๋ฉ์๋ ์์ ํจ์๋ฅผ ๊ฐ๋ฆฌํจ๋ค. - ์ฆ,
map()
๋ฉ์๋๋ฅผ ํธ์ถํ์ง ์๊ณ ๋ ๋ ๋ฒ์งธ ์ธ์์map()
๋ฉ์๋ ์์ ๋ค์ด๊ฐ ํจ์๋ฅผ ๋ฐฐ์นํดmap()
๋ฉ์๋๋ฅผ ๋ฐ๋ก ์ ์ฉํ ์ ์๋ค.
- ๋ถ๊ฐ ์์์ด๋ฉฐ, ๋ฐฐ์ด์ ์ฐ์ด๋
thisArg
- ๋ถ๊ฐ ์์์ด๋ค.
์์
map()
๋ฉ์๋ ํจ์์์ ๋ฐ์์ฌthis
์ ๊ฐ์ ๋ฐฐ์นํ๋ค. ์์ผ๋ฉดthis
์ ๊ฐ์map()
๋ฉ์๋๋ฅผ ์ ์ฉํ๋ ๋ฐฐ์ด ์์ ์ด ๋๋ค.
- ๋ถ๊ฐ ์์์ด๋ค.
์์
ํด๋น ์ฑ๋ฆฐ์ง ๊ตฌํํ ๋ ๋น์, step3 ํ์ด์ง๊ฐ ๋ถ๋ฌ์์ง๋ฉด ์ด๋ฏธ ์ ์ ๊ฐ ์ ํํ ํน์ ์ ํํ์ง ์์ ์ต์ ๋ฆฌ์คํธ ์์ญ๋ค์ ์ฐจ์ด๊ฐ ๋๋๋ก ๋ถ๋ฌ์ค๊ฒ ํ ํ์๊ฐ ์์๋ค.
DOM ๊ฐ์ฒด๋ฅผ ํตํด getElementsByClassName()
๋ฉ์๋๋ฅผ ์ด์ฉํด ํด๋น๋๋ ์์ญ์ ๋ถ๋ฌ์์ง๋ง, HTMLCollection
์ด๋ผ๋ ์ ์ฌ ๋ฐฐ์ด์ธ ๊ฐ์ฒด๊ฐ ๋ฐ์์์ก๋ค.
HTMLCollection
- ๊ฐ๋จํ ๋งํด HTML์ ์๋ ์์๋ค์ ๋ฐฐ์ด๋ก ๋ชจ์๋์ ๊ฐ์ฒด์ด๋ค.
- ๋ฐฐ์ด์ฒ๋ผ ๋ณด์ด์ง๋ง,
typeof
์ฐ์ฐ์์Array.isArray()
๋ฉ์๋๋ฅผ ์ด์ฉํด ํ์ธํ๋ฉด ๋ฐฐ์ด์ด ์๋์ ํ์ธํ ์ ์๋ค.
์ด๋ฌ๋ค๋ณด๋, ๊ฐ ์์๋ค๋ง๋ค ์ ์ ๊ฐ ์ด๋ฏธ ์ ํํ ๊ฑด์ง, ์๋๊ฑด์ง๋ฅผ ์์๋๋ก ์กฐ๊ฑด์ ์ฒดํฌํด ํ์๋ฅผ ๋ค๋ฅด๊ฒ ํ๊ธฐ ์ํด forEach()
๋ฉ์๋๋ฅผ ๊ด๋ฆฌํ๊ณ ์ถ๋ค๊ณ ํ๋จํ๊ณ , ์ด๋ฅผ ์ํด ์ ์ฌ ๋ฐฐ์ด์ธ ๊ฐ์ฒด๋ฅผ ๋ฐฐ์ด๋ก ๋ฐ์์ฌ ํ์๊ฐ ์์๋ค.
๋ฐ๋ผ์ Array.from()
์ ํตํด ์ ์ฌ ๋ฐฐ์ด ๊ฐ์ฒด์ธ HTMLCollection
์ ๋ฐฐ์ด๋ก ๋ฐ์์ ์๋์ ๊ฐ์ด forEach()
๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์์ญ๋ณ ์ํ๋ฅผ ๋ณ๊ฒฝํ๋๋ก ๊ตฌํํ๋ค.
const checkboxGroup = document.getElementsByClassName("checkbox");
// checkbox class๊ฐ ๋ค์ด๊ฐ ์์๋ค์ ๋ฆฌ์คํธํ
// Array.from์ ํตํด ๋ฆฌ์คํธํ๋ ์์๋ค์ ๋ฐฐ์ด๋ก ๋ณ๊ฒฝํ๊ณ , forEach ๋ฉ์๋๋ฅผ ์ฌ์ฉํด
// ๊ฐ๊ฐ์ ์์๋ณ๋ก ์ํ๋ฅผ ์ฒดํฌํ์ฌ ์ ์ฉํ๋๋ก ํจ.
**Array.from(checkboxGroup)**.forEach((element) => {
if (selectState.period === "Yearly") {
const costSpan = element.parentNode.childNodes[5];
costSpan.innerText =
element.id === "onlineService" ? "+$10/yr" : "+$20/yr";
}
if (selectState.addon.indexOf(element.id) !== -1) {
element.parentNode.classList.add("border-purplish-blue", "bg-magnolia");
element.classList.add("bg-purplish-blue", "border-purplish-blue");
} else {
element.parentNode.classList.remove(
"border-purplish-blue",
"bg-magnolia"
);
element.classList.remove("bg-purplish-blue", "border-purplish-blue");
}
});
์์ ๋ฐฉ์์ ์ด์ฉํด ๋ฐฐ์ด ์ DOM ์์๋ค์ ์ ์ ๊ฐ ์ ํํ ์ต์ ์ธ์ง๋ฅผ ํ์ธํ์ฌ ์ ํํ๋ค๋ฉด ์ ํํ ์ํ๋ก, ์๋๋ผ๋ฉด ์ ํํ ์ํ๋ฅผ ์ ๊ฑฐํ๋ค.
์ด์ฒ๋ผ Array.from()
์ ์ด์ฉํ๋ฉด ์ ์ฌ ๋ฐฐ์ด์ธ ๊ฐ์ ๋ฐฐ์ด๋ก ๋ณํํด์ค ๋ฟ๋ง ์๋๋ผ, ๋ฐฐ์ด์ด ๋๊ธฐ ๋๋ฌธ์ ๋ฐฐ์ด์ ๋ฉ์๋๋ค์ ๋ฐ๋ก ํ์ฉํ ์๋ ์๋ค.
2๏ธโฃ JavaScript๋ฅผ ์ด์ฉํด ๋ถ๋ชจ, ์์, ํ์ ์ ๋ ธ๋๋ฅผ ๋ฐ์์ค๋ ๋ฐฉ๋ฒ
์ฌ๊ธฐ ๋ ธํธ ํธ๋ฆฌ ๊ทธ๋ฆผ์ ๋ณด์.
์ถ์ : https://www.lambdatest.com/blog/testcafe-selectors/
div
๋ผ๋ ์์๋ฅผ ๋ณด๋ฉด div
์์ img
, h1
, p
, div
๋ผ๋ ์์๊ฐ ์๋ค.
๊ทธ๋ฆฌ๊ณ div
์ ๋์ผ ์ ์์ script
๋ผ๋ ์์๊ฐ ์กด์ฌํ๋ฉฐ div
์ script
๋ ๋ชจ๋ body
์์์ ํฌํจ๋์ด ์๋ค.
์ด๋ฅผ HTML๋ก ํํํ๋ฉด ์๋์ ๊ฐ์ด ๊ทธ๋ ค์ง๋ค.
<body>
<**div**>
<h1></h1>
<p></p>
<img>
<div></div>
</**div**>
<script></script>
</body>
๊ทธ๋ฆฌ๊ณ ์ค์ฌ์ด ๋๋ div ์์์ ๋ค๋ฅธ ์์๋ค๊ณผ์ ๊ด๊ณ๋ฅผ ์ ๋ฆฌํ๋ฉด ์๋์ ๊ฐ์ด ์ ๋ฆฌํ ์ ์๋ค.
- ๋ถ๋ชจ ๋
ธ๋:
body
- ํ์ ๋
ธ๋:
script
- ์์ ๋
ธ๋:
h1
,p
,img
,div
๋ด์ฉ์ ์ ๋ฆฌํ๋ฉด์ ์์์ ๋ ธ๋๊ฐ ๋ฒ๊ฐ์ ๋์ค๋ ๋ฐ์ ํผ๋์ด ์์ ์ ์์ผ๋ฏ๋ก ์ ์ ์ง๊ณ ๊ฐ์.
์ถ์ : https://dionysus2074.tistory.com/137
- ๋
ธ๋(Node)๋, ์ฝ๊ฒ ๋งํด HTML์ ๋ฌธ์๋ฅผ ๊ตฌ์ฑํ๋ ๋จ์์ด๋ค.
์ด ๋
ธ๋์๋ ํ
์คํธ, ์์, ์์ฑ, ์ฃผ์ ๋ฑ์ด ํฌํจ๋์ด ์๋๋ฐ ์ฐ๋ฆฌ๊ฐ ํํ ๋งํ๋
div
์img
,h1
๊ฐ์ ์์๋ ๋ฐ๋ก ์ด ์์ ๋ ธ๋(Element Node)๋ฅผ ๊ฐ๋ฆฌํจ๋ค. - ๊ทธ ๋ฐ์๋ ์์ ๋ ธ๋ ์์ ์ถ๋ ฅ๋๋ ํ ์คํธ ๋ด์ฉ์ ํ ์คํธ ๋ ธ๋(Text Node), HTML ์์ ์์ ์๋ง ๋ณผ ์ ์๊ฒ ์ฃผ์์ ๋ฌ์๋๋ ๋ด์ฉ์ ์ฃผ์ ๋ ธ๋(Comment Node), ์์ ์์ ์ฐ์ด๋ onclick์ด๋ style, id์ ๊ฐ์ ์์ฑ๋ค์ ์์ฑ ๋ ธ๋(Attribute Node)๋ก ๋ถ๋ฆฌ๊ณ ์๋ค.
์ 1๏ธโฃ์์ Array.from()์ผ๋ก ๋ฐ์์จ ๋ฐฐ์ด์ ์์๋ค๊ณผ ๊ด๋ จํ์ฌ ์ ์ ๊ฐ ์ ํํ ์ต์ ์ธ์ง ์๋์ง์ ๋ฐ๋ผ ์คํ์ผ์ ๋ณํ๋ฅผ ์ฃผ๊ณ ์ถ์๋ค.
๊ทธ๋ฌ๋ checkbox class๊ฐ ํฌํจ๋ ์์๋ button
์์ ์์ ์๋ div
์ด๋ฉฐ, ์ ์ ์ ์ ํ ์ฌ๋ถ์ ๋ฐ๋ผ ์ฒดํฌ๋ฐ์ค ํ์์ ๋ณํ์ ์๊ธฐ๋ ๊ฒ ์ธ์๋ ์ฒดํฌ๋ฐ์ค๋ฅผ ํฌํจํ ๋ฒํผ์ ํ
๋๋ฆฌ์ ์์์ ๋ณ๊ฒฝํ ํ์๊ฐ ์์๋ค.
<button
class="flex flex-row items-center border-light-gray border-2 rounded-md my-2 p-3 w-full xsm:w-110 xsm:my-4 xsm:p-4"
onclick="selectAddOnOption(event)">
<div
id="largerStorage"
class="**checkbox** flex justify-center items-center w-6 h-6 border-light-gray border-2 mr-3 rounded pointer-events-none xsm:mr-4">
<img src="assets/images/icon-checkmark.svg" alt="" class="w-4 h-4" />
</div>
<div class="flex flex-col items-start pointer-events-none">
<label
class="block text-marine-blue text-sm font-medium pointer-events-none xsm:text-base "
>Larger storage</label
>
<span class="block text-cool-gray text-xs pointer-events-none xsm:text-sm"
>Extra 1TB of cloud save</span
>
</div>
<span
class="ml-auto text-sm text-purplish-blue font-medium pointer-events-none"
>+$2/mo</span
>
</button>
๋ฐ๋ผ์ ์ฒดํฌ๋ฐ์ค๋ฅผ ๋ด๋นํ๋, checkbox class๊ฐ ํฌํจ๋ div
์ ๋ถ๋ชจ์ธ button
์ ์ฐพ์ ๋ ์์์ class์ ๋ณํ๋ฅผ ์ฃผ๊ธฐ ์ํด ์๋์ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ๋ค.
const checkboxGroup = document.getElementsByClassName("checkbox");
// checkbox class๊ฐ ๋ค์ด๊ฐ ์์๋ค์ ๋ฆฌ์คํธํ
// Array.from์ ํตํด ๋ฆฌ์คํธํ๋ ์์๋ค์ ๋ฐฐ์ด๋ก ๋ณ๊ฒฝํ๊ณ , forEach ๋ฉ์๋๋ฅผ ์ฌ์ฉํด
// ๊ฐ๊ฐ์ ์์๋ณ๋ก ์ํ๋ฅผ ์ฒดํฌํ์ฌ ์ ์ฉํ๋๋ก ํจ.
Array.from(checkboxGroup).forEach((element) => {
if (selectState.addon.indexOf(element.id) !== -1) {
**element.parentNode**.classList.add("border-purplish-blue", "bg-magnolia");
// ์์์ .parentNode ์์ฑ์ ๋ถ๋ฌ์ค๋ฉด ํด๋น ์์์ ๋ถ๋ชจ ๋
ธ๋๋ฅผ ๋ฐ์์ฌ ์ ์๋ค.
// ์ฌ๊ธฐ์ element๋ checkbox class๋ฅผ ํฌํจํ๊ณ ์๋ div์ด๋ฏ๋ก,
// element.parentNode๊ฐ ๋ฐ์์ค๋ ์์๋ div์ ๋ถ๋ชจ์ธ button์ด๋ค.
element.classList.add("bg-purplish-blue", "border-purplish-blue");
} else {
element.parentNode.classList.remove("border-purplish-blue", "bg-magnolia");
element.classList.remove("bg-purplish-blue", "border-purplish-blue");
}
});
์ด์ฒ๋ผ ์์์ ๋ถ๋ชจ ๋
ธ๋๋ฅผ ๋ฐ์์ค๊ณ ์ถ๋ค๋ฉด .parentNode
์์ฑ์ ํ์ฉํ๋ฉด ๋๋ค.
ํํธ, ์ด ๋ด์ฉ ์ธ์๋ ์ ์ ๊ฐ ์ฐ ๊ตฌ๋ ์ธ์ง, ์ ๊ตฌ๋ ์ธ์ง์ ๋ฐ๋ผ์ ํ์ด์ง์ ์๋ ๊ธ์ก ํ์๊ฐ ์ฐ ๋จ์, ์ ๋จ์๋ก ๋ณ๊ฒฝ๋๋ ๋ด์ฉ์ ๊ตฌํํ ํ์๊ฐ ์์๋ค.
๋์ผ ์ ์์ ์๋ ํ์ ๋ ธ๋๋ฅผ ๋ฐ์์ค๊ณ ์ถ์ง๋ง, ํ์ ๋ ธ๋๋ฅผ ์ง์ํด์ฃผ๋ ๊ฒ์ ํด๋น ์์์ ์ง์ ์๊ณผ ์ง์ ๋ค์ด๋ฏ๋ก ๊ฐ๊ฒฉ ๊ฐ์ ์ถ๋ ฅํ๋ ์์์ธ span์ ๋ฐ์์ฌ ์ ์์ด์ ๋ถ๋ชจ ๋ ธ๋์ธ button๋ก ๊ฑฐ์ฌ๋ฌ ์ฌ๋ผ๊ฐ button์ ์์ ๋ ธ๋ ์ค span์ ๋ฐ์์ค๋๋ก ์กฐ๊ฑด์ ์ถ๊ฐํ๋ค.
const checkboxGroup = document.getElementsByClassName("checkbox");
// checkbox class๊ฐ ๋ค์ด๊ฐ ์์๋ค์ ๋ฆฌ์คํธํ
// Array.from์ ํตํด ๋ฆฌ์คํธํ๋ ์์๋ค์ ๋ฐฐ์ด๋ก ๋ณ๊ฒฝํ๊ณ , forEach ๋ฉ์๋๋ฅผ ์ฌ์ฉํด
// ๊ฐ๊ฐ์ ์์๋ณ๋ก ์ํ๋ฅผ ์ฒดํฌํ์ฌ ์ ์ฉํ๋๋ก ํจ.
Array.from(checkboxGroup).forEach((element) => {
if (selectState.period === "Yearly") {
// ํ์ฌ ์ ์ ๊ฐ ๊ตฌ๋
ํ๋ค๋ ์กฐ๊ฑด์ด '์ฐ ๊ตฌ๋
'์ด๋ผ๋ฉด
const costSpan = element.parentNode.childNodes[5];
// button์ ์์ ์์์ธ span์ ์ฐพ์์ ํด๋น ๋
ธ๋์ ์ถ๋ ฅ๋๋ ๊ฐ๊ฒฉ์ด ์กฐ๊ฑด์ ๋ฐ๋ผ
// ๋ฐ๋๋๋ก ์ ์ฉํจ.
costSpan.innerText =
element.id === "onlineService" ? "+$10/yr" : "+$20/yr";
}
if (selectState.addon.indexOf(element.id) !== -1) {
**element.parentNode**.classList.add("border-purplish-blue", "bg-magnolia");
element.classList.add("bg-purplish-blue", "border-purplish-blue");
} else {
element.parentNode.classList.remove("border-purplish-blue", "bg-magnolia");
element.classList.remove("bg-purplish-blue", "border-purplish-blue");
}
});
์์ element.parentNode
๊ฐ ๋ฐ์์ค๋ ๋
ธ๋๋ div
์ ๋ถ๋ชจ์ธ button
๋ผ๊ณ ์ค๋ช
ํ๋ค.
์ด button
์ ์์ ๋
ธ๋ ์ค ๊ฐ๊ฒฉ ์ ๋ณด๋ฅผ ๊ฐ์ง span
์ ๋ด์ฉ์ ๋ฐ๊พธ๊ณ ์ถ์์ผ๋ฏ๋ก ์์ ๋
ธ๋๋ฅผ ๋ฐ์์ค๋ ์์ฑ์ธ .childNodes
๋ฅผ ํ์ฉํด ์์ ๋
ธ๋๋ฅผ ๋ณ์ costSpan
์ ์ ์ฅํ๋ค.
์ฐธ๊ณ ๋ก ๋ถ๋ชจ ๋
ธ๋์ .childNodes
๋ฅผ ํ์ฉํ๋ฉด ์๋์ ๊ฐ์ด ์ฝ์์ฐฝ์์ ์์ ๋
ธ๋๋ค์ ๋ณผ ์ ์๋ค.
์ฌ์ด์ฌ์ด์ ๋ณด์ด๋ ํ
์คํธ ๋
ธ๋๋ /n
, ์ค ๋ฐ๊ฟ ๊ฐ์ด ์ ์ฉ๋์ด์์ด์ ๊ทธ๋ ๋ค.
element.parentNode.childNodes[5]
๋ผ๊ณ ์ง์ ํ๋ ์ด์ ๋ ๋ฐ๋ก ์์ ๋
ธ๋๋ค ์ค์ span
์ด 5๋ฒ ์ธ๋ฑ์ค์ ํด๋นํด์ ๊ทธ๋ ๋ค.
์์ ๋ด์ฉ์ ํ ๋๋ก, ๊ทธ๋ฆฌ๊ณ ์ข ๋ ๋ณด์ถฉํ์ฌ DOM ์์์์ ๋ถ๋ชจ, ์์, ํ์ ์์๋ฅผ ๋ถ๋ฌ์ค๋ ์์ฑ๋ค์ ์ ๋ฆฌํ์๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
- ๋ถ๋ชจ ์์
parentNode
: ๋ถ๋ชจ๊ฐ ๋๋ ๋ ธ๋๋ฅผ ๋ถ๋ฌ์จ๋ค.parentElement
: ๋ถ๋ชจ๊ฐ ๋๋ DOM ์์๋ฅผ ๋ถ๋ฌ์จ๋ค.
- ์์ ์์
childNodes
: ์์์ด ๋๋ ๋ ธ๋๋ค์ ๋ฆฌ์คํธ๋ก ๋ถ๋ฌ์จ๋ค. ์ด ๋, ์ด ๋ฆฌ์คํธ๋ โ์ ์ฌ ๋ฐฐ์ดโ์ด๋ค.firstChild
: ์์์ด ๋๋ ๋ ธ๋๋ค ์ค์ ์ฒซ ๋ฒ์งธ ๋ ธ๋๋ฅผ ๋ถ๋ฌ์จ๋ค.lastChild
: ์์์ด ๋๋ ๋ ธ๋๋ค ์ค์ ๋ง์ง๋ง ๋ ธ๋๋ฅผ ๋ถ๋ฌ์จ๋ค.
- ํ์ ์์
previousSibling
: ๋์ผ ์ ์์ ์๋ ๋ ธ๋ ์ค์ ๋ฐ๋ก ์ ๋ ธ๋๋ฅผ ๊ฐ์ ธ์จ๋ค.previousElementSibling
: ๋์ผ ์ ์์ ์๋ ๋ ธ๋ ์ค์ ๋ฐ๋ก ์ DOM ์์๋ฅผ ๊ฐ์ ธ์จ๋ค.nextSibling
: ๋์ผ ์ ์์ ์๋ ๋ ธ๋ ์ค์ ๋ฐ๋ก ๋ค ๋ ธ๋๋ฅผ ๊ฐ์ ธ์จ๋ค.nextElementSibling
: ๋์ผ ์ ์์ ์๋ ๋ ธ๋ ์ค์ ๋ฐ๋ก ๋ค DOM ์์๋ฅผ ๊ฐ์ ธ์จ๋ค.
๋ณธ์ธ์ด ๊ฐ์ ธ์ค๊ณ ์ถ์ ๋
ธ๋๊ฐ ๋ง์ฝ ์์๋ผ๋ฉด, parentElement
, previousElementSibling
, nextElementSibling
์ ์จ์ ํ์คํ๊ฒ ์์๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด๋ค.
3๏ธโฃ Tailwind CSS ๋ด Important ์ ์ฉ
CSS์์๋ ํน์ ์คํ์ผ ๊ฐ์ ์ด๋ ํ ๋ณํ๋ฅผ ์ฃผ๋๋ผ๋ ๋ฐ๋์ ์ ์ฉํด์ผ ํ๋ ๊ฐ์ด๋ผ๋ฉด !impotant
๋ผ๋ ๊ฐ์ ๋ค์ ๋ถ์์ผ๋ก์ ๋ณํ์ ๋ฐ๋ผ์๋ ํน์ ๊ฐ์ ์ ์ง์์ผ์ค๋ค.
Tailwind CSS๋ class๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๊ธฐ ๋๋ฌธ์ CSS๋ฅผ ๊ฑด๋๋ฆฌ์ง ์์ผ๋ฏ๋ก !impotant
๊ฐ์ ์ง์ ๋ถ์ผ ์๋ ์์ผ๋, Tailwind CSS์์ ์ง์ํด์ฃผ๋ ๊ธฐ๋ฅ์ ํตํด ์ํ๋ ์คํ์ผ์ !important
๊ฐ์ ๋ถ์ฌ์ค ์ ์๋ค.
๋น๋ก ์ด๋ฒ ์ฑ๋ฆฐ์ง์์ ํด๋น ๊ฐ์ ์ฌ์ฉํ ์ฝ๋๋ ์ฌ์ฉํ๋ ค๋ค๊ฐ ํ์๊ฐ ์์ด์ ธ ์ญ์ ํ์ผ๋, ๋์ค์ ๋ ์ฐพ์๋ณผ ๊ธฐํ๊ฐ ์์ ์ ์์ผ๋ ์ด๋ ๊ฒ ๋ฐฐ์ด ๋ด์ฉ์ ๊ธฐ๋กํด๋ณด์๋ค.
๋ฐฉ๋ฒ์ ์๋์ ๊ฐ๋ค.
<script>
tailwind.config = {
important: true,
};
</script>
์์ ๊ฐ์ด ๊ฐ์ tailwind.config
์์ important
์ ๊ฐ์ true
๋ก ์ค์ ํ๋ฉด Tailwind CSS๋ก ์ค์ ํ ๋ชจ๋ class์ ์คํ์ผ์ !important
๋ฅผ ํฌํจํ๊ฒ ๋๋ค.
๋ง์ฝ, ์ ์ฒด ๋ค ์ ์ฉ์ด ์๋ ํน์ class๋ ํน์ id์๋ง ์ ์ฉํ๊ณ ์ถ๋ค๋ฉด ์๋์ ๊ฐ์ด ์ฌ์ฉํ๋ฉด ๋๋ค.
<script>
tailwind.config = {
important: "#onlineService",
};
</script>
์์ฒ๋ผ ์ค์ ํ๋ฉด onlineService
๋ผ๋ id
๊ฐ์ ๊ฐ์ง ์์์๋ Tailwind CSS๊ฐ ๋ฐฐ์น๋ class๋ค์ด ๋ชจ๋ !important
๊ฐ์ ๊ฐ์ง ์คํ์ผ class๋ก ๋ฐ๋๋ค.
ํ์ง๋ง, ๋ณดํต !important
๊ฐ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ํ๋์ ์คํ์ผ ๊ฐ์ ๋ณํ๋ฅผ ์ฃผ๊ณ ์ถ์ง ์์ ๋ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ ๊ฒ์ด๋ค.
์ด๋๋ ์์์ ๋ฐฐ์น๋ Tailwind CSS class์ !
๋ฅผ ๋ถ์์ผ๋ก์ ํด๋น ์คํ์ผ์๋ง !important
๊ฐ์ ๋ถ์ฌํ ์ ์์ผ๋, ํ์ํ ๊ฒฝ์ฐ !
๋ฅผ ์ ๊ทน ํ์ฉํ์.
<input
id="phone"
type="text"
class="border-light-gray border-2 rounded w-full **!px-4 !py-2** outline-0 **focus:!border-purplish-blue** placeholder:text-sm placeholder:font-medium"
placeholder="e.g. +1 234 567 890"
onchange="inputValidationCheck(event)" />
4๏ธโฃ Tailwind CSS ๋ด @apply๋ฅผ ์ด์ฉํ class ๋ณํฉ
Tailwind CSS๋ ์ฌ๋ฌ๊ฐ์ง class๋ค์ ์กฐํฉํด DOM ์์์ ์คํ์ผ์ ๊ตฌ์ฑํ๋๋ก ๋์์ฃผ๋ CSS ํ๋ ์์ํฌ๋ค.
ํ์ง๋ง, ์ด๋ฐ Tailwind CSS๋ฅผ ์ด์ฉํ๋ค๋ณด๋ฉด ๋ฒํผ UI ๋ฑ๊ณผ ๊ฐ์ด ์์ ๋๊ฐ์ ์คํ์ผ์ ์ ์ฉํ๊ฒ ๋๋ DOM ์์๋ค์ด ๊ผญ ์๊ธฐ ๋ง๋ จ์ธ๋ฐ, ๊ทธ๋ด ๊ฒฝ์ฐ ํ๋์ ๋ฒํผ UI์ ์ ์ฉํ class๋ฅผ ์ ๋ถ ๋ณต์ฌํด์ ๊ฐ์ ธ์ค๊ฒ ๋๋ ๊ฒฝ์ฐ๊ฐ ์๊ธด๋ค.
๊ทธ๋ฌ๋ React ๊ฐ์ JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ์ฌ๋๋ค ์ ์ฅ์์๋, ์ด๋ ๊ฒ ๋ฐ๋ณต์ ์ธ ์์๋ฅผ ์์ฑํ๊ธฐ๋ณด๋ค๋ ์ปดํฌ๋ํธ์ฒ๋ผ ๋์ผํ ์์๋ฅผ ํ๋๋ก ๋ฌถ์ด์ฃผ๊ณ ์ถ์ ์ถฉ๋์ด ์๊ธธ ๊ฒ์ด๋ค.
Tailwind CSS์์๋ ์ด๋ฌํ ๋์ผํ ์คํ์ผ class๋ค์ ํ๋๋ก ๋ฌถ์ด์ค ์ ์๋, ์ ํํ๋ ํจํด๋ค์ ํ๋๋ก ๋ฌถ์ด์ค ์ ์๋ @apply
๋ผ๋ ๊ธฐ๋ฅ์ ์ง์ํ๋ค.
๋จ, ์ด ๊ธฐ๋ฅ์ Tailwind CSS์์ ์ถ๊ตฌํ๋ ์ด๋ฆ์ ๊ณ ๋ฏผํ์ง ์๊ณ class๋ง์ผ๋ก ํธํ๊ฒ ์ฌ์ฉํ ์ ์๋ ๋ฐฉํฅ์ ๋ฒ์ด๋๋ ์ฌํญ์ด๋ผ Tailwind CSS์์๋ ์ฌ์ฉ ๋ฐฉํฅ์ ๊ถ์ฅํ๊ณ ์์ง ์๋ค. (๋จ์ํ ๊น๋ํ๊ฒ ๋ณด์ด๊ฒ ํ๋ ค๊ณ ์ปดํฌ๋ํธ์ฒ๋ผ ์ค์ด๋ ๋ฐฉ์์ Tailwind CSS์์๋ ์ข์ํ์ง ์๋๋ค.)
์์
๋น์์๋ ์ด ๋ฐฉ์์ ์ฌ์ฉํด๋ณด๋ ค๊ณ ํ์ผ๋ Tailwind CSS๊ฐ ์ถ๊ตฌํ๋ ๋ฐฉํฅ๊ณผ ๋ค๋ฅธ ๊ฒ ๊ฐ๋ค๊ณ ํ๋จํ๊ณ , ๋ @apply
๋ฅผ ์ด์ฉํด class๋ฅผ ๋ฌถ๋ ๋ฐฉ์์ด ์ค์ ๋ก๋ ์ ์ฐ์ด๋์ง ํ๋จ์ด ์์ง ์์๋ค.
๊ทธ๋์ ์ค์ ์ ์ฉํด๋ณด๊ณ ๋์์ด ์ด๋ป๊ฒ ๋๋์ง ์์๋ณธ ๋ค์ ๋ค์ ์๋๋๋ก class๋ฅผ ๋๋ ค๋จ์ง๋ง, ๊ธฐ๋ก์ ํตํด ์ด๋ ๊ฒ Tailwind CSS์ class๋ฅผ ํ๋๋ก ๋ฌถ๋ ๋ฐฉ๋ฒ์ด ์๋ค๋ ๊ฒ์ ๋จ๊ฒจ๋๋ ค ํ๋ค.
์ฐ์ , CSS ํ์ผ์์ ์๋์ ๊ฐ์ด ์ค์ ํด์ผ ํ๋ค.
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.form-input {
@apply border-light-gray border-2 rounded w-full px-4 py-2 outline-0 focus:border-purplish-blue placeholder:text-sm placeholder:font-medium;
}
}
@layer
๋ Tailwind CSS ๋ด์์ ์ฌ์ฉ์๊ฐ ์ง์ ํ ์คํ์ผ ์ํธ๋ฅผ ๋ชจ์๋์ ๋๋ ํ ๋ฆฌ ํน์ ๋ฒํท์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค.
์ฐ์ , Tailwind CSS ๋ด์ ๋๋ ํ ๋ฆฌ๋ ํฌ๊ฒ base
์, components
, ๊ทธ๋ฆฌ๊ณ utilities
๋ก ์ด๋ฃจ์ด์ ธ ์๋ค.
base
: DOM ์์ ๊ทธ ์์ฒด(h1
,h2
,p
โฆ)๋ฅผ ๊ฐ๋ฆฌํค๋ ๋ ์ด์ด์ด๋ค.components
: ์ฃผ์ ์คํ์ผ๋ค์ Tailwind CSS์ class๋ค๋ก ๋ชจ์๋์ ๋ ์ด์ด์ด๋ค.utilities
: CSS ์ค์ ์ํธ์์ฉํ๋ ์์ฑ๋ค(focus
,filter
,hover
โฆ)์ Tailwind CSS์ class๋ก ๋ชจ์๋์ ๋ ์ด์ด์ด๋ค.
์ฌ์ฉํ๊ณ ์ ํ๋ ์์๋ Tailwind CSS์ ํด๋์ค๋ค์ ํ๋์ ์ปดํฌ๋ํธ์ฒ๋ผ, ์ ํํ๋ ํจํด์ผ๋ก ๋ฌถ์ด์ฃผ๊ธฐ ์ํด class๋ก ๋ฌถ์ด์ฃผ๋ ๊ฒ์ด๋ค.
components
๋ ์ด์ด์ ์ฌ์ฉํ๊ณ ์ ํ๋ class์ ์ด๋ฆ๊ณผ ํจ๊ป @apply
๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํด ํด๋น class์ ๋ฌถ์ด์ค class๋ค์ ๋์ดํด ๋ฌถ์ด์ฃผ๋ฉด ํด๋น class๋ฅผ ์๋์ฒ๋ผ ์ฌ์ฉํ ์ ์๋ค.
<input
id="email"
type="email"
class="**form-input**"
placeholder="e.g. stephenking@lorem.com"
onchange="inputValidationCheck(event)" />
์ด๋ ๊ฒ @apply
๋ฅผ ์ด์ฉํด ์์ฝ๊ฒ Tailwind CSS์ class๋ค์ ์ ํํ๋ ํ๋์ class๋ก ๋ง๋ค์ด ์ฌ์ฉํ ์ ์๋ค.
๋ค๋ง, ์ด ๋ฐฉ์์๋ ๊ฐ์ฅ ํฐ ๋ฌธ์ ๋ ์ ์ง๋ณด์๊ฐ ์ด๋ ต๋ค๋ ๊ฒ์ด๋ค. Tailwind CSS์์๋ ์ฌ๋ฌ๊ฐ์ง class๋ฅผ ๋์ดํ๊ณ JavaScript ๋ฑ์ ํตํด์ class๋ฅผ ์ถ๊ฐ, ๋ณ๊ฒฝ, ์ ๊ฑฐํจ์ผ๋ก์ ์์ฝ๊ฒ ๊ฐ๋จํ ๋ณํ๋ฅผ ์ค ์ ์๋ค.
๊ทธ๋ฌ๋, ์์ ๊ฐ์ด ํ๋์ class๋ก ๋ฌถ์ด๋ฒ๋ฆฌ๋ฉด ํ๋๋ก๋ง ๊ด๋ฆฌํ๊ฒ ๋๋ฏ๋ก ์ํ๋ ์คํ์ผ์ ์ถ๊ฐ, ๋ณ๊ฒฝ, ์ ๊ฑฐํ๋ ๊ฒ์ด ์ด๋ ต๋ค. (JavaScript์์ DOM ์์์ style์ ํตํด์ ๋ณ๊ฒฝํด์ค์ผ ํ ๊ฒ์ด๋ค.)
๋ฐ๋ผ์, Tailwind CSS์์๋ ์ ๋ง๋ก ์ค์ํ ๋ชฉ์ ์ด ์์ง ์๋ค๋ฉด ํ๋์ class๋ก ๊ด๋ฆฌํ๋ ๊ฒ์ ์ถ์ฒํ์ง ์๊ณ ์์ผ๋ @apply
๋ฅผ ์ฌ์ฉํ๊ธฐ ์ ์๋ ์ด ์ ์ ์๊ฐํ๊ณ ํ๋จํด ์ฐ๋๋ก ํ์.
5๏ธโฃ Tailwind CSS ๋ด ์ผํ์ฑ์ธ ์คํ์ผ์ ์ ์ฉํ๋ ๋ฐฉ๋ฒ
Tailwind CSS๊ฐ ์ ๊ณตํ๋ class ์ค์์๋ ์ ์ ๊ฐ ํ์๋ก ํ๋ class๊ฐ ์๋ค๋ฉด tailwind.config
์ extend
๋ฑ์ ํ์ฉํ์ฌ ๋ฒ์๋ ์์ ๋ฑ์ ์ถ๊ฐํ ์ ์๋ค.
๊ทธ๋ฌ๋, ์ฌ๋ฌ ๋ฒ ์ฌ์ฉํ class๊ฐ ์๋๋ผ๋ฉด ์ด๋ฌํ ์ถ๊ฐ๋ ๋นํจ์จ์ ์ผ ์ ์๋ค๊ณ ํ๋จํ๋ค. ๊ฐ๋ น 1ํ์ฑ์ผ๋ก๋ง ์ฐ์ด๋ ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง ์คํ์ผ์ ์๊ฐํ๋ค๋ฉด, ๊ตณ์ด ํ์ฅํด์ ์ธ ํ์๋ ์์ ํ ๋๊น.
Tailwind CSS์์๋ ์ด๋ฌํ ์ผํ์ฑ์ผ๋ก๋ง ์ฐ์ผ ์ ์๋ ์คํ์ผ์ tailwind.config
๋ฑ์ ํตํด ์ค์ ํ์ง ์๊ณ class ๋ด์์ ๋ฐ๋ก ์ฌ์ฉํ ์ ์๋๋ก ๊ธฐ๋ฅ์ ์ง์ํด์ฃผ๊ณ ์๋ค.
์ฌ์ฉ ๋ฐฉ๋ฒ์ ๊ฐ๋จ, **์ค์ ํ๊ณ ์ ํ๋ style ์์-[์ผํ์ฑ ๊ฐ]**
์ ์ถ๊ฐํ๋ฉด ๋๋ค.
์ฑ๋ฆฐ์ง์์ ์ด ๋ฐฉ๋ฒ์ ์ด์ฉํด ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ค๋๋ก ํ๋ค.
<nav
class="**bg-[url('./assets/images/bg-sidebar-mobile.svg')]** bg-no-repeat bg-cover flex flex-row justify-center gap-4 xsm:hidden w-full h-44 py-8 relative"></nav>
์ด๋ฏธ์ง ๋ฟ๋ง ์๋๋ผ ํฐํธ์ ํฌ๊ธฐ๋, border๋ padding, margin ๊ฐ ๋ฑ ๋ค์ํ ์คํ์ผ ์์๋ค์๋ ์ผํ์ฑ์ผ๋ก ์ฐ์ด๋ ๊ฐ๋ค์ด๋ผ๋ฉด ์์ ๋ฐฉ์์ผ๋ก ํํํด ์ธ ์ ์๋ค.
<div class="rounded-[12px]"></div>
<!-- border-radius: 12px -->
<p class="text-[14px]"></p>
<!-- font-size: 12px -->
- [Javascript] ๋ถ๋ชจ, ์์, ํ์ ๋ ธ๋(node)์ ์์(element) ์ฐพ๊ธฐ
- Tailwind CSS Docs
- javascript - Most efficient way to convert an HTMLCollection to an Array - Stack Overflow
- HTMLElement - ์ํ์ฝ๋ฉ
- HTMLCollection - ์ํ์ฝ๋ฉ
- javascript - Most efficient way to convert an HTMLCollection to an Array - Stack Overflow
- Array.from() - JavaScript | MDN
- regex - What regular expression will match valid international phone numbers? - Stack Overflow
- JavaScript : email validation - w3resource
- Frontend Mentor - @DrunkenNeoguri
- Twitter - ํ์์น๋ ๋๊ตฌ๋ฆฌ(@develop_neoguri)