shadowDOM

 

HTML, CSS, JS 전반적 특성 때문에 생기는 웹앨 빌드의 취약성을 제거한다.

id나 class가 중복되어도 충돌되는지에 대한 여부를 알려주지 않기때문에 버그가 많았다.

스타일이 쌓이고 쌓이다가.. 도저히 안될때면 !important를 통해 스타일을 해결하기도 했다.

 

shadowDOM은 CSS및 DOM을 수정한다.

vanilla 자바스크립트에서 CSS를 마크업과 번들로 묶고 세부정보를 숨겨 자체포함 구성요소로 작성할 수 있다.

 

구성요소

  • HTML Templates

  • Shadow DOM

  • Custom elements

  • HTML Imports

CSS범위, DOM 캡슐화 등이 shadowDOM으로 인해 재사용가능하기 때문에 

shadowDOM을 사용하면 웹 컴포넌트로 만들 필요가 없다.

 

  • 자체적으로 DOM이 포함되어있다.

  • document.querySelector 컴포넌트의 shadowDOM에서 노드를 반환하지 않음

  • CSS의 범위가 정해져있음

  • id, class 충돌에 대해 걱정하지 않아도됨

 

호스트에 shadow DOM을 붙이기 위해, attachShadow() 메서드를 사용한다.

주로 상호 작용하는 특정 요소들은 shadow host가 될 수 없기 때문에,

단순히 <a> 요소를 shadow host로 사용할 수 없다.

 

새로운 shadow tree를 만들기 위해 콘텐츠를 생성해야 한다.

shadow tree는 DOM tree와 비슷하지만 일반 DOM 대신 shadow DOM을 사용한다.

 

예시

-코드 출처 : https://wit.nts-corp.com/2019/03/27/5552

HTML

<div class="shadow-host"></div>

JS로 엘리먼트 삽입

// 호스트에 shadowDOM을 붙임
const shadowEl = document.querySelector(".shadow-host");
const shadow = shadowEl.attachShadow({mode: 'open'});
 
// 내부에 삽입할 엘리먼트 생성
const link = document.createElement("a");
link.href = shadowEl.querySelector("a").href;
link.innerHTML = `
    <span aria-label="Twitter icon"></span>
    ${shadowEl.querySelector("a").textContent}
`;
 
// shadowDOM에 삽입
shadow.appendChild(link);

JS로 CSS삽입

// shadowDOM에 추가할 스타일 태그 생성
const styles = document.createElement("style");
 
// 내부에 삽입할 스타일 생성
styles.textContent = `
a, span {
  vertical-align: top;
  display: inline-block;
  box-sizing: border-box;
}
a {
    height: 20px;
    padding: 1px 8px 1px 6px;
    background-color: #1b95e0;
    color: #fff;
    border-radius: 3px;
    font-weight: 500;
    font-size: 11px;
    font-family:'Helvetica Neue', Arial, sans-serif;
    line-height: 18px;
    text-decoration: none;
}
a:hover {  background-color: #0c7abf; }
span {
    position: relative;
    top: 2px;
    width: 14px;
    height: 14px;
    margin-right: 3px;
    background: transparent 0 0 no-repeat;
    background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2072%2072%22%3E%3Cpath%20fill%3D%22none%22%20d%3D%22M0%200h72v72H0z%22%2F%3E%3Cpath%20class%3D%22icon%22%20fill%3D%22%23fff%22%20d%3D%22M68.812%2015.14c-2.348%201.04-4.87%201.744-7.52%202.06%202.704-1.62%204.78-4.186%205.757-7.243-2.53%201.5-5.33%202.592-8.314%203.176C56.35%2010.59%2052.948%209%2049.182%209c-7.23%200-13.092%205.86-13.092%2013.093%200%201.026.118%202.02.338%202.98C25.543%2024.527%2015.9%2019.318%209.44%2011.396c-1.125%201.936-1.77%204.184-1.77%206.58%200%204.543%202.312%208.552%205.824%2010.9-2.146-.07-4.165-.658-5.93-1.64-.002.056-.002.11-.002.163%200%206.345%204.513%2011.638%2010.504%2012.84-1.1.298-2.256.457-3.45.457-.845%200-1.666-.078-2.464-.23%201.667%205.2%206.5%208.985%2012.23%209.09-4.482%203.51-10.13%205.605-16.26%205.605-1.055%200-2.096-.06-3.122-.184%205.794%203.717%2012.676%205.882%2020.067%205.882%2024.083%200%2037.25-19.95%2037.25-37.25%200-.565-.013-1.133-.038-1.693%202.558-1.847%204.778-4.15%206.532-6.774z%22%2F%3E%3C%2Fsvg%3E);
}다
`;
 
