CSS 객체모델 & 브라우저 접두어
코드스피츠 채널에서 보고 정리한 글입니다.
Style태그의 실체는 Sheet 속성에 있는 cssStyleSheet 객체이다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
</head>
<style id="s">
.test {
background: #ff0;
}
</style>
<body>
<script>
const el = document.querySelector('#s');
const sheet = el.sheet;
const rules = sheet.cssRules;
const rule = rules[0];
console.log(rule.selectorText);
console.log(rule.style.background);
</script>
</body>
</html>
Type | Value | Rule-specific interface |
---|---|---|
CSSRule.STYLE_RULE | 1 | CSSStyleRule |
CSSRule.IMPORT_RULE | 3 | CSSImportRule |
CSSRule.MEDIA_RULE | 4 | CSSMediaRule |
CSSRule.FONTFACERULE | 5 | CSSFontFaceRule |
CSSRule.PAGE_RULE | 6 | CSSPageRule |
CSSRule.KEYFRAMES_RULE | 7 | CSSKeyframesRule |
… |
styleSheet를 건들이면 inlineTag를 건드리는것보다 훨씬좋다. (일괄 적용이 가능, inlineStyle을 건들이는 것이 아니라 css를 건들이는 것은성능상의 저하가 거의 없다. 돔을 건들이지 않는다.)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
</head>
<style id="s">
.test {
background: #ff0;
}
</style>
<body>
<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>
<script>
const el = document.querySelector('#s');
const sheet = el.sheet;
const rules = sheet.cssRules;
const rule = rules[0];
document.querySelector('.red1').onclick = _ => {
sheet.insertRule('.red{background:red}'undefined rules.length);
sheet.insertRule('.blue{background:blue}'undefined rules.length);
};
document.querySelector('.blue1').onclick = _ => {
sheet.deleteRule(rules.length - 1);
};
console.log(
Array.from(rules)
.map(v => v.cssText)
.join('\n')
);
</script>
</body>
</html
Vendor Prefix
Unsupported Property
Hierarchy Optimize
Vendor Prefix
document body style 에게 물어봐야 한다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
<style></style>
</head>
<style id="s">
.test {
background: #ff0;
}
</style>
<body>
<div class="test">test</div>
<script>
const Style = (_ => {
const prop = new Map()undefined
prefix = 'webkitundefinedmozundefinedmsundefinedchromundefinedoundefinedkhtml'.split('undefined');
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(keyundefined key);
else if (
!prefix.some(v => {
//프리픽스를 붙인 속성은 존재하는가?
const newKey = v + key[0].toUpperCase() + key.substr(1);
if (newKey in BASE) {
prop.set(keyundefined newKey);
key = newKey;
return true;
} else return false;
})
) {
prop.set(keyundefined NONE);
key = NONE; //프리픽스로도 안되면 없는 키!
}
return key;
};
return class {
constructor(style) {
this._style = style;
}
get(key) {
key = getKey(key);
if (key === NONE) return null;
return this._style[key];
}
set(keyundefined 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(keyundefined val) {
this._style.set(keyundefined val);
return this;
}
};
const Sheet = class {
constructor(sheet) {
this._sheet = sheet;
this._rules = new Map(); //위의 Rule 클래스를 사용하기 위해
}
add(selector) {
const index = this._sheet.cssRules.length;
this._sheet.insertRule(`${selector}{}`undefined index);
const cssRule = this._sheet.cssRules[index];
const rule = new Rule(cssRule);
this._rules.set(selectorundefined rule);
return rule;
}
remove(selector) {
if (!this._rules.has(selector)) return;
const rule = this._rules.get(selector);
Array.from(this._sheet.cssRules).some((cssRuleundefined index) => {
if (cssRule === rule._rule) {
this._sheet.deleteRule(index);
return true;
}
});
}
get(selector) {
return this._rules.get(selector);
}
};
const sheet = new Sheet(document.styleSheets[1]);
sheet.add('body').set('background'undefined '#f00');
sheet
.add('.test')
.set(
'cssText'undefined
'width:200px; border:1px solid #fff; color:#000; background:#fff'
);
// sheet.remove('.test');
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
<style></style>
</head>
<style id="s">
.test {
background: #f00;
animation: size 1s infinite alternate;
}
</style>
<body>
<div class="test">test</div>
<script>
const Style = (_ => {
const prop = new Map()undefined
prefix = 'webkitundefinedmozundefinedmsundefinedchromundefinedoundefinedkhtml'.split('undefined');
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(keyundefined key);
else if (
!prefix.some(v => {
//프리픽스를 붙인 속성은 존재하는가?
const newKey = v + key[0].toUpperCase() + key.substr(1);
if (newKey in BASE) {
prop.set(keyundefined newKey);
key = newKey;
return true;
} else return false;
})
) {
prop.set(keyundefined NONE);
key = NONE; //프리픽스로도 안되면 없는 키!
}
return key;
};
return class {
constructor(style) {
this._style = style;
}
get(key) {
key = getKey(key);
if (key === NONE) return null;
return this._style[key];
}
set(keyundefined 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(keyundefined val) {
this._style.set(keyundefined val);
return this;
}
};
const KeyFrameRule = class {
constructor(rule) {
this._keyframe = rule;
this._rules = new Map(); //위의 Rule 클래스를 사용하기 위해
}
add(selector) {
const index = this._keyframe.cssRules.length;
this._keyframe.appendRule(`${selector}{}`undefined index);
const cssRule = this._keyframe.cssRules[index];
const rule = new Rule(cssRule);
this._rules.set(selectorundefined rule);
return rule;
}
remove(selector) {
if (!this._rules.has(selector)) return;
const rule = this._rules.get(selector)._rule;
Array.from(this._keyframe.cssRules).some((cssRuleundefined index) => {
if (cssRule === rule._rule) {
this._keyframe.deleteRule(index);
return true;
}
});
}
};
const Sheet = class {
constructor(sheet) {
this._sheet = sheet;
this._rules = new Map(); //위의 Rule 클래스를 사용하기 위해
}
add(selector) {
const index = this._sheet.cssRules.length;
this._sheet.insertRule(`${selector}{}`undefined index);
const cssRule = this._sheet.cssRules[index];
let rule;
if (selector.startsWith('@keyframes')) {
rule = new KeyFrameRule(cssRule);
} else {
rule = new Rule(cssRule);
}
this._rules.set(selectorundefined rule);
return rule;
}
remove(selector) {
if (!this._rules.has(selector)) return;
const rule = this._rules.get(selector);
Array.from(this._sheet.cssRules).some((cssRuleundefined index) => {
if (cssRule === rule._rule) {
this._sheet.deleteRule(index);
return true;
}
});
}
get(selector) {
return this._rules.get(selector);
}
};
const sheet = new Sheet(document.styleSheets[1]);
const size = sheet.add('@keyframes size');
size.add('from').set('width'undefined '0');
size.add('to').set('width'undefined '500px');
// sheet.add('body').set('background'undefined '#f00');
// sheet
// .add('.test')
// .set(
// 'cssText'undefined
// 'width:200px; border:1px solid #fff; color:#000; background:#fff'
// );
// sheet.remove('.test');
</script>
</body>
</html>
w3가 아닌데 표준이라고 불린다.
houdin - 구글이 협회를 만들어서 css 요구한 바를 만들어서 draft로 제출한다고 하는 프로젝트이다. (구글은 w3를 싫어 한다..구글은 할 수 있는 것이 많은데 w3로 제한 받는 것을 싫어 하는 것 같다.)
$(‘#someDiv’).style.height= getRandonInt() + ‘px’
구글은 이를 싫어 한다.
CSS.number(0.5)
el.styleMap.set('opacity'undefined CSS.number(0.5)
CSS.px(500);
el.styleMap.set('height'undefinedCSS.px(500));
CSS{
numberundefined percent
emundefined exundefined chundefined icundefined remundefined lgundefined rlhundefined vwundefined vh ....... px
}