๋ฌธ์ ๋ชฉ๋ก์ผ๋ก ๋์๊ฐ๊ธฐ
๐ก์ง์์๋ต์ https://github.com/pul8219/TIL
Issues
ํญ์ ์๋ง์ step ์ด์์์ ๋จ๊ฒจ์ฃผ์ธ์. โก๏ธ Issueํญ์ผ๋ก ์ด๋
- ์์ฑ์: Wol-dan (@pul8219)
- ์คํฐ๋ ์ฃผ์ : FrontEnd ๋ฉด์ ์คํฐ๋ https://gitlab.com/siots-study/topics/-/wikis/%EC%8B%AC%ED%99%941
- ๊ณต๋ถ ๋ฒ์: STEP 40 ~ 42 ์ฝ๋์คํผ์ธ 76 CSS Rendering - 3ํ์ฐจ
- ๊ธฐํ: 06/05(ํ ) ~ 06/08(ํ) (STEP 40), 06/12(ํ ) ~ 06/15(ํ) (STEP 41), 06/19(ํ ) ~ 06/22(ํ) (STEP 42)
๐ฌ
- CSSOM(CSS Object Model): ์๋ฐ์คํฌ๋ฆฝํธ์์ CSS๋ฅผ ์กฐ์ํ ์ ์๋๋ก ํ๋ API ์งํฉ. CSS๋ฅผ ๊ฐ์ฒดํ ์์ผ ๋ชจ๋ธ๋งํ ๊ฒ์ด๋ค.
- DOM(Document Object Model): HTML(Document)์ ๊ฐ์ฒดํ์์ผ ํ๋ก๊ทธ๋๋ฐ ๊ฐ๋ฅํ๊ฒ ๋ง๋ ๊ฒ. ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๊ฐ DOM ๊ตฌ์กฐ์ ์ ๊ทผํ ์ ์๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋ ์ธํฐํ์ด์ค๋ก ๋ฌธ์ ๊ตฌ์กฐ, ์คํ์ผ, ๋ด์ฉ ๋ฑ์ ๋ณ๊ฒฝํ ์ ์๊ฒ ํ๋ค. ์๋ฐ์คํฌ๋ฆฝํธ๋ก DOM API๋ฅผ ์ด์ฉํ๋ฉด html์ ์ง์ ์์ ํ์ง ์๊ณ ๋ DOM์์์ ๋ณํ๋ฅผ ์ค ์ ์๋ค.
ํ๊ทธ ๊ทธ ์์ฒด๋ ์ค์ฒด๊ฐ ์๋ ์ผ์ข ์ ์ปจํ ์ด๋ ๋ฐ์ค๊ฐ์ ๊ฒ์ด๊ณ , ์ค์ ๋ด์ฉ์ ํ๊ทธ ์์ ์๋ค.
ex) ์๋ฅผ ๋ค์ด <style>
ํ๊ทธ ์์ฒด์๋ sheet
๋ผ๋ ์ค์ฒด๊ฐ ์๊ณ <canvas>
ํ๊ทธ์ ์ค์ฒด๋ getContext๋ก ์ป์ canvastocontext์ ์๋ค. ์ด๋ฅผ DOM์ ํฌํจ์ํค๋ ค๋ฉด ๋ฐ๋์ canvas๋ผ๋ ํ๊ทธ๋ก ๊ฐ์ธ์ผ๋ง(wrapping) html์ ๋ฃ์ ์ ์๋ค.
์ด๋ฌํ ํ๊ทธ ๋ด์ ๋ด์ฉ๋ค์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ html ๋ฒ์ ๋ง๋ค ๊ฐ๊ธฐ ๋ค๋ฅด๋ค. ์๋ฅผ ๋ค์ด canvas๋ getcontext๋ผ๋ ๋ฉ์๋๋ฅผ ํธ์ถํด ์ฌ์ฉํ์ง๋ง ๊ตฌํ element๋ค์ ์์ฑ์ผ๋ก ์ฌ์ฉํ๋ค.
Style DOM Element
ใด sheet: CSSStyleSheet
ใด CSSRules: CSSRuleList
ใด Item: CSSStyleRule -> Type, SelectorText, Style(CSSStyleDeclaration), ...
์์ ์ฝ๋๋ก CSSOM์ ์ฌ์ฉ๋ฒ์ ๋ํด ๋ ์์ธํ ์์๋ณด์. style ํ๊ทธ์ sheet ์์ฑ์ ์ฌ์ฉํ๋ฉด CSSStyleSheet
๋ผ๋ ๊ฐ์ฒด๋ฅผ ์ป์ ์ ์๋ค.
<style id="s">
.test {
background: #ff0;
}
</style>
const el = document.querySelector('#s');
const sheet = el.sheet;
const rules = sheet.cssRules;
๋ํ sheet์์ cssRules
๋ผ๋ list๋ฅผ ๊ฐ๊ณ ์๋ค. ์ด ์์๋ ์ฌ๋ฌ rule
๋ค์ด ๋ค์ด์๋ค. (์ด๋ ์ ์ฌ๋ฐฐ์ด๋ก ๋ฐฐ์ด๊ณผ ๋น์ทํ ์น๊ตฌ์ด๋ค. length๋ผ๋ ์์ฑ์ ์์ง๋ง ๋ฐฐ์ด ๋ฉ์๋๋ ๋จนํ์ง ์๋ list์ด๋ค.)
rules์ 0๋ฒ์งธ๋ถํฐ๋ rule๋ค์ด ๋ค์ด์๋ค. ์ด ์์ ์์ rules[0]์๋ style ๋ด์์ ์ ์ํ .test ๋ด์ฉ์ด ๋ค์ด๊ฐ์๋ค. ๋ง์ฝ ์ ์ํ style์ด ์ฌ๋ฌ๊ฐ์๋ค๋ฉด [0], [1], [2], ...
์ ์ฐจ๋ก๋๋ก ๋ค์ด์์ ๊ฒ์ด๋ค.
- type
- selectortext (ex.
.test
) - style๊ฐ์ฒด(DOM์๋ ๋ค์ด์๋ style ๊ฐ์ฒด)
const el = document.querySelector('#s');
const sheet = el.sheet;
const rules = sheet.cssRules;
const rule = rules[0];
console.log(rule);
/**
* CSSStyleRule
* ใดselectorText: ".test"
* ใดstyle:
* ใดbackground: "rgb(255, 255, 0)"
* ใดtype: 1
* ...(๋ ๋ง์ ์์ฑ๋ค์ด ์๋ค)
*/
์ด๋ ๊ฒ html์์ text๋ก ์ ์ํ๋ css๊ฐ ๋ธ๋ผ์ฐ์ ์ ํด์๊ณผ์ ์ ๊ฑฐ์น๊ณ ๋๋ฉด ๋ฉ๋ชจ๋ฆฌ์ ๊ฐ์ฒด ํํ๋ก ์ ์ฅ๋๊ธฐ ๋๋ฌธ์ ํ๋ํ๋์ ์ ๊ทผ์ด ๊ฐ๋ฅํ๋ค. ์ด๋ ๊ฒ html์ text๋ก ์ ์ด๋์ css๊ฐ ๋ฉ๋ชจ๋ฆฌ์์ ๊ตฌ์กฐ๋ก ๋ฐ๋๋ ๊ณผ์ ์ด CSSOM์ด๋ค.
๊ทธ๋ ๋ค๋ฉด ๊ฐ๊ฐ rule๋ค์ ๋ค์ด์๋ type
์์ฑ์ ๋ฌด์์ผ๊น? ์ด๋ CSS ์ ์์ ๋ํ rule์ ์๋ฏธํ๋ค. type
์๋ ๊ต์ฅํ ๋ง์ ์ข
๋ฅ๊ฐ ์๋ค.
- type:1 | STYLE_RULE | CSSOM (์์ ์๋ ๊ฐ์ฒด๊ฐ CSSOM ๋ผ๋ ๋ป)
- type:2 | CHARSET_RULE | CSSOM ->
@charset
- type:3 | IMPORT_RULE | CSSOM
- type:4 | FONT_FACE_RULE | CSSOM -> ์ธ๋ถ ํฐํธ ๋ถ๋ฌ์ฌ ๋ ์ฌ์ฉํ๋
font-face
๋ฅผ ์๊ฐํด๋ณด๊ธฐ - type:7 | KEYFRAMES_RULE | css3-animations -> css3-animations๋ผ๋ ํน๋ณํ ๊ฐ์ฒด๋ก ๋ฐ๋์ด ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅ๋๋ค.
sheet.insertRule('์คํ์ผ', ๋ฃ์ index ๋ฒํธ)
insertRule
์ ์ฌ์ฉํด sheet์ ์ถ๊ฐํ๋ฉด CSS๋ฅผ ๋์ ์ผ๋ก ์ถ๊ฐํ ์ ์๋ค.
const el = document.querySelector('#s');
const sheet = el.sheet;
const rules = sheet.cssRules;
const rule = rules[0];
// rules์ ๋ง์ง๋ง ๋ถ๋ถ์ ์ถ๊ฐํ๊ธฐ ์ํด์ ์ธ๋ฑ์ค๋ฅผ rules.length๋ก ์ฃผ์๋ค.
sheet.insertRule('.red{background:red}', rules.length);
sheet.insertRule('.blue{background:blue}', rules.length);
console.log(sheet);
insertRule
์ ํ ๋ค, sheet๋ฅผ ์ถ๋ ฅํด cssRules๋ฅผ ํ์ธํด๋ณด๋ฉด ์ฐ๋ฆฌ๊ฐ ์ง์ ํ red, blue ํญ๋ชฉ์ด ์ถ๊ฐ๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์ค์ CSS ์คํ์ผ์ ์ถ๊ฐ๊ฐ ๋์๋์ง ์์๋ณด๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด html์ body์์ ํ์ธํ๋ค.
<!-- ์คํํด๋ณด๋ฉด CSS๊ฐ ์ ์ ์ฉ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค. -->
<div class="red">red</div>
<div class="blue">blue</div>
cssRules๋ ์์๊ฐ ๊ต์ฅํ ์ค์ํ๊ธฐ ๋๋ฌธ์
insertRule
์ ํ ๋ ๋ ๋ฒ์งธ ์ธ์๋ก css๋ฅผ ์ถ๊ฐํ ์์๋ฅผ ์ง์ ํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค. (์๋ฅผ ๋ค์ด CSS์์ .test๋ผ๋ css๋ฅผ ๋ ๋ฒ ์ฃผ๋๋ฐ ๋จผ์ ์ ์ธ๋ ๊ฒ์ width:100, ๋ ๋ฒ์งธ๋ก ์ ์ธ๋ ๊ฒ์ width:200์ด๋ผ๋ฉด ์๋ ์ ์ธ๋ 200์ด ๋จนํ๋ค.)
๋ง์ฝ insert๋ฅผ ๋์ค์ ํ๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น? ๋์ค์ insertํ๋๋ผ๋ CSS๊ฐ ์ ์ฉ๋๋์ง ์์๋ณด์. onclick์ ์ด์ฉํ์ฌ red๋ถ๋ถ์ ํด๋ฆญํ ๋๋ง๋ค insertRule์ด ์๋ํ๊ฒ ์ฝ๋๋ฅผ ์์ฑํ๋ค.
const el = document.querySelector('#s');
const sheet = el.sheet;
const rules = sheet.cssRules;
const rule = rules[0];
document.querySelector('.red').onclick = (_) => {
sheet.insertRule('.red{background:red}', rules.length);
sheet.insertRule('.blue{background:blue}', rules.length);
};
์ ์ฝ๋๋ฅผ ์คํํด red๋ฅผ ํด๋ฆญํ๋ฉด CSS๊ฐ ๋ณํํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์ด์ ๋ ๋ค์๊ณผ ๊ฐ๋ค. document์ ๋ฑ๋ก๋ sheet๊ฐ ๋ณํํ๋ฉด ๋ ๋๋ง์ ๋ค์ ํ๊ธฐ ๋๋ฌธ์ด๋ค.(repaint, reflow๊น์ง)
document์ ๋ฑ๋ก๋ stylesheet๋ผ๋ ๊ฑด ์ด๋ป๊ฒ ์๋๊ฑธ๊น? ์ด๋ ์ฐ๋ฆฌ๊ฐ html์ style ํ๊ทธ๋ฅผ ์ด๋ฏธ ๋ฑ๋กํ๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฑ๋ก๋ stylesheet๋ ๋ฐ๋ก ๊ด๋ฆฌํ๊ฒ ๋๋๋ฐ ์ด๋ ๋ธ๋ผ์ฐ์ ์์ ํ์ธํ ์ ์๋ค.
๋ธ๋ผ์ฐ์ ์ฝ์์ฐฝ์์ document.styleSheets
๋ฅผ ์
๋ ฅ. (โ ๊ฐ์์์ CSSStyleSheet๊ฐ 2๊ฐ ๋์ค๋๋ฐ ๋ด ๊ฒฐ๊ณผ์์ ํ๋๋ง ๋์จ๋ค) CSSStyleSheet ์์ cssRules๋ฅผ ๋ณด๋ฉด ์ฐ๋ฆฌ๊ฐ insertRule๋ก ์ถ๊ฐํ CSS๋ค์ด ๋ฐ์๋ผ์๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ๋ง์ฝ CSSStyleSheet๊ฐ ์ฌ๋ฌ๊ฐ๋ผ๋ฉด ์๋์ ์๋ ์คํ์ผ ํ๊ทธ ์ผ์๋ก ์ฐ์ ์์๊ฐ ๋๋ค.(์๋์๋ ๊ฒ์ด ์์ ์๋ ๊ฒ์ ์ด๊ธด๋ค.)
๋ํ ๋ธ๋ผ์ฐ์ ์ฝ์์ฐฝ์์ ์ฐ๋ฆฌ๊ฐ ๋ง๋ ์คํ์ผ ์์ฑ์ ๋ ์ ์๋ค. document.styleSheets[0].disabled = true
๋ก ๋ฐ๊พธ๋ฉด onclick์ผ๋ก ์ ์ฉํ๋ CSS๊ฐ ๋์ํ์ง ์๋ ๊ฒ์ ๋ณผ ์ ์๋ค. (์ ์ฉํ๋ ์คํ์ผ์ํธ๋ฅผ ๊บผ๋ฒ๋ฆฐ ๊ฒ์)
deleteRule
์ ์ด์ฉํด CSS๋ฅผ ์ ๊ฑฐํ ์๋ ์๋ค. ์ธ๋ฑ์ค๋ง ์ง์ ํด์ฃผ๋ฉด ๋๋ค. insertRule ์ฝ๋์ ๋ค์ ์ฝ๋๋ฅผ ์ถ๊ฐํด๋ณด์. blue๋ฅผ ํ๋ฒ ํด๋ฆญํ๋ฉด ๋ง์ง๋ง์ผ๋ก ์ถ๊ฐํ๋ .blue
์ ๋ํ CSS๊ฐ ์ญ์ ๋ ๊ฒ์ด๋ค.
document.querySelector('.blue').onclick = (_) => {
sheet.deleteRule(rules.length - 1);
};
CSSOM์ ๋ค๋ฃจ๋ ๊ฒ์ DOM์ style ๊ฐ์ฒด๋ฅผ ๋ค๋ฃจ๋ ๊ฒ๊ณผ ์์ ํ ๋ค๋ฅด๋ค. CSSOM์ ์ด์ฉํ๋ฉด stylesheet๋ฅผ ๋์ ์ผ๋ก ์กฐ์ํ ์ ์๋ค๋ ๊ฒ์ด๋ค. inline ํ๊ทธ๋ฅผ ๊ฑด๋๋ฆฌ๋ ๊ฒ๋ณด๋ค ์ข๋ค. css object(stylesheet) ํ๋๋ง ๊ฑด๋๋ฆฌ๋ฉด ์คํ์ผ์ด ์ ์ฉ๋์๋ ์์๋ค์ ์ผ๊ด์ ์ผ๋ก ์ ์ฉ๋๋๊น ์ข์ ๊ฒ! ์ด ๊ฒฝ์ฐ์ ์ฑ๋ฅ์์ ์ ํ๊ฐ ์์ ๊ฒ์ด๋ค. (+ ์ ์์ ์ฒ๋ผ ํ๊ทธ์๋ ๋ฏธ๋ฆฌ class๋ฅผ ์ง์ ํด๋์๋ ์๊ด์๋ค) dom์ ๊ทธ๋๋ก ๋๊ณ cssom๋ฅผ ์ฌ์ฉํด ํด๋์ค๋ dom๊ตฌ์กฐ์ ๋ง๊ฒ cssobject๋ง ๋ฐ๊ฟ์ฃผ๋ ๊ฒ. ํ๋ํ๋ dom์ style์ ์กฐ์ ํ๋ ๊ฒ๋ณด๋ค ํจ์ฌ ๋น ๋ฅด๋ค. ์๋ฅผ ๋ค์ด red๋ผ๋ ์ด๋ฆ์ ๊ฐ์ง class๊ฐ ๊ต์ฅํ ๋ง์ ๋ cssom๋ฅผ ํ์ฉํ๋ฉด ์์์ ๋งํ ๊ฒ์ฒ๋ผ ์ผ๊ด์ ์ผ๋ก css ์ ์ฉํ ์ ์๊ฒ๋๋ค.
<div class="red red1">red</div>
<div class="blue blue1">blue</div>
<div class="red">red</div>
<div class="blue">blue</div>
<div class="red">red</div>
<div class="blue">blue</div>
<div class="red">red</div>
<div class="blue">blue</div>
<div class="red">red</div>
<div class="blue">blue</div>
<div class="red">red</div>
<div class="blue">blue</div>
<div class="red">red</div>
<div class="blue">blue</div>
<div class="red">red</div>
<div class="blue">blue</div>
<div class="red">red</div>
<div class="blue">blue</div>
document.querySelector('.red1').onclick = (_) => {
sheet.insertRule('.red{background:red}', rules.length);
sheet.insertRule('.blue{background:blue}', rules.length);
};
document.querySelector('.blue1').onclick = (_) => {
sheet.deleteRule(rules.length - 1);
};
์คํํด๋ณด๋ฉด css object๋ฅผ ์กฐ์ ํจ์ผ๋ก์จ css๋ฅผ ์ผ๊ด์ ์ผ๋ก ์ฝ๊ณ ๋น ๋ฅด๊ฒ ๋ฐ๊ฟ ์ ์์์ ๋ณผ ์ ์๋ค.
์ง๊ธ๊น์ง CSS object ๋ชจ๋ธ์ ๋ํ ๊ธฐ๋ณธ์ ์ก์์ผ๋ ์ด๋ฅผ ์ด์ฉํด CSS ์ ์ฒด๋ฅผ ์คํฌ๋ฆฝํธ๋ฅผ ํตํด ์์ ์ ์ผ๋ก ํต์ ํ ์ ์๋ ํ๋ ์์ํฌ๋ฅผ ๋ง๋ค์ด๋ณด์.
- Vendor Prefix ํด๊ฒฐ
- CSS ๋์ ์กฐ์
- Vendor Prefix
Vendor Prefix๋?
๊ฐ์ CSS๋๋ผ๋ ์น๋ธ๋ผ์ฐ์ ๋ง๋ค ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ์ง์๋ ์ ์๋ค. ๊ท ์ผํ๊ฒ ์คํ์ผ ์์ฑ์ ํํํ๊ฒ ํ ์ ์๊ฒ ๋ธ๋ผ์ฐ์ ๋ง๋ค prefix(์ ๋์ฌ)๋ฅผ ๋ถ์ฌ ์คํ์ผ์ ์ง์ ํด์ค ์ ์๋๋ฐ ์ด๋ฅผ Vendor Prefix๋ผ๊ณ ํ๋ค. ์๋ฅผ๋ค๋ฉด IE or Edge๋ Vendor Prefix๊ฐ
-ms-
์ด๋ค.
Vendor Prefix๋ ์คํ์ค์ ์์ฑ์ ํ์ธํด๋ณด๋ ์๋ฐ์ ์๋ค. ์๋ฅผ ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ ๊ฒ์ด ํ์ฉ๋์ง ์๋๋ค๋ ๊ฒ์ด๋ค. '๋ธ๋ผ์ฐ์ ๊ฐ ํฌ๋กฌ์ด๋ผ๋ฉด border-radius์ webkit์ ๋ถ์ด์' ์ด๋ฐ๊ฒ ํตํ์ง ์๋๋ค. ํฌ๋กฌ ๋ฒ์ 54์์๋ webkit์ ๋ถ์ฌ์ผํ๋๋ฐ ๋ฒ์ 66์์๋ ๋ถ์ด๋ฉด ์๋ผ ์ด๋ฐ์์ผ๋ก ์๋ํ๊ธฐ ๋๋ฌธ. border-radius๊ฐ ์๋๋ฅผ ๊ทธ๋๊ทธ๋ ํ์ธํ ์ ๋ฐ์ ์๋ค. ๋ฏธ๋ฆฌ ๊ณต์์ ๋ง๋ค ์ ์๊ณ ์ด๋ค ์์ฑ์ด ์์์ง ์์์ง๋ ์คํ๋์ค์ ํ์ธํด์ผํ๋ค.
Runtime Fetch ํด์ผํ๋ค.
- Unsupported Property
๋ธ๋ผ์ฐ์ ๋ง๋ค ์ง์ํ์ง ์๋ ์์ฑ์ด๋ ๊ฐ์ด ์กด์ฌํ๋ค. ๋ํ์ ์ผ๋ก ie7์ rgba๋ฅผ ์ฌ์ฉํ๋ฉด ๋ธ๋ผ์ฐ์ ๊ฐ ์ฃฝ๋๋ค.
Graceful Fail ์คํจ๋ฅผ ์กฐ์ฉํ๊ฒ ์ ์ฒ๋ฆฌํ๊ณ ์ถ๋ค๋ ๊ฒ(์์๋ ์ผ์ฒ๋ผ)
- Hierarchy Optimize
๊ณ์ธต๊ตฌ์กฐ๋ฅผ ์ต์ ํํด์ผํ๋ ์ผ๋ค์ด ๋ง์ด ์๊ธด๋ค. ์คํ์ผ์ํธ๊ฐ ์์ญ๊ฐ ์์ ๋, ์๋ฅผ ๋ค์ด .red์ ๋ํด ๊ณ์ฐํ๋ ค๋ฉด, stylesheet ๊ฐ์ฒด๋ฅผ ๋ค ๋๋ฉด์(stylesheet 0๋ฒ๋ถํฐ 1,2,.. ์์ผ๋ก ๊ณ์ฐํ๋ค) ๊ทธ ์์ rule list๋ฅผ ๋ค ๋๋ฉด์ ๊ทธ ์์ rule๋ฅผ ๋ค ๋๋ฉฐ ๊ทธ์์ ์์ฑ๋ค์ ๋ชจ๋ ํฉ์ณ ๊ณ์ฐ์ ํด์ผํ๋ค.(์ค๋๊ฑธ๋ฆผ) ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ styleํ๊ทธ๋ linkํ๊ทธ๋ฅผ ์ฌ๋ฌ๊ฐ ๋ฌ๋ฉด ๋ธ๋ผ์ฐ์ ๊ฐ ์ฃฝ์ด๋๋๊ฒ์ด๋ค.(CSS ์ค์ฒฉ ๊ณ์ฐ)
css ๊ฐ์ฒด ๋ชจ๋ธ์ ์ด์ฉํ๋ฉด ์ด๋ฅผ ํ๋์ ๊ฐ์ฒด๋ก ํตํฉํ๊ณ sheet.disabled = true;
๋ฅผ ์ด์ฉํด ๋ช๊ฐ๋ ๋๋ฉด ๋๋ค.
Style(CSSStyleDeclare)
- ๊ฐ์ฅ ์์กด์ฑ์ด ์์
- dom์๋ ์๊ณ cssrule์๋ ์๋
- vendor prefix ์ฒ๋ฆฌ
โฌ๏ธ (์์กด ๋ฐฉํฅ, '์๊ณ ์๋ค'๋ ๊ฒ)
Rule(CSSRule)
- style์ ์์ ํ ์ ์๋ rule
โฌ๏ธ
CSS(StyleSheet)
- rule์ ์ฌ๋ฌ๊ฐ ์์ ํ ์ ์๋ sheet ๊ฐ์ฒด
์์กด์ฑ์ด ์๋ style๋ถํฐ ๋ง๋ค์(๊ทธ๊ฒ ์ฝ๋ค)
Rule ๋ด๋ถ์ Style์ด๋ผ๋ ๊ฐ์ฒด๊ฐ ๋ค์ด์๋ค(CSSStyleDeclaration) ์ด๊ฑธ ์ถ์ํํด ํ๋์ ๊ฐ์ฒด๋ก ๋ง๋ค ๊ฒ์ด๋ค. ์ ์ถ์ํ๋ฅผ ํ๋๋ฉด, Style์ ๋ ๋ก ์์ฑ์ ๋ฃ์ผ๋ฉด Vendor Prefix๊ฐ ํด๊ฒฐ์ด ๋์ง ์๊ธฐ ๋๋ฌธ์ด๋ค. ์ฐ๋ฆฌ๊ฐ ๋ง๋ ๊ตฌ์กฐ๋ฅผ ํตํด ๋ฃ์ด์ผ Vendor Prefix๊ฐ ์๋์ผ๋ก ์ฒ๋ฆฌ๋๋๋ก ํ๋ ค๊ณ ํ๋ ๊ฒ์ด๋ค.
Style์ด๋ผ๋ ํด๋์ค๋ฅผ ๋ง๋ค์ด๋ณด์
const Style = (_ => {
const prop = new Map, prefix = 'webkt,moz,ms,chrome,o,khtml'.split(',');
const NONE = Symbol();
const BASE = document.body.style;
const getKey = key => {...}; // end of getKey()
return class{...};
})();
prefix
: ๊ทธ์ ์ vendor prefix ๋ฌธ์์ด์ด ํ์ํ๋ค(๋ธ๋ผ์ฐ์ ์ ๋ฐ๋ผ ์ฌ๋ฌ๊ฐ์ง vendor prefix๊ฐ ์์) split์ผ๋ก ๋ฌธ์์ด์ ์ชผ๊ฐ prefix๋ผ๋ ๋ฐฐ์ด๋ก ๊ฐ์ง๊ณ ์๋๋ค.- ์๋ฃํ์ด Map์ธ
prop
: key๋ ์์ฑ(ex.background
), value๋ ํด๋น ๋ธ๋ผ์ฐ์ ์์ vendor prefix๋ฅผ ํฌํจํด ์ง์ํ๋ ์ง์ง ์ด๋ฆ์ด ๋ค์ด๊ฐ ๊ฒ์ด๋ค. None
: ์ด๋ค ์์ฑ์ ์ด ๋ธ๋ผ์ฐ์ ๊ฐ ์ง์ํ์ง ์๋๋ค๋ ๊ฒ์ ํ์ํ๊ธฐ ์ํ ์ฅ์น- ์์์๋ ์ค๋ช
ํ๋ฏ ์ด๋ค ์์ฑ์ด ์๋์ง ์๋์ง๋ ์คํ๋์ค์ ํ์ธํด์ผํ๋ค. (ex. border-radius๋ผ๋ ์์ฑ์ด ์๋?) ์๋์ง ์๋์ง๋ฅผ ๋๊ตฌ์๊ฒ ๋ฌผ์ด๋ณผ ๊ฒ์ธ๊ฐ? ๋ฐ๋ก ์ด๋ค ๋ธ๋ผ์ฐ์ ๋ ๋ฐ๋์ ๊ฐ์ง๊ณ ์๋
document.body.style
์ ๋ฌผ์ด๋ณด๋ฉด ๋๋ค. body์ ์๋ ์์ฑ์ด๋ผ๋ฉด ์๋ ๊ฒ์ด ํ์ ๋๋ ๊ฒ!
๊ฒฐ๊ตญ, ์์์ ์ธ๊ธํ๋ ๊ฒ์ฒ๋ผ
Unsupported Property
์ง์ํ์ง ์๋ ์์ฑ์ ๋ถ๋๋ฝ๊ฒ ์คํจํ๊ธฐ ์ํดNONE
์ ์ฌ์ฉํ ๊ฒ์ด๊ณ- Vendor Prefix๋ฅผ Runtime์ Fetchํ๊ธฐ ์ํด์
BASE
, ์ฆ body์ ์๋ style ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ ๊ฒ์ด๋ค. ๋ผ๋ ๊ฒ!
์ฝ๋์์ getKey() ๋ถ๋ถ
const Style = (_ => {
// ...
const getKey = key => {
if(prop.has(key)) return prop.get(key);
if(key in BASE) prop.set(key, key);
else if(!prefix.some(v =>{
// prefix๋ฅผ ๋ถ์ธ ์์ฑ์ ์กด์ฌํ๋๊ฐ?
// ...
})){
prop.set(key, NONE);
key = NONE;
}
return key;
}; // end of getKey()
return class{...};
})();
-
์ง์ง ์ด๋ฆ์ ์ด๋ป๊ฒ ์ป๋? ํ์ค ์ด๋ฆ, ์๋ฅผ ๋ค์ด
border-radius
๋ฅผ ๋ณด๋์ ๋ ์ด๋ฅผ ์ง์ํ๋ ์ด๋ค ๋ธ๋ผ์ฐ์ ๊ฐ ์์ ๋border-radius
๋ผ๋ ์ด๋ฆ์ ๊ทธ๋๋ก ๋ฐ์์ผ ํ๊ณ , ์ด๋ฅผ ์ง์ํ์ง ์๊ณ webkit border-radius๊ฐ ์๋ ์ด๋ค ๋ธ๋ผ์ฐ์ ๋webkit border-radius
๋ฅผ ๋ฐ์์ผ ํ๋ค. ์์ ์ง์ํ์ง ์๋ ๋ธ๋ผ์ฐ์ ๋ผ๋ฉดNONE
์ ๋ฐํํด์ค์ผํ๋ค. ์ด๋ฐ ์ญํ ์ ํ๋ ๊ฒ์ดgetKey
ํจ์์ด๋ค. ํ์ค์ด๋ฆ์ ์ฃผ๋ฉด ํด๋น ๋ธ๋ผ์ฐ์ ๊ฐ ์ง์ํ๋ ์ง์ง ์ด๋ฆ์ ๋ฐํํ๋ ํจ์์ธ ๊ฒ์ด๋ค.์ด๋ฅผ ๋งค๋ฒ ๊ณ์ฐํ๋ฉด ๋นํจ์จ์ ์ด๊ธฐ ๋๋ฌธ์ ํ๋ฒ ์ฐ์ฐํด ์ง์ง ์ด๋ฆ์ ์์๋์ผ๋ฉด ์ด๋ฅผ ์บ์์ ์ ์ฅํ๋๋ก ๊ตฌํํ ๊ฒ์ด๋ค. ์ด ์บ์๊ฐ
prop
์ด๋ค. ์บ์์ ์๋ค๋ฉด ์บ์์ ์๋ ๊ฐ์ผ๋ก ์ค๋ค๋ ๊ฒ์ด๋ค. ์ฆ ๋ณด๋ธ ํ์ค ์ด๋ฆ(key)์ ํด๋นํ๋ ๊ฒ์ด ์บ์์์ ๋ค์ด์๋ค๋ฉด ๊ทธ ์บ์ ์์ ์๋ ์ง์ง ์ด๋ฆ์ ๋ฆฌํดํด์ค๋ค. (๋งค๋ฒ ์ด if๋ฌธ ๋ฐ์ ๊ณ์ฐํ๊ธฐ ์ซ๊ธฐ ๋๋ฌธ์) ํ๋ฒ์ด๋ผ๋ ์ง์ง ์ด๋ฆ์ ํ์ธํ ์ ์ด ์๋ค๋ฉด ์บ์์ ์ ์ฅํด๋๋ค๋ ๊ฒ์ด๋ค. -
if(key in BASE) prop.set(key, key);
: body style์ border-radius๊ฐ ์๋ค๋ฉด ์บ์์ ์ ์ฅํด๋๋ค๋ ๊ฒ์ด๋ค. -
else if
๋ฌธ: ๋ง์ฝ border-radius๊ฐ ์๋ค๋ฉด? prefix๋ฅผ ๋ถ์ธ border-radius๋ ์๋๊ฐ? ์์ผ๋ฉด key๋ฅผ NONE์ผ๋ก ์ค์ ํ๋ค.
Array.prototype.some()
arr.some(callback[, thisArg])
callback์ด ์ด๋ค ๋ฐฐ์ด ์์๋ผ๋ ์ฐธ์ธ(truthy)๊ฐ์ ๋ฐํํ๋ ๊ฒฝ์ฐ์ true์ด๊ณ ๊ทธ ์ธ์๋ false์ด๋ค.
else if
๋ฌธ์ ์์ธํ ๋ค์ฌ๋ค๋ณด์(์ฝ๋ ์ฃผ์ ์ฐธ๊ณ )
else if(!prefix.some(v =>{
// prefix๋ฅผ ๋ถ์ธ ์์ฑ์ ์กด์ฌํ๋๊ฐ?๋ฅผ ์์๋ณผ ์ฐจ๋ก
// ๋ฉ์๋ some์ ์ธ์์ธ callback ํจ์ ๋ถ๋ถ์ด๋ค.
// webkitBackground ์ด๋ฐ์์ผ๋ก ์์ฑ๋ช
์ฒซ๊ธ์๊ฐ ๋๋ฌธ์์ด๋ฏ๋ก ํ์ค key์ธ background์ ์์ vendor prefix๋ฅผ ๋ถ์ด๊ณ ๋๋ฌธ์๋ก ๋ฐ๊พธ๊ณ ๋๋จธ์ง ๋ค์ธ 'ackground'๋ฅผ ๋ถ์ฌ์ฃผ๋ ์์
์ด ํ์ํ๋ค.
const newKey = v + key[0].toUpperCase() + key.substr(1);
if(newKey in BASE){ // prefix๋ถ์ธ ์์ฑ์ด body์ ์๋ค๋ฉด,
prop.set(key, newKey); // border-radius ๋ถ๋ฅด๋ฉด prefix๋ฅผ ๋ถ์ธ ์ง์ง ์ด๋ฆ์ ์บ์์ ์ ์ฅ
key = newKey; // ๋ฆฌํดํ key๋ ๋์ด์ ์๋ ํค๊ฐ ์๋๋ผ newKey
return true; // ์ง์ง ์ด๋ฆ์ ์ฐพ์์ผ๋ ์ฌ๊ธฐ์ ๋์ด ๋ผ๋๊ฒ. some์ ๋ ๋์ง ์์๋ ๋๋ค๊ณ ๋์ด๋ฒ๋ฆฌ๋ ๊ฒ์ด๋ค.
} else return false;
})){
// some์ ๊ฒฐ๊ณผ๊ฐ false์ผ ๊ฒฝ์ฐ์๋ง ์ฌ๊ธฐ์ ๋ค์ด์จ๋ค.
prop.set(key, NONE);
key = NONE;
}
return key; // ๊ทธ๋ฅ key๊ฐ ๋ฆฌํด๋๋ ์ง, newKey๊ฐ ๋ฆฌํด๋๋ ์ง, NONE์ด ๋ฆฌํด๋ ๊ฒ์
๋ค์๋ถํด if(prop.has(key)) return prop.get(key);
์ ๊ฑธ๋ ค์(ํ๋ฒ ํ ๊ฑด ์บ์์ ์ด๋ฏธ ์๊ธฐ ๋๋ฌธ์ ๊บผ๋ด์ฐ๊ธฐ๋ง ํ๋ฉด ๋๋ค.) ๊ทธ ์๋์ ๊ณ์ฐ๋ค์ ํ์ง ์์๋ ๋๋ค.
์ง๊ธ๊น์ง ํ ๊ฒ์ Vendor Prefix๋ฅผ ๋ฐํ์์ ์กฐ์ฌํด์ ์ง์ง ์ด๋ฆ์ ์ป๊ฒ๋๋ ์ ๋ต์ ์ง ๊ฒ์ด๋ค. ์์์ ๋งํ๋ฏ ๊ณต์์ด ํตํ์ง ์๋๋ค. ๋ชจ์ง๋ผ ์ด๋ฉด ๋ชจ๋ ์์ฑ์ moz๋ฅผ ๋ถ์ฌ์ผ์ง~ ์ด๊ฒ ๊ฐ๋ฅํ์ง ์๋ค๋ ๊ฒ์ด๋ค. ๋ฐ๋์ ๋น์์ ํ์ธํด์ผํ๋ค. ํฌ๋กฌ์ด๋ผ๊ณ ์ผ๊ด์ ์ผ๋ก webkit์ ๋ชจ๋ ๋ถ์ฌ๋ ์ ๋๋ค. ์์ฑ๋ง๋ค ๋ค ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ด๋ค. ๋ชจ๋ ๋ธ๋ผ์ฐ์ ์ ๋์ํ๋ ค๋ฉด ์ด ๋ฐฉ๋ฒ๋ฐ์ ์๋ค.
const Style = ((_) => {
// ...
const getKey = (key) => {
// ...
}; // end of getKey()
return class {
constructor(style) {
this._style = style;
} // ์์ฑ์์ style๊ฐ์ฒด๋ฅผ ์ค๋ค. ์ด ํด๋์ค๋ style ๊ฐ์ฒด๋ฅผ ์๊ณ ํ์ด๋๋ค.
// ํค๋ฅผ ์ป๊ธฐ(์คํ์ผ ์ํธ์ ์๋ background๋ผ๋ ๊ฐ์ ์ป๊ณ ์ถ๋ค๋ฉด?)
get(key) {
key = getKey(key); // ๋ฐ๋์ getKey์ ๋ณด๋ด์ ์ง์ง ์ด๋ฆ์ ์ป์ด์ผ ํ๋ค.
if (key === NONE) return null; // ๋ธ๋ผ์ฐ์ ๊ฐ ์ง์ํ์ง ์๋ ๊ฒฝ์ฐ ๋ถ๋๋ฝ๊ฒ null์ ๋ฆฌํดํ๊ธฐ // Unsupported Property ๋ฌธ์ ํด๊ฒฐ!
return this._style[key]; // ์ด๋ฆ์ด ์๋ค๋ฉด ์ง์ง ์ด๋ฆ์ผ๋ก style๊ฐ์ฒด์ ํด๋น ์์ฑ๊ฐ์ ๊ฐ์ ธ์ค์
}
set(key, val) {
key = getKey(key);
// key๊ฐ NONE์ด ์๋๋ฉด
if (key !== NONE) this._style[key] = val; // ๊ฐ์ ์ค์
// NONE์ด๋ฉด ์คํ์ผ์ ์์ ๊ฑด๋ค์ง ์๋๋ค(Graceful Fail)
return this; // set์ ๊ณ์ ํธ์ถํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์์ set์ ์ธ ์ ์๊ฒ this๋ฅผ ๋ฆฌํด
}
};
})();
class๋ฅผ ๋ง๋๋ ๋ถ๋ถ๋ ์ค์ํ์ง๋ง getKey()
ํจ์๊ฐ ์์ฑ์ ์ป์ด์ค๋ ๋ฐฉ์์ด ๊ฐ์ฅ ํต์ฌ์ด๋ค.
const Style = ((_) => {
const prop = new Map(),
prefix = 'webkt,moz,ms,chrome,o,khtml'.split(',');
const NONE = Symbol();
const BASE = document.body.style;
const getKey = (key) => {
if (prop.has(key)) return prop.get(key);
if (key in BASE) prop.set(key, key);
else if (
!prefix.some((v) => {
const newKey = v + key[0].toUpperCase() + key.substr(1);
if (newKey in BASE) {
prop.set(key, newKey);
key = newKey;
return true;
} else return false;
})
) {
prop.set(key, NONE);
key = NONE;
}
return key;
}; // end of getKey()
return class {
constructor(style) {
this._style = style;
}
get(key) {
key = getKey(key);
if (key === NONE) return null;
return this._style[key];
}
set(key, val) {
key = getKey(key);
if (key !== NONE) this._style[key] = val;
return this;
}
};
})();
const el = document.querySelector('#s');
const sheet = el.sheet;
const rules = sheet.cssRules;
const rule = rules[0];
const style = new Style(rule.style); // โญ
style.set('borderRadius', '20px').set('boxShadow', '0 0 0 10px red');
console.log(rule); // js์์ ์ง์ ํ css ์คํ์ผ๋ค์ด ์ ๋ค์ด๊ฐ์๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
<!DOCTYPE html>
<html>
<head>
<style id="s">
.test {
background: #ff0;
}
</style>
</head>
<body>
<div class="test">abc</div>
<script src="basicTest.js"></script>
</body>
</html>
- vendor prefix๋ฅผ ๊ฐ์ญํ๊ฑฐ๋ ์ด์ ๊ตฌ์ ๋ฐ์ง ์๊ณ ์คํ์ผ ์์ฑ์ ์ง์ ํ ์ ์๊ฒ ๋์๋ค.
this
๋ฅผ ๋ฆฌํดํ๋๋ก ํ์ผ๋ฏ๋กset
์ฒด์ด๋์ด ๊ฐ๋ฅํ๋ค.
๋ธ๋ผ์ฐ์ ๋ด๋ถ์ ๋ฐ์๋ ๋๋
์ด๋ ๊ฒ ๋ฐ์๋ ์๋,
์ด๋ ๊ฒ ๋ฐ์๋ ์๋ ์๋ ๊ฒ์ด๋ค.
rule์ ๊ตฌ์ฑํ๋ ๊ฒ๋ค(type, selectortext, style, ...)์ ํ๋ํ๋ ๊ตฌํํ๋ ๊ฒ์ด ์๋๋ผ rule์ ํต์ผ๋ก ๊ฐ์ธ๋ ๊ฐ์ฒด๋ฅผ ๋ง๋ค๋ฉด ๋๋ค.
const Style = ((_) => {
// ... ์์์ ๋ง๋ค์๋ Style ์ฝ๋
})();
const Rule = class {
constructor(rule) {
this._rule = rule;
this._style = new Style(rule.style);
}
get(key) {
return this._style.get(key);
}
set(key, val) {
this._style.set(key, val);
return this;
}
};
const el = document.querySelector('#s');
const sheet = el.sheet;
const rules = sheet.cssRules;
const rule = new Rule(rules[0]); // ์ฐ๋ฆฌ๊ฐ ๋ง๋ ํด๋์ค์ธ Rule์ rule์ ์ง์ ํ๋ค.
rule.set('borderRadius', '20px').set('boxShadow', '0 0 0 10px red'); // ์ด๋ ๊ฒ ํ๋ฉด rule์์ style์ ์์์ ๋ฐ์์ด ๋ ๊ฒ์ด๋ค. (rule์ด style์ ์์ ํ๋ ๊ตฌ์กฐ์ด๋ค.)
- Rule์ ์์ฑ์์์ rule๊ณผ ์์์ ๋ง๋ค์๋
Style
๊ฐ์ฒด๋ฅผ ์ง์ ํ๋ค. get
,set
๋Style
์get
,set
์ ์ด๋ค.- rule์
get
,set
์ ํ ๋ Style ๊ฐ์ฒด์ ์์กดํ๊ฒ ๋๊ณ , Style ๊ฐ์ฒด๋get
,set
์getKey
๋ผ๋ ํจ์์ ์์กดํ๊ฒ ๋ผ ์ง์ง ์ด๋ฆ์ธ์ง ํ์ธํ๋ ์ ์ฐจ๋ฅผ ์งํํ๋ค. ์ด๋ก์จ ๋ณด๋ค ๋ ์์ ํ ๋ฐฉ๋ฒ์ผ๋ก css๋ฅผ ์กฐ์ํ ์ ์๊ฒ ๋์๋ค.
rule์ add/remove ํ๋ ๊ฒ์ด Sheet ๊ฐ์ฒด์ ์ฃผ ๊ธฐ๋ฅ์ด๋ค. ๋์ ์ผ๋ก CSS ์ถ๊ฐ/์ญ์ ํ๊ธฐ ์์ ๋ฐฐ์ ๋ ์ฝ๋๋ฅผ ๋ฐํ์ผ๋ก ์ด๋ป๊ฒ ๊ตฌํํด์ผํ ์ง ์๊ฐํด๋ณด์.
const sheet = el.sheet;
const rules = sheet.cssRules;
// ์ถ๊ฐ // rules์ length๊ฐ ํ์ํจ์ ์ ์ ์๋ค.
sheet.insertRule('.red{background:red}', rules.length);
// ์ญ์
sheet.deleteRule(rules.length - 1);
const Style = ((_) => {
// ...
})();
const Rule = class {
constructor(rule) {
this._rule = rule;
this._style = new Style(rule.style);
}
// ...
};
// โญ
const Sheet = class {
constructor(sheet) {
this._sheet = sheet;
this._rules = new Map();
}
add(selector) {
const index = this._sheet.cssRules.length;
this._sheet.insertRule(`${selector}{}`, index); // selector๋ฅผ ์ง์ ํ์ฌ ๋น rule์ ๋ฃ๋๋ค.
const cssRule = this._sheet.cssRules[index]; // ์ง์ง cssRule์ด ๋ด๊ธด๋ค.
const rule = new Rule(cssRule); // ์ฐ๋ฆฌ๊ฐ ๋ง๋ Rule ๊ฐ์ฒด์ cssRule์ ๋ฃ๋๋ค.
this._rules.set(selector, rule); // key๋ฅผ selector๋ก ํด์ rule์ rules์ ์ ์ฅํด๋๋๋ค. -> ๋์ค์ selector๋ก ์กฐํํ ์๋ ์์
return rule; // add๋ฅผ ์คํํ๋ฉด ์ด๋ ๊ฒ ๋ง๋ค์ด์ง rule์ด ๋ฆฌํด๋๋ค.
}
remove(selector) {
// ์ด์ ์ deleteRule์ index๋ฅผ ์ง์ ํด์ผ๋ง delete๊ฐ ๊ฐ๋ฅํ๋ค.
// ํ์ง๋ง ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฑด selector๋ก Rule์ ์ง์ฐ๋ ๊ฒ์ด๋ค.(.red, .blue๋ฅผ ํน์ ํ์ฌ ์ง์ฐ๊ณ ์ถ์ ๊ฒ์ด๋ค.)
if (!this._rules.contains(selector)) return; // rules์ ํด๋น selector๋ก๋ rule์ด ์๋ค๋ฉด ๊ทธ๋ฅ ๋ฆฌํด
const rule = this._rules.get(selector)._rule; // Rule ๊ฐ์ฒด์๋ _rule์ด ์๋ค.(์์์ ์ง Rule ๊ฐ์ฒด ๊ด๋ จ ์ฝ๋ ์ฐธ๊ณ ํ๊ธฐ)
Array.from(this._sheet.cssRules).some((cssRule, index) => {
// cssRules๋ฅผ ๋๋ฉด์ rule๊ณผ ๊ฐ๋ค๋ฉด ํด๋น rule์ ์ญ์
if (cssRule === rule._rule) {
this._sheet.deleteRule(index);
return true; // true๋ฅผ ๋ฆฌํดํด some ํจ์๋ฅผ ๋ฉ์ถ๋ค.
}
});
}
get(selector) {
return this._rule.get(selector);
}
};
// Sheet ๊ฐ์ฒด๋ฅผ ์ง์ ์ฌ์ฉํด๋ณด์
const sheet = new Sheet(document.styleSheets[0]);
sheet.add('body').set('background', '#f00');
sheet.add('.test').set(
'cssText',
`
width:200px;
border:1px solid #fff;
color: #000;
background: #fff
`
);
<!DOCTYPE html>
<html>
<head>
<style></style>
</head>
<body>
<div class="test">test</div>
<script src="test.js"></script>
</body>
</html>
cssText
๋ฅผ ์ฌ์ฉํด ์ฌ๋ฌ๊ฐ์ง ์์ฑ์ ํ๋ฒ์ ๋ฐ์ด๋ฃ์ ์ ์๋ค. ์ด๋๋ ์๋ฐ์คํฌ๋ฆฝํธ ์์ฑ๋ช ๋์ CSS ์ด๋ฆ ๊ทธ๋๋ก ์ธ ์ ์๋ค๋ ์ฅ์ ์ด ์๋ค. ๋ค๋ง ์ด ์์ ์์๋ ์ด๋ ๊ฒ ์์ฑํ๊ฒ ๋๋ฉด ์ฐ๋ฆฌ๊ฐ ๋ง๋ key-value ์์คํ ์ด ๋ฌด์๋ฏธํด์ง๋ค.
๊ทธ๋ฌ๋ ์ฐ๋ฆฌ๊ฐ ์ง๊ธ๊น์ง ๊ตฌํํ ๊ฒ์ CSS rule type 1๋ฒ์ ์ปค๋ฒํ ๊ฒ์ ๋ถ๊ณผํ๋ค. import ๋๋ ๊ฒฝ์ฐ๋? @font-face ๋ฑ์ ์ด๋ป๊ฒ ํ ๊ฒ์ธ๊ฐ?
keyframes์๋ from
, to
์ ๊ฐ์ keyframe animation ์
๋ ํฐ๊ฐ ๋ค์ด๊ฐ์๋ค.(DOM ์
๋ ํฐ๊ฐ ์๋) ์ด๋ฅผ ์ด๋ป๊ฒ ๊ฐ์ฒดํ ์ํฌ ๊ฒ์ธ๊ฐ?
const Style = ((_) => {
const prop = new Map(),
prefix = 'webkt,moz,ms,chrome,o,khtml'.split(',');
const NONE = Symbol();
const BASE = document.body.style;
const getKey = (key) => {
if (prop.has(key)) return prop.get(key);
if (key in BASE) prop.set(key, key);
else if (
!prefix.some((v) => {
const newKey = v + key[0].toUpperCase() + key.substr(1);
if (newKey in BASE) {
prop.set(key, newKey);
key = newKey;
return true;
} else return false;
})
) {
prop.set(key, NONE);
key = NONE;
}
return key;
}; // end of getKey()
return class {
constructor(style) {
this._style = style;
}
get(key) {
key = getKey(key);
if (key === NONE) return null;
return this._style[key];
}
set(key, val) {
key = getKey(key);
if (key !== NONE) this._style[key] = val;
return this;
}
};
})();
const Rule = class {
constructor(rule) {
this._rule = rule;
this._style = new Style(rule.style);
}
get(key) {
return this._style.get(key);
}
set(key, val) {
this._style.set(key, val);
return this;
}
};
const Sheet = class {
constructor(sheet) {
this._sheet = sheet;
this._rules = new Map();
}
add(selector) {
const index = this._sheet.cssRules.length;
this._sheet.insertRule(`${selector}{}`, index);
const cssRule = this._sheet.cssRules[index];
// โญ @keyframes์ ์ฒ๋ฆฌํ๊ธฐ ์ํ ๋ถ๊ธฐ๊ฐ ์๊ฒผ๋ค.
let rule;
if (selector.startsWith('@keyframes')) {
rule = new KeyFramesRule(cssRule);
} else {
rule = new Rule(cssRule);
}
this._rules.set(selector, rule);
return rule;
}
remove(selector) {
if (!this._rules.contains(selector)) return;
const rule = this._rules.get(selector)._rule;
Array.from(this._sheet.cssRules).some((cssRule, index) => {
if (cssRule === rule._rule) {
this._sheet.deleteRule(index);
return true;
}
});
}
get(selector) {
return this._rule.get(selector);
}
};
// โญ
// KeyFramesRule์ Sheet ๊ฐ์ฒด์ ์๊น์๊ฐ ๋๊ฐ๋ค.
// keyframes ๋ด๋ถ๊ฐ ์คํ์ผ ์ํธ์ฒ๋ผ ์๊ฒผ๊ธฐ ๋๋ฌธ์ด๋ค.
// ์ ์ผํ ์ฐจ์ด์ ์ rule์ ๋ฃ์ ๋ insertRule ๋์ appendRule์ ์จ์ผํ๋ค.(w3c ํ์ค)
const KeyFramesRule = class {
constructor(rule) {
this._keyframe = rule;
this._rules = new Map();
}
add(selector) {
const index = this._keyframe.cssRules.length;
this._keyframe.appendRule(`${selector}{}`);
const cssRule = this._keyframe.cssRules[index];
const rule = new Rule(cssRule);
this._rules.set(selector, rule);
return rule;
}
remove(selector) {
if (!this._rules.contains(selector)) return;
const rule = this._rules.get(selector)._rule;
Array.from(this._keyframe.cssRules).some((cssRule, index) => {
if (cssRule === rule._rule) {
this._keyframe.deleteRule(index);
return true;
}
});
}
};
const sheet = new Sheet(document.styleSheets[0]);
const size = sheet.add('@keyframes size'); // keyframes rule ๊ฐ์ฒด๊ฐ ๋ฆฌํด๋จ
size.add('from').set('width', '0'); // Rule ๊ฐ์ฒด๊ฐ ๋ฆฌํด๋๋ฏ๋ก set ์ ํธ์ถํ ์ ์์
size.add('to').set('width', '500px');
- keyframes animation์ ๋์ ์ผ๋ก ์ ์ํด ์ธ ์ ์๊ฒ ๋์๋ค.
- ์๋ฅผ ๋ค์ด ๋ฐํ์์ ์ ๋๋ฉ์ด์ ์ ๋๋ฅผ ์กฐ์ ํด์ผํ๋ ๊ฒฝ์ฐ keyframe์ ์ฐ๊ธธ ํฌ๊ธฐํ๊ณ ์๋ฐ์คํฌ๋ฆฝํธ ์ ๋๋ฉ์ด์ ์ ์ผ๋ค๋ฉด ์ด์ ๊ทธ๋ฌ์ง ์์๋ ๋๋ค!
https://drafts.css-houdini.org/css-typed-om/
- CSS draft๋ฅผ ๋ด๋ ๊ทธ๋ฃน
- Chrome์ ๊ฒฝ์ฐ houdini ํ๋ก์ ํธ์์ ์งํํ๋ draft๋ค์ ๊ฑฐ์ ๋ฐ์ํ๋ค. ์ฌ์ค ๊ตฌ๊ธ์๊ฒ ํ์ค์ด ์๋์ด๋ ์๊ด์ด ์๋ ๊ฒ์ด๋ค. (w3c๋ฅผ ์คํ๋ ค ๊ท์ฐฎ์ํจ)
- ๊ตฌ๊ธ์ด houdini ํ๋ก์ ํธ๋ฅผ ํตํด Chrome์ ๋ฐ์ํ ์คํ๋ค์ ์ฌ๋ฌ๊ฐ์ง๊ฐ ์๋ค. ๊ทธ์ค paint api๋ฅผ ์ด์ฉํ๋ฉด ์์์ ๋ฐฑ๊ทธ๋ผ์ด๋์ (์ด๋ฏธ์ง๋ฅผ ๋ฃ๋๊ฒ ์๋๋ผ) ๋ณ์ ๊ทธ๋ฆฌ๋ ๊ฒ๋ ๊ฐ๋ฅํ๋ค.
- ์ด์ค์์ Typed OM(Object Model)์ ๋ํด์ ์์๋ณผ ๊ฒ์ด๋ค. ์ฐ๋ฆฌ๊ฐ ์ง๊ธ๊น์ง ๋ฐฐ์ด CSSOM์ ์ ์ ์ธ ํ์ฅํ์ด๋ผ ๋ณผ ์ ์๋ค.
$('#someDiv').style.height = getRandomInt() + 'px';
// ์ฃผ๋ ค๋ ๊ฑด ์ซ์์ธ๋ฐ ์ ๊ฒฝ์ฐ์ฒ๋ผ ํญ์ ๋ฌธ์์ด๋ก ๋ง๋ค์ด์ค์ผ๋๋ ๋๊ฐ ์๋ค. ์๋ฐ์คํฌ๋ฆฝํธ ์ ๋๋ฉ์ด์
ํ๋ ์์ํฌ์์ ํ
์คํธ๋ก ์ ํํด์ฃผ๋ ๊ณผ์ ์์ ์ค๋๊ฑธ๋ฆด ์ ์๋ค. ๊ตฌ๊ธ๋ ์ด๊ฒ ์ซ์๋ ๊ฒ! ์ ์ฝ๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๋ฐ๊ฟ ์ ์๋ค.
const h = $('someDiv').styleMap.get('height');
$('#otherDiv').styleMap.set('height', h);
// ์ด๊ฒ typed OM์ด๋ค. h๊ฐ ๋๋์ฒด ๋ญ๊น?
CSS.number(0.5); //ํฌ๋กฌ์์ ์คํ ๊ฐ๋ฅ
// ์์ํ ์ซ์๋ฅผ ์๋ฏธํ๋ค.
// CSS์์ ์์ํ ์ซ์๋ฅผ ์ฐ๋ ๊ฒฝ์ฐ๋ opacity, z-index ๋ฑ์ด ์๋ค.
el.styleMap.set('opacity', CSS.number(0.5));
CSS.px(500); // ์ฐ๋ฆฌ๊ฐ ์น์ํ px!
el.styleMap.set('height', CSS.px(500)); // ์ด๋ ๊ฒ ์ฐ๋ฉด ๋์ด์ ๋ฌธ์์ด์ ์ฐ์ง ์์๋ ๋๋ค.
// ์ ์ ์ธ ํ์ ๊ฐ์ง๊ณ ์๋คํ์ฌ Typed OM์ด๋ผ๊ณ ํ๋ค.(ํ์ ๋ง๋ ๊ฐ์ ๋ณด๋)
CSSTransformValue
ใดCSSTransformComponent(์๋ ์์๋ค์ ๋ฌถ์ด์ค๋ค.)
ใดCSSTranslate, CSSRotate, CSSScale, CSSSkew, ...
margin: 10px 0 0 10px -> trbl ์ ๋ฌถ์ด์ฃผ๋ CSSPositionValue ํ์ด ํ์ํ๋ค.(number 2๊ฐ๋ 4๊ฐ๋ฅผ ๋ฐ์๋ค์ผ ์ ์๋ค)
background: url('a.png') -> CSSImageValue ๋ผ๋ ํ์
ํ์
CSS๊ฐ์ค ์ด๋ฏธ์ง๋ฅผ ๋ฐ์ ์ ์๋ ๊ฒ๋ค - list style(์ ์ฐํจ), cursor(ํฌ๊ธฐ, ๋ธ๋ผ์ฐ์ ์ ์์ด ์ ํ์ )
inset, left... -> CSSStyleValue
<!DOCTYPE html>
<html>
<head>
<style>
.test {
background: #f00;
}
</style>
</head>
<body>
<div class="test">test</div>
<script>
document
.querySelector('.test')
.attributeStyleMap.set('height', CSS.px(300));
</script>
</body>
</html>
- ์ฐธ๊ณ )
attributeStyleMap
์ด ์ด๋ฆ์ ๋ธ๋ผ์ฐ์ ๋ฒ์ ๋ง๋ค ๋ฐ๋ ์ ์๋ค. - ํ์ค์ ๋ฒ์ฉ ๋ธ๋ผ์ฐ์ ๋ฅผ ์ง์ํ๋ ์น์ฌ์ดํธ vs ํฌ๋กฌ๋ง ์ง์ํ๋ ์น์ฌ์ดํธ(ex.๊ด๋ฆฌ์ํ์ด์ง)๊ฐ ์๋ค. ์จ๋ ํฌ๋กฌ๋ง ์ง์ํ๋ ๊ฒฝ์ฐ ์ด๋ฐ ์คํ ์จ๋๋๋ค๋ ๊ฒ...
STEP 40
STEP 41