// shadowDOM에 삽입
shadow.appendChild(styles);

일반적인 방법과 동일하게 appendChild() 메서드를 사용하여 shadow DOM에 새로운 요소를 추가합니다.

shadowDOM을 하나의 페이지로 보지않고 각각의 컴포넌트 덩어리로 생각하면 편하다.

원본 글 캡쳐
원본 글 캡쳐
원본 글 캡쳐

shadow-root, html template를 확인할 수 있다.

 

원본 글 캡쳐


<slot> 태그 내부에 html template의 내용이 삽입된다.

만약 <slot> 비어있다면 슬롯이 대체 컨텐츠를 렌더링한다.

 

그외에 호스팅태그 접근, 상위태그접근 등 어떻게 사용하면되는지에 대해 자세하게 적혀있다.

 

역사관련 작성된 부분에서 말하듯 지난 2년동안 chrome 35+/opera가 한동안 shadowDOM을 제공했다고한다.

아티클 프로젝트를 하면서 내가 얼마나 최신 트렌드에 무뎠는지를 알게되는 경우가 많다.(최신도 아니지..)

 

shadowDOM v1은 chrome53, Opera 40, Safari 10 및 Firefox 63으로 제공됩니다. Edge 는 개발을 시작했다고하니.
IE 하위를 맞춰야하는것이 아니라면 <iframe> 대신에 써볼만 할 것같다.

iframe과는 다른 것으로 shadowDOM은 경량화된, 감춰진 DOM 트리로 보면된다.

보안을 원하면 iframe을 사용하도록...

 


원본 글

https://developers.google.com/web/fundamentals/web-components/shadowdom

그 외 참고

https://wit.nts-corp.com/2019/03/27/5552

https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow

 

 

 

*예시에 사용한 비디오는 유튜브 장삐쭈님 것을 가져다가 사용하였습니다.

css만으로 반응형 비디오 만들기

동적인 값을 구할 때, 보통 js를 많이들 사용합니다.

하지만, 효율적인 이유로 css로 가능한 부분은 js로 컨트롤 하지 않죠.

 

javascript를 사용하지 않고 css만으로 유튜브에서 가져온 비디오를 반응형으로 만들어보도록 하겠습니다. 

 

먼저 내가 반응형으로 작업하려는 비디오의 비율을 알아야합니다.

보통 데스크탑에서의 비디오 비율은 16:9 이지만, 아닌 경우도 있으므로 비율을 알고 계산을 진행하도록 합니다.

 

16:9 비율의 해상도 표

2160p 3840x2160
1440p 2560x1440
1080p 1920x1080
720p 1280x720
480p 1280x720
360p 640x360
240p 426x240

 

반응형 비디오를 만들기 위해 본인이 올리려는 비디오의 비율을 먼저 아셔야합니다.

모든 값은 반응형이므로 width:100%를 기준으로 생각하여 height값을 넣을 것입니다.

 

21:9 일경우 9/21 = 0.42857140.4285714285714286‬%... (반올림하던 버리던, 이럴 경우 각자의 판단이 중요합니다^^)

16:9 일경우 9/16 = 0.5625%

4:3 일경우 3/4 = 0.75%

 

계산 방식 감이 오시죠?

 

