컨테이너 쿼리: 또 다른 Polyfill
38988 단어 webdevcssshowdevjavascript
그러나 아쉽게도 모든 브라우저가 이를 구현하기 전까지는 폴리필에 의존해야 작동합니다.
다른 polyfill은 잘 작동하지만 postCSS나 특정 구문이 필요하지 않고 현재 작업 중인 프로젝트에 더 적합한 것이 필요했습니다.
그래서 저는 저만의 polyfill을 만들기로 결정했고 결국 502바이트 gzip으로 압축된 스크립트로 끝났습니다.
if(!("CSSContainerRule"in window)){const e=(e,s)=>e.reduce((e,t,c)=>s(t)?c:e,-1),s=new ResizeObserver(s=>{for(let t of s){const s=t.target,c=s.__cq,n=e(c.bp,e=>e<=t.contentRect.width);n!==s.index?(s.style.cssText=c.css.filter((e,s)=>s<=n).join(""),c.index=n):-1===n&&s.removeAttribute("style")}});[...document.styleSheets].map(e=>{fetch(e.href).then(e=>e.text()).then(e=>{let t,c=new Set;const n=/@container\s?\(min-width:\s?(?<breakpoint>.*)px\)\s?\{\s?(?<selector>.*)\s?\{\s?(?<css>.*;)\s?\}/gm;for(;t=n.exec(e);)[...document.querySelectorAll(t.groups.selector)].forEach(e=>{e.__cq=e.__cq||{bp:[],css:[],index:-1};const s=t.groups.breakpoint-0,n=t.groups.css,o=e.__cq.bp.findIndex(e=>e===s);o<0?(e.__cq.bp.push(s),e.__cq.css.push(n)):e.__cq.css[o]=e.__cq.css[o].concat(n),c.add(e)});for(let e of c)s.observe(e)})})}
알겠습니다. 완전히 읽을 수 없으므로 스크립트를 보기 전에 HTML 및 CSS로 스테이지를 설정해 보겠습니다.
무대 설정
HTML에서 다음을 새 문서에 추가하십시오.
<main>
<div class="cqw"><div class="cq cq1"></div></div>
<div class="cqw"><div class="cq cq2"></div></div>
<div class="cqw"><div class="cq cq3"></div></div>
<div class="cqw"><div class="cq cq4"></div></div>
</main>
<head>
-섹션에서 스타일시트에 대한 링크를 추가합니다.<link href="cq.css" rel="stylesheet">
이제
cq.css
-시트를 생성합니다.body {
margin: unset;
}
main {
display: flex;
flex-wrap: wrap;
}
.cq {
aspect-ratio: var(--asr, 1);
background-color: var(--bgc, silver);
width: var(--w, 25vw);
}
.cqw {
contain: layout inline-size;
}
.cq1 { --bgc: tomato }
.cq2 { --bgc: orange }
.cq3 { --bgc: skyblue }
.cq4 { --bgc: tan; }
@container (min-width: 300px) { .cq { --asr: 2/1; } }
@container (min-width: 300px) { .cq1 { --bgc: indianred; } }
@container (min-width: 300px) { .cq2 { --bgc: darkorange; } }
@container (min-width: 300px) { .cq3 { --bgc: steelblue; } }
@container (min-width: 300px) { .cq4 { --bgc: lavender; } }
@media (min-width: 600px) { .cq { --w: 50vw; } }
@media (min-width: 900px) { .cq { --w: 25vw } }`
이제 페이지가 다음과 같이 표시됩니다.
스크립트
먼저 스크립트가 필요한지 여부를 확인해야 합니다.
if (!('CSSContainerRule' in window))
다음으로 페이지의 스타일시트를 반복하고 (다시 말하지만 캐시되어 있음)
fetch()
로 결과를 변환하고 .text()
규칙을 문자열로 반환합니다.[...document.styleSheets].map(sheet => {
fetch(sheet.href)
.then(css => css.text())
.then(rules => { ... }
regEx
를 사용하여 해당 문자열에서 필요한 것을 찾습니다.const re = /@container\s?\(min-width:\s?(?<breakpoint>.*)px\)\s?\{\s?(?<selector>.*)\s?\{\s?(?<css>.*;)\s?\}/gm
NOTE: A good place to play around with RegEx, is regex101.com
이 식은
breakpoint
, selector
및 css
라는 제목의 일치 그룹을 반환합니다.이제 일치 항목을 반복해 보겠습니다. 일치할 때마다
querySelectorAll
를 사용하여 DOM
와 일치하는 selector
의 요소를 찾습니다.각 요소에서 중단점 배열, 각 중단점에 대한 css 및 인덱스를 포함하는 객체
__cq
를 생성합니다. 반복할 때마다 객체가 이미 존재하는지 확인합니다.let match;
let observe = new Set();
while (match = re.exec(rules)) {
[...document.querySelectorAll(match.groups.selector)].forEach(elm => {
elm.__cq = elm.__cq || { bp: [], css: [], index: -1 }
const bp = match.groups.breakpoint-0;
const css = match.groups.css;
const index = elm.__cq.bp.findIndex(item => item === bp);
if (index < 0) {
elm.__cq.bp.push(bp);
elm.__cq.css.push(css);
}
else {
elm.__cq.css[index] = elm.__cq.css[index].concat(css);
}
observe.add(elm);
})
}
Set()
라는 observe
는 (고유한) 요소 집합을 유지하는 데 사용되며 다음을 관찰해야 합니다.for (let item of observe) RO.observe(item);
RO
는 ResizeObserver
이다.const RO = new ResizeObserver(entries => {
for (let entry of entries) {
const elm = entry.target;
const cq = elm.__cq;
const lastIndex = findLastIndex(cq.bp, item => item <= entry.contentRect.width);
if (lastIndex !== elm.index) {
elm.style.cssText = cq.css.filter((item, index) => index <= lastIndex).join('');
cq.index = lastIndex;
}
else if (lastIndex === -1) elm.removeAttribute('style');
}
});
findLastIndex
라는 작은 방법을 사용하고 있습니다.const findLastIndex = (items, callback) => items.reduce((acc, curr, index) => callback(curr) ? index : acc, -1);
... 그리고 그것을 사용하여 현재 필요한
breakpoint
(bp)를 결정한 다음 요소의 style>
속성을 css
개체의 __cq
로 설정합니다.다음은 전체 스크립트입니다. 이 스크립트 또는 위의 축소된 버전을 데모 페이지의
<script>
-태그에 추가하세요.if (!('CSSContainerRule' in window)) {
const findLastIndex = (items, callback) => items.reduce((acc, curr, index) => callback(curr) ? index : acc, -1);
const RO = new ResizeObserver(entries => {
for (let entry of entries) {
const elm = entry.target;
const cq = elm.__cq;
const lastIndex = findLastIndex(cq.bp, item => item <= entry.contentRect.width);
if (lastIndex !== elm.index) {
elm.style.cssText = cq.css.filter((item, index) => index <= lastIndex).join('');
cq.index = lastIndex;
}
else if (lastIndex === -1) elm.removeAttribute('style');
}
});
[...document.styleSheets].map(sheet => {
fetch(sheet.href)
.then(css => css.text())
.then(rules => {
let match;
let observe = new Set();
const re = /@container\s?\(min-width:\s?(?<breakpoint>.*)px\)\s?\{\s?(?<selector>.*)\s?\{\s?(?<css>.*;)\s?\}/gm
while (match = re.exec(rules)) {
[...document.querySelectorAll(match.groups.selector)].forEach(elm => {
elm.__cq = elm.__cq || { bp: [], css: [], index: -1 }
const bp = match.groups.breakpoint-0;
const css = match.groups.css;
const index = elm.__cq.bp.findIndex(item => item === bp);
if (index < 0) {
elm.__cq.bp.push(bp);
elm.__cq.css.push(css);
}
else {
elm.__cq.css[index] = elm.__cq.css[index].concat(css);
}
observe.add(elm);
})
}
for (let item of observe) RO.observe(item);
}
)
})
}
이제 페이지 크기를 조정하면 상자가
aspect-ratio
및 background-color
로 변경됩니다.900px
에서 레이아웃은 초기 값으로 돌아간 다음 1200px
에서 업데이트된 값으로 돌아갑니다.NOTE: It's mobile first, thus only
min-width
will work, and only with pixels as it's value, since thecontentRect.width
of theResizeObserver
returns a value in pixels.
저는 최적화하거나 변경/추가할 수 있는 많은 것들이 있다고 확신합니다(예를 들어 오류 처리!). 결국 이것은 제가 3-4시간 안에 요리한 것입니다!
아래의 Codepen은 Codepen에서 열거나 편집하고 브라우저 크기를 조정하는 경우 가장 잘 작동합니다.
읽어 주셔서 감사합니다!
Pexels에서 제공하는 Pixabay의 표지 이미지
Reference
이 문제에 관하여(컨테이너 쿼리: 또 다른 Polyfill), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/madsstoumann/container-queries-another-polyfill-2488텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)