본문 바로가기

프론트엔드

css style tag(rule) 복사하기

반응형
  • 키워드: clone css, cloneNode, insertRule, cssText, emotion, CSSOM
  • 상황
    1. @emotion 라이브러리가 만들어주는 style tag를 복사하는 작업을 하고자한다.
      HTML emotion style tags
  • 해결 과정
    1. emotion이 만든 style tag는 'data-emotion' attribute를 갖고 있음을 확인했다.
      따라서 style tag 중, data-emotion 속성을 가진 tag를 복사하면 될 것이다.
        const emotionStyleTags =
        Array.prototype.filter.call(document.getElementsByTagName('style'), (style) =>
          style.hasAttribute('data-emotion'),
        )
      
      const emotionStyleTagClones = emotionStyleTags.map((tag) => tag.cloneNode(true));
      hasAttribute와 cloneNode를 활용해 emotion style tag를 복사했다.

      이제 복사한 tag를 원하는 위치에 복사하면 된다.
        emotionStyleTagClones.forEach((tag) => {
        document.head.appendChild(tag);
      });


    2. 하지만 build를 하고 development 환경이 아닌 production 환경으로 실행하니 위 로직이 정상 작동하지 않았다.
      emotion github 코드를 통해 원인을 찾아보니 production 환경에서 css rule을 insert하는 로직이 다르다는 것을 발견했다.
      https://github.com/emotion-js/emotion/blob/main/packages/sheet/src/index.js#L141

      production 환경일 경우 성능 최적화를 위해 sheet.insertRule(rule, sheet.cssRules.length 을 사용하고, 아니면 tag.appendChild(document.createTextNode(rule))를 사용한다.

    3. tag를 그대로 clone하지 않고 cssRule을 복사하도록 변경한다.
      복사 과정에서 rule.cssText와 insertRule 함수를 사용했다.
        let clonedStyleElement: HTMLStyleElement | null = null;
      const emotionStyleElements =
        Array.prototype.filter.call(document.getElementsByTagName('style'), (style) =>
          style.hasAttribute('data-emotion'),
        );
      const emotionStyleRules = emotionStyleElements
        .map((style) => Array.prototype.map.call(style.sheet.rules, (rule: CSSStyleRule) => rule.cssText))
        .flat() as string[];
        
      ...
      
      clonedStyleElement = document.createElement('style');
      clonedStyleElement.setAttribute('id', 'clone');
      clonedStyleElement.setAttribute('type', 'text/css');
      document.head.appendChild(clonedStyleElement);
      
      emotionStyleRules.forEach((rule) => {
        clonedStyleElement?.sheet?.insertRule(rule);
      });


    4. 이 포스트는 emotion 관련 style tag를 위주로 설명했지만, 다른 경우에도 cssText, insertRule를 이용해 style을 복사할 수 있을 것이다.
반응형