자 그럼 16:9 비율의 동영상으로 가지고 작업한 html과 css를 가져와 보도록 하겠습니다. 

 

HTML

<div class="video_wrap">
  <iframe src="https://www.youtube.com/embed/cEN00wMFB2A" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>

 

비디오 태그(iframe, video)와 부모 태그가 필요합니다.

 

CSS

.video_wrap{ position:relative; width:100%; height:0; padding-bottom:56.25%; }
.video_wrap iframe{ position:absolute; width:100%; height:100% }

-비디오 영역에 실질적인 영역 값을 주게됩니다.

-위에서 계산한 비율 값을 넣어주는데, height가 아니라 padding-bottom 값으로 줍니다.

width:100%, padding-bottom:56.25%

 

여기서 왜? 

height가 아니고 padding-bottom으로 높이 값을 컨트롤하는지 의문인 분들이 있을 수 있습니다.

참조하는 태그가 다르기 때문입니다.

 

한번 예시를 들어보겠습니다.

-먼저 height를 사용해보도록 하겠습니다.

루트영역 .section을 만들어서 height:1000px을 주었습니다.

그 후에 height:56.25%를 준 결과입니다.

무엇을 참조하는지 가장 잘보이는 부분입니다.

height의 참조 기준은 부모 태그이기 때문에 .section의 height:1000px의 56.25%값이 적용되어 video_wrap의 높이 값이 562.5px로 height가 지정된 것이 확인됩니다.

 

이렇게 되면 비디오 비율이 깨져서 상, 하단에 검정 배경이 노출되는 것이 확인됩니다.

 

-padding-bottom값을 이용하면 어떻게 되는지도 보겠습니다.

height:0;을 준 후 padding-bottom:56.25%를 주었습니다.

높이 450px이 된 것으로 보아 padding값은 적용된 태그 자신을 참조하고 있음을 확인할 수 있습니다. 

비디오가 깨지지 않고있음이 확인됩니다.

 

결론을 내리면 아래와 같습니다.

-height의 %는 부모태그 참조

-padding의 %는 적용된 해당 자신을 참조

 

그래서 padding값을 사용해서 비디오를 반응형으로 만드는 것입니다.

그리고 padding값을 사용하기 때문에 css를 더 추가해줘야합니다.

 

css 코드를 보면 video_wrap(부모태그)에 position:relative;가 있고 ifram에 position:absolute가 있음을 보셨을 것입니다.

 

이것은 padding-bottom때문에 iframe이 영역 밖으로 밀려나게되는데 iframe을 absolute로 띄워서 얹기 위함입니다.

그리고 absolute를 사용하기 위해 relative로 부모태그를 기준으로 만들어준 것이구요.

이것이 이해가 안간다면 position에 대해 찾아보시기 바랍니다.

 

자 이렇게해서 완성본입니다.

See the Pen agyaae by leeyoonseo (@okayoon) on CodePen.

 

 

 

 

그렇다면 유튜브에서 가져오는 태그만일까?하고 html5 태그인 <video>로도 실험해습니다.

결론부터 말하면 <video> 태그를 사용할 때, 이러한 편법을 쓸 필요가 없습니다.

물론 써도 반응형 비디오가 되기는 합니다만, <video>태그는 자동으로 height를 맞춰줘서 불 필요한 작업입니다.

비디오, 아이프레임 css 결과 캡쳐

width:100%, height:100%; 를 동일하게 주었을 때의 결과입니다.

<video>태그 만이 비율을 맞춰 표현함을 볼 수 있었습니다.

따라서 <video>태그일 경우에는 위의 padding-bottom 편법을 쓰지 않아도 됩니다.

 

아이프레임은 HTML Inline Frame 요소이며 inline frame의 약자이다. 

 

"

효과적으로 다른 HTML 페이지를 현재 페이지에 포함시키는 중첩된 브라우저로

iframe 요소를 이용하면 해당 웹 페이지 안에 어떠한 제한 없이 다른 페이지를 불러와서 삽입 할 수 있다.

"

 

 

HTML

// 문법
<iframe src="삽입할페이지주소" width="너비" height="높이"></iframe>

// 예시
<iframe src="./intro.html" width="300" height="300"></iframe>

 

유튜브 iframe API 사용 예제

<body>
  <script>

      var player;

      function onYoutubeIframeApiReady(){
          player = new YT.Player('player', {
              width : '640',
              height : '360',
              videoId : 'M7Ic1UVf-VE',
              events : {
                  'onReady' : onPlayerReady,
                  'onStateChange' : onPlayerStateChange
              }
          });
      }
  </script>

  <iframe src="page.html" width="300" height="300">
      iframe을 지원하지 않는 브라우저입니다.
  </iframe>
</body>

noframes 요소는 해당 브라우저가 iframe 요소를 지원하지 않을 때 보여지는 문자열을 저장합니다.

 

 

iframe 속성

- src : 삽입 할 홈페이지 url

- name : 프레임 이름

- width : 프레임 가로 너비

- height : 프레임 세로 길이

- frameborder : 프레임 테두리 선 (0으로 설정하면 프레임의 테두리선 x, 1로 설정하면 프레임의 테두리선 o)

- scrolling : 스크롤바의 표시 여부를 나타냅니다. (yes로 설정하면 스크롤 바 무조건 표시, auto는 자동 표시)

- align : 정렬, left 왼쪽, right 오른쪽, middle 중앙, absmiddle 줄 중간 정렬이 있다.

- seamless : 경계선 없이 문서의 일부인 것처럼 화면에 렌더한다.

- srcdoc : 직접 태그소스를 iframe으로 표시할 수 있다.

<iframe srcdoc="<p>하이?</p>"></iframe>

- sandbox : 보안을 위해 iframe에서 폼이나 자바스크립트 실행이 되지 못하게 함.(해킹 문제고 생긴 속성)

  ㄴ allow-forms - 폼실행 허용

  ㄴ allow-same-origin 같은 도메인의 리소스 이용가능

  ㄴ allow-scripts 스크립트 실행 허용

<iframe sandbox="" src="주소"></iframe>

// 스크립트 실행 가능
<iframe sandbox="allow-script" src="주소"></iframe>

 

 

iframe의 target 속성

a 태그 속성 중 target 속성은 링크가 걸린 문서가 나타날 프레임을 설정하는 것으로 새로운 창으로 링크를 호출 할 수도 있고 현재창에 보여줄 수 도 있다.

그리고 부모 프레임 영역에 나타날 수 도 있고 프레임을 지정하여 원하는 프레임에 링크를 실행할 수 도 있다.

_blank  새창에서 열기
_self  내용을 현재 프레임 영역에서 열기(포커스가 있는 프레임 / 기본값)
_parent 내용을 부모 프레임 영역에 열기
_top  내용을 무조건 전체 영역에 열기
프레임명
해당 이름을 가진 프레임 영역에 열기
// 새창에서 열기
<iframe target="_blank" src="주소"></iframe>

// 내용을 현재 프레임 영역에서 열기(포커스가 있는 프레임 / 기본값)
<iframe target="_self" src="주소"></iframe>

// 내용을 부모 프레임 영역에 열기
<iframe target="_parent" src="주소"></iframe>

// 내용을 무조건 전체 영역에 열기
<iframe target="_top" src="주소"></iframe>

// 해당 이름을 가진 프레임 영역에 열기
<iframe target="프레임명" src="주소"></iframe>

 

 

프레임 예제

수직(세로) 프레임 

<frameset cols="25%,*,25%">
	<frame name="left" src="/html/left.html"/>
	<frame name="center" src="/html/center.html"/>    
	<frame name="right" src="/html/right.html"/>    
</frameset>

 

수평(가로) 프레임

<frameset rows="20%,60%,20%">

	// noresize="noresize"속성은 사용자가 창의 크기를 조절할 수 없게 한다.
	<frame name="top" src="/html/top.html" noresize="noresize"/>
	<frame name="center" src="/html/center.html" noresize="noresize"/>    
	<frame name="bottom" src="/html/bottom.html" noresize="noresize"/>    
    
</frameset>

 

구글 지도

구글 지도 > 주소 검색 > 공유 > 지도 퍼가기 > HTML 복사

<iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3161.6604808673546!2d126.97262231608447!3d37.586611830734384!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x357ca2b7ef94a16b%3A0xb16d5f135eaaddc!2z7LKt7JmA64yA!5e0!3m2!1sko!2skr!4v1588826305729!5m2!1sko!2skr" width="600" height="450" frameborder="0" style="border:0;" allowfullscreen="" aria-hidden="false" tabindex="0"></iframe>

 

 

유튜브

해당 동영상 > 공유 > 퍼가기 > 동영상 퍼가기

* 해당 동영상은 인기동영상에서 가져왔습니다. 장삐쭈님 동영상입니다.

유튜브 사용 시 src뒤에 물음표를 붙이고 옵션을 넣을 수 있다.

이어서 옵션을 추가할때에는 &를 사용한다.

 

?autoplay=1 (자동재생) 

&loop=1 (반복)

 

 

주의할 사항

HTML5 이전에도 이후에도 <iframe>을 사용하지 않는 것을 권고한다.
- 반응형 웹 사이트가 대세인 오늘날의 트렌드와 상극이다.
- 페이지의 파편화 문제가 생긴다.

  ㄴ 검색 엔진 등록 시 frameset 뿐만 아니라 메뉴용 페이지, 콘텐츠용 페이지 까지 함께 크롤링 할 수 있다.
- 웹접근성 저해의 요인이 될 수 있음으로 남용에 주의해야한다.
- 프레임 구조가 가지고 있던 장점을 CSS와 jQuery로 해결 할 수 있다.

 

<video>태그 브라우저, 모바일 이슈 정리 사항

몇 주 전쯤에 <iframe>으로 작업하던 것들을 웹 앱 이슈상 <video>태그로 변경하는 작업을 하였다.

이 작업의 이유는 회사 정책의 문제였다.

 

+) 비디오 태그로 변경을 하면서 진행했던 부분은 자동재생이었다.

하지만 몇가지 이슈가 있었다.

 

이슈

1. autoplay를 걸었음에도 안된다.

2. 아이폰에서 전체화면이 된다. 

+) 같은 os 버전임에도 아이폰8은 전체화면으로 넘어갔고, 아이폰10은 정상적으로 inline 노출이 되었다.

3. ie8 안됨

 

<video> 태그 자동 재생 이슈

- 대부분의 브라우저에서 <video>의 자동재생을 원한다면 음소거(muted)속성을 필수로 추가하여야 한다.

(크롬도 작년 6월부터 브라우저 정책이 바뀌었다고 한다)

참고

https://developers.google.com/web/updates/2017/09/autoplay-policy-changes

https://lifehacker.com/mute-most-autoplay-videos-with-the-latest-chrome-update-1825354497

 

자동 재생이 가능한 경우는

1. 같은 도메인에서 이벤트가 발생한 경우(클릭, 페이지이동)

- 위와 같은 방법때문에 작년에 변경되었음에도 이제 이슈가 발견되었다.

2. 비디오 내에 오디오 트랙이 없을 경우.

http://iropke.com/archive/video-autoplay.html

여기 블로거분이 테스트한 경우를 보면 파일 자체에 오디오 정보가 없어야한다!(소리가 녹음이 안된거랑은 다른 개념)

3. muted 속성을 적용했을 경우

4. iphone에서 playsinline 요소를 추가하였을 경우 (webkit 접두어를 추가하여 webkit-playsinline으로 추가하여 사용했었으나 최근 HTML 사양에 추가되어 접두사 없이도 사용가능하다.)

5. 모바일일 때 해당 사이트를 홈 스크린에 즐겨찾기 한 경우

6. 부모 Frame이 권한을 부여하여 자식 Frame에서 비디오를 실행했을 경우 (?)

참고

https://medium.com/@idchoi2/html5-video-%ED%83%9C%EA%B7%B8-%EC%9E%90%EB%8F%99%EC%9E%AC%EC%83%9D-%EC%9D%B4%EC%8A%88-f97e5d397516

 

결론적으로 우리가 <video>태그 사용할 때 자동 재생을 원한다면 필수로 넣어야하는 속성 3가지이다.

autoplay muted playsinline

 

- pc 브라우저, 안드로이드는 autoplay, muted 속성만 추가하여도 자동 재생이 가능하다.

- 아이폰은 IOS10버전 이후부터 playsinline 속성을 추가 적용해주어야한다.

(아이폰8에서는 IOS12.2 버전으로 테스트 한 결과 전체화면으로 자동재생 되었으나 업데이트 문서에 IOS10 이후부터 적용 되었다고 작성되어있다)

참고 

https://medium.com/code-divoire/autoplaying-inline-videos-on-iphone-ios-10-using-angular-d4e2eaba2164

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-playsinline

 

★★★추가로 아이폰 저전력모드일때 위의 속성을 넣었음에도 자동재생이 안된다고 한다.★★★

 

하위브라우저 이슈 (HTML5 태그라서 ie8이하는 지원이 안됩니다.)

- 포스터 이미지와 안내 문구를 삽입해 주어야 한다.

ie8이하에서는 source가 렌더링되지 않기 때문에 이미지나 텍스트가 나오게 할 수 있다.

아래 처럼 비디오 태그 내부에 넣어주면 된다.

// HTML

<video>
  <source src="비디오 링크" type="비디오 타입">
  <img src="이미지 링크">
  <p>브라우저 버전이 낮다</p>
</video>

혹은 별도로 주석문을 달아준다.

// HTML

<!--[if lte IE8]>
    <p>브라우저 버전이 낮다</p>
<![endif]-->

 

미디어쿼리

비디오 인라인 css

// HTML
<style>
  #either-gif-or-video video { display: none; }
  
  @media (-webkit-video-playable-inline) {
      #either-gif-or-video img { display: none; }
      #either-gif-or-video video { display: initial; }
  }
</style>

<div id="either-gif-or-video">
  <video src="image.mp4" autoplay loop muted playsinline></video>
  <img src="image.gif">
</div>

참고

https://webkit.org/blog/6784/new-video-policies-for-ios/

 

++++ 0406 추가

코드펜에 테스트하신 분이 있어서 링크를 달아둔다.

https://codepen.io/alfmos/pen/VwwqQje

 

++++ 추가 이슈

애플 기기에서(아이폰, 맥..) 비디오 객체는 HTML에 넣은 후에 동적으로 비디오 태그의 링크만 바꿔치기 할때,

애플에 내장된 비디오 버튼에 대한 이슈가 발생했다.

 

정확히 발생한 내역은 버튼 클릭 시 해당 버튼에 삽입된 링크의 값이 비디오 태그의 src에 박히는 것이었는데,

이때 사용자가 첫번째 비디오를 재생하다가 다른 버튼을 눌러 두번째 비디오가 로드되었을 때

애플 내장 비디오의 컨트롤바의 버튼이 여전히 재생되어있음으로 되어있는 것이었다, 재렌더링을 안하는 듯 싶었다.

안드로이드는 링크가 변경되고 .load()를 시킬때 마다 내장 비디오의 컨트롤바까지 재렌더링을 하는 듯 싶어 이 이슈는 발생하지 않았다.

(재렌더링 하냐 안하냐의 문제가 아닐수도 있다.)

 

사용자가 처음 재생을 누른 상태

 

다른 비디오 src를 삽입하고 재로드 시켰음에도 여전히 재생하는 듯 보인다.

그냥 개발 방식을 바꿔서 작업하느라 이슈에 대한 사항은 넘어가기로했다.

 

 

+ Recent posts