반응형

제이쿼리에서는 js로 애니메이션 효과를 구현하려면 편하게 .animate 메소드를 사용할 수 있었지만

바닐라 js로 애니메이션을 구현하려고 하면 비교적 복잡한 코드로 구현이 필요하다는걸 알 수 있다.

 

애니메이션 작동 원리를 생각해보면 일정한 시간 간격으로 특정 위치로 이동하게 되는 소스를 구현해야 하는데 일정 시간을 반복하는걸 생각해보면 setInterval이나 setTimeout 를 생각할 수 있는데 두 가지 방법 보다는 requestAnimationFrame이 브라우저 측면에서는 훨씬 효율적이다.

 

1. requestAnimationFrame 란?

requestAnimationFrame는 (줄여서 RAF라고 부른다.) 브라우저에서 제공하는 메소드로 애니메이션과 같이 반복적인 작업을 수행할 때 사용한다. 

requestAnimationFrame의 특징을 설명하기 위해서는 setInterval, setTimeout 함수와 비교를 해보면 쉽게 알 수 있다.

setInterval / setTimeout requestAnimationFrame (RAF)
타이머의 주기가 정확하지 않기 때문에 애니메이션이 일정하지 않게 보이거나 끊겨 보일 수 있다. 브라우저의 리페인트 주기에 맞게 콜백 함수를 예약하기 때문에 브라우저가 화면을 갱신할 때에 맞춰 애니메이션을 실행한다. 따라서 부드러운 애니메이션을 볼 수 있다.
고정된 타이머 주기를 사용해서 타이머 기능이 필요하지 않더라도 불필요하게 작업이 되거나 배터리 소모가 된다. 브라우저에 최적화하여 애니메이션을 처리하기 때문에 사용자 디바이스의 사양이나 배터리 수명 등을 최적화 하여 실행한다.
브라우저가 비활성된 상태에서 혹은 백그라운드에서 실행을 멈추지 않고 계속해서 작업을 실행하기 때문에 불필요한 리소스를 소비하게 된다. 브라우저가 비활성된 상태이거나 백그라운드에서 실행을 할 때 애니메이션 처리를 조정하여 성능을 최적화한다. 
정해진 시간 간격에 따라 비동기적으로 작업을 예약하고 실행하기 때문에 다른 브라우저 작업과 동기화가 보장되지 않을 수 있다. 브라우저의 리페인트 주기에 맞게 콜백 함수를 실행하기 때문에 다른 브라우저 작업과 동기화 되어 실행한다.

 

그리고 

requestAnimationFrame은 초가 60프레임 정도를 구현한다.

(브라우저 상황에 따라 다를 수 있다. 하지만 최대한 구현하려한다.)

 

2. requestAnimationFrame 사용

기본적인 사용은 아래와 같다.

const callBackFnc = (timeStamp) => {
	console.log(timeStamp);  // timeStamp 값이 시간에 따라 계속 증가한다.
}

// requestAnimationFrame(콜백함수);
requestAnimationFrame(callBackFnc);

 

requestAnimationFrame(콜백함수) 로 사용하는데

이때 사용하는 콜백함수는 첫 번째 파라미터 값으로 타임 스탬프를 가져온다.

그리고 그 타임 스탬프는 시간의 경과에 따라 증가한다.

 

 

requestAnimationFrame의 간단한 사용 예시를 보자면

const testBtn = document.querySelector('.btn_test');
let testDuration = 0;

const scrollEvt = () => {
  testDuration += 50;
  const rafScroll = requestAnimationFrame(scrollEvt);
	
  window.scrollTo(0, testDuration);
  if(testDuration > 500){
    cancelAnimationFrame(rafScroll);
    testDuration = 0;
  }
}
testBtn.addEventListener('click', scrollEvt);

 

위 코드는 실무에서 쓰일 일은 없는 소스지만 간단하게 보자면

testBtn 버튼을 누르면 브라우저의 스크롤이 내가 지정한 위치만큼 이동하게 된다.

 

window.scrollTo(0, 원하는 위치);

로 지정을 하는데 testDuration이라는 반복되는 값을 만들어서 requestAnimationFrame 함수가 실행 될 때 마다

50씩 증가를 시켜 스크롤 위치에 변화를 준다.

 

그리고 증가하는 testDuration 변수 값이 500이 넘어가게 되면 requestAnimationFrame 함수의 반대 성격의  cancelAnimationframe() 함수를 사용해서 애니메이션을 멈출 수 있다.

 

(버튼 클릭 -> 50씩 스크롤 위치 증가 -> 증가된 값이 500이 넘어가면 멈춤)

 

 

3. requestAnimationFrame 활용

실무에서 쓸법한 내용으로 예시를 들자면

const duration = 500;  // 애니메이션 속도 조정
const moveEtc = (target) => {
  const targetEl = document.querySelector(target);	// 함수를 실행할 때 목적지 엘리먼트를 받아옴

  if(targetEl){
    let start;
    const targetPosition = targetEl.getBoundingClientRect().top;  // 파라미터로 받아온 target의 위치값 구하기
    const startPosition = window.scrollY;	// 애니메이션 이벤트가 발생했을 때의 현재 위치 값
    const distance = targetPosition;
    const easings = {	// 애니메이션의 easing 효과
      linear(t) {
        return t;
      },
      easeInQuad(t) {
        return t * t;
      },
      easeOutQuad(t) {
        return t * (2 - t);
      },
      easeInOutQuad(t) {
        return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
      },
      easeInCubic(t) {
        return t * t * t;
      },
      easeOutCubic(t) {
        return (--t) * t * t + 1;
      },
      easeInOutCubic(t) {
        return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
      },
      easeInQuart(t) {
        return t * t * t * t;
      },
      easeOutQuart(t) {
        return 1 - (--t) * t * t * t;
      },
      easeInOutQuart(t) {
        return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t;
      },
      easeInQuint(t) {
        return t * t * t * t * t;
      },
      easeOutQuint(t) {
        return 1 + (--t) * t * t * t * t;
      },
      easeInOutQuint(t) {
        return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t;
      }
    };
    const moveScroll = function(timeStamp){  // requestAnimationFrame를 적용할 함수, 첫 번째 파라미터로 타임 스탬프 받아오기
      if(start === undefined){
        start = timeStamp;  // requestAnimationFrame 함수의 타임스탬프를 기준으로 시작 시간을 지정
      }

      const elapsed = timeStamp - start;  // 시작 시간, 타임 스탬프를 이용해서 애니메이션 경과 시간을 구한다.
      const targetPos = startPosition + distance * easings.linear(elapsed / duration);  // 현재 위치, 목표 위치, easing 등으로 최종 목적지까지의 애니메이션 형태를 구성
      window.scrollTo(0, targetPos);

      if(elapsed <= duration){
        requestAnimationFrame(moveScroll);
      }else if(timeStamp >= elapsed){
        // 애니메이션 종료 후
        const targetPosFix = window.scrollY + targetEl.getBoundingClientRect().top;
        window.scrollTo(0, targetPosFix);
      }
    };
    requestAnimationFrame(moveScroll);
  }
}

 

위와 같이 작성할 수 있다.

requestAnimationFrame(콜백함수)의 콜백함수는 첫 번째 파라미터로 받아온 타임 스탬프 값(timeStamp)을 이용해서 애니메이션 시작점, 목적지, 애니메이션의 경과 시간 등을 계산한다.

 

 

 


참고

https://developer.mozilla.org/ko/docs/Web/API/window/requestAnimationFrame

https://codepen.io/pawelgrzybek/pen/ZeomJB

https://velog.io/@y_jem/react-%EC%8A%AC%EB%A1%AF-%EC%B9%B4%EC%9A%B4%ED%8A%B8-%EA%B8%B0%EB%8A%A5

 

 

 

반응형
반응형

브라우저는 html 파일을 상단 head 태그의 내용부터 순차적으로 파싱을 하게된다.

 

그래서 자바스크립트를 css를 추가하는 것 처럼 head 태그 안에서 사용하게 되면 문제가 발생하게 된다.

<!doctype html>
<html lang="ko">
  <head>
    <script>
      const btn = document.querySelector('#btn');
      btn.addEventListener('click', function(){
        console.log('Hello World');  // undefined btn 에러 출력
      })
    </script>
  </head>
  <body>
    <button id="btn">버튼</button>
  </body>
</html>

 

 

위 소스로 보았을 때 브라우저는 head태그에 있는 script를 먼저 파싱하고

body 태그 안에 있는 DOM 요소를 파싱하기 때문에 

id가 btn인 button 태그를 불러오기 전에 스크립트로 해당 태그를 불러오려고 했으니 undefined 에러가 출력이 된다.

 

위와 같은 이슈를 해결하기 위해서 몇 가지 방법이 있다.

 

1. </body> 태그 상단에 스크립트 작성

보통 스크립트 태그는 </body> 태그 위에 위치해서 사용하는 경우가 많은데

습관적으로 사용해서 의식을 못할 수도 있지만 DOM 파싱 순서 이슈 때문에 사용하게 된다.

<!doctype html>
<html lang="ko">
  <head></head>
  <body>
    <button id="btn">버튼</button>
    
    <script>
      const btn = document.querySelector('#btn');
      btn.addEventListener('click', function(){
        console.log('Hello World');  // 콘솔창에 'Hello World' 정상 출력
      })
    </script>
  </body>
</html>

 

2. load 이벤트 리스너 사용

window.onload나 DOMContentLoaded 이벤트 리스너를 사용해서 HTML이 파싱된 후에 스크립트를 실행할 수 있도록 처리할 수 있다.

  •  window.onload : HTML 파싱으로 DOM 생성, 외부 콘텐츠(이미지, css, script 등) 모든 요소가 로드된 후 발생하는 이벤트
  • DOMContentLoaded : HTML 파싱으로 DOM 생성만 한 후 발생하는 이벤트

 

*window.onload

모든 요소를 다 로드한 후 스크립트를 뿌려주다보니 페이지 내에 요소가 많을 경우 스크립트를 실행하기까지 초기 로딩 시간이 길어질 수 있다.

<!doctype html>
<html lang="ko">
  <head>
    <script>
      window.onload = function(){
        const btn = document.querySelector('#btn');
        btn.addEventListener('click', function(){
          console.log('Hello World');
        })
      }
    </script>
  </head>
  <body>
    <button id="btn">버튼</button>
  </body>
</html>

 

 

* DOMContentLoaded 

HTML DOM만 생성되고 스크립트를 실행하다보니 window.onload 보다 빠르게 실행된다.

<!doctype html>
<html lang="ko">
  <head>
    <script>
      documnet.addEventListener('DOMContentLoaded', function(){
        const btn = document.querySelector('#btn');
        btn.addEventListener('click', function(){
          console.log('Hello World');
        })
      });
    </script>
  </head>
  <body>
    <button id="btn">버튼</button>
  </body>
</html>

 

 

3. defer, async 속성으로 script 로드

HTML5에 추가된 속성으로 script태그에 defer, async 속성을 추가해서 사용한다.

  • defer : HTML 파싱과 함께 비동기로 script를 불러온다.(script 파일은 가져오지만 바로 실행하지 않는다.)
  • async : HTML 파싱과 함께 비동기로 script를 불러온다.(script 파일을 가져오면서 바로 실행한다.)
더보기

*비동기: 브라우저가 HTML 파싱을 하면서 스크립트 태그를 만났을 때하던 작업 (HTML 파싱)을 멈추지 않고 script 파일을 병렬적으로 가져온다.

defer 속성은 HTML을 파싱하면서 script 파일만 불러오고 실행은 나중에 하지만

async 속성은 HTML을 파싱하면서 script 파일을 불러옴과 동시에 실행을 같이 하고 실행하는 순간에는 HTML 파싱을 일시 정지하기 때문에 경우에 따라 에러가 발생할 수 있다.

 

그렇기 때문에 대부분의 경우에는 defer 속성을 사용하고 async는 필요에 따라 사용하면 좋을 것 같다.

 

 

 

 

 

 

 


참고

https://www.youtube.com/watch?v=7qVc4Ez0fnY&list=PLlaP-jSd-nK9LiA2n07uBhzOn9wI53xGV&index=4

 

 

반응형
반응형

특정한 영역에서 텍스트 효과를 눈에 띄게 보이게 하기 위해서 텍스트가 입력되는 형태의 타이핑 효과를 자바스크립트를 이용해서 구현할 수 있다. 

 

1. 스크립트 작성

구글링을 해보면 한 줄/여러 줄일 경우를 나눠서 타이핑 효과 스크립트를 다르게 작성하는데

작업을 하다보면 한 줄 혹은 여러 줄로 수정이 많이 발생할 수 있기 때문에 여러 줄 기준으로 스크립트를 작성해놓고 그 때 그 때 맞춰서 텍스트만 수정해주는게 좋아 여러 줄 기준으로 스크립트를 작성했다.

 

아래 소스의 변수들을 간략히 설명하면

 

- txtString : 내가 작성할 문자열 (두 줄로 입력할 경우 문자열에 \n 기호 입력)

- txtSpeed : 타이핑 속도를 조절할 숫자

- txtDelay : 타이핑 완료 후 새로 입력되는데까지 대기 시간

- txtIndex : 타이핑 되는 현재의 문자열 index

- *typeControl : 타이핑 완료 후에 타이핑을 멈추게 하기 위해 on/off 기능을 위한 변수

- *txtNow : txtIndex를 setInterval을 돌리면서 하나씩 증가시켜 txtString에 작성했던 문자열을 하나씩 받아온다. 추가로 받아오는 문자열에 \n (줄바꿈) 특수기호가 있으면 <br> 태그를 입력해준다.

const txtWrap = document.querySelector('.typing');
const txtString = '안녕하세요! \n Hello World!';
const txtSpeed = 300;
const txtDelay = 2000;
let txtIndex = 0;
let typeCotrol = true;

function typingEvent(){
    if(typeCotrol === true){  // true일 경우 타이핑 진행, false일 경우 타이핑 중지
        let txtNow = txtString[txtIndex++];
        txtWrap.innerHTML += txtNow === "\n" ? "<br>": txtNow;
        if(txtIndex >= txtString.length){  // 문자열 index와 문자열 length를 비교해 문자열 마지막을 판별
            txtIndex = 0;  // 문자 입력을 다 하면 index를 0으로 초기화
            typeCotrol = false;  // 입력 완료시 typeControl을 false로 바꿔 입력 막기
        }
    }else{
        // typeControl이 false일 경우
        clearInterval(setTyping);  // setInterval 제거 (타이핑 멈춤)
        setTimeout(function(){
            txtWrap.innerHTML = '';
            typeCotrol = true;
            setTyping = setInterval(typingEvent, txtSpeed);  // txtDelay만큼 시간 후 재 입력
        }, txtDelay)
    }
}

let setTyping = setInterval(typingEvent, txtSpeed);  // setInterval을 통한 텍스트 입력

 

기본적으로 setInterval을 통해 인덱스를 증가시켜 변수로 담아놓을 문자열을 하나씩 받아온다.

이 때 주의해야할 점은 

txtWrap.innerHTML = 문자열이 아니라

txtWrap.innerHTML += 문자열 로 작성해야 문자열이 하나씩 추가가 된다. (+=)

 

 

2. CSS 작성

단순 텍스트 입력만 구현하는 것 보다 실제 텍스트를 입력하는 것 같이 깜빡이는 커서 효과도 줄 수 있다.

.typing:after  {
  display:inline-block;
  margin-left:2px;
  vertical-align:middle;
  content:'';
  background:#000;
  width:2px;
  height:22px;
  animation:blink infinite 1s ease;
}

@keyframes blink {
  0%	{opacity:0;}
  100%	{opacity:1;}
}

 

위와 같이 가상 선택자 after를 사용해서 임의의 바(bar)를 만들고

animation을 사용해서 깜빡임 효과를 주면 보다 리얼하게 텍스트 입력 효과를 만들 수 있다.

 

 

 

예시 화면

 

See the Pen Typing by KK (@hyunjin-k) on CodePen.

 

 

반응형
반응형

Axios(액시오스)는 뷰에서 권고하고 있는 HTTP 통신 라이브러리 이고

Promise 기반이다.

 

*Promise : 자바스크립트 비동기 처리 패턴

 

HTTP 통신 라이브러리인 Axios를 알아보기 전에 간단하게 HTTP 통신 구조를 알아보려한다.

1. HTTP 통신 구조 (클라이언트 <-> 서버)

브라우저 (클라이언트)와 서버에서 HTTP 통신 방법을 간단하게 정리하자면

  1. 브라우저(클라이언트)에서 HTTP Requset를 통해 서버의 특정 백엔드 로직을 실행하고
  2. 백엔드 로직을 통해 DB에서 로직에 맞는 데이터를 가져온다.
  3. 가져온 데이터를 또 다시 특정 백엔드 로직을 통해 HTTP Response로 브라우저(클라이언트)에 응답을 한다.
  4. HTTP Response를 통해 가져온 데이터를 브라우저에 뿌려준다.

위와 같이 HTTP 통신 구조를 간략하게 정리할 수 있다.

 

2. Axios 적용

CDN 방식 or NPM 방식으로 axios 라이브러리를 설치한 뒤에

<div id="app">
    <button v-on:click="getData">get user</button><!-- 버튼 클릭시 vue 인스턴스에 저장해놓은 getData 메소드를 호출한다. -->
    <div> {{ users }} </div>
</div>
 
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
    new Vue({
        el: '#app',
        data: {
            users: []
        },
        methods: {
            getData: function(){
                var vm = this;
                axios.get('https://jsonplaceholder.typicode.com/users/')    // https://jsonplaceholder.typicode.com/users/ 에서 데이터 받아오기
                    .then(function(response){
                        console.log(response.data);     // response에서 data 속성 값 출력
                        // this.users = response.data;  // 함수 안에 this로 해당 this는 vue 인스턴스에 대한 this가 window를 가르킨다.
                        vm = response.data;             // getData 함수 내부에 this를 따로 변수에 담아서 사용
                    }.catch(function(){
                        console.log(error);
                    });
            }
        }
    });
</script>

 

vue 인스턴스 methods에 axios를 사용하기 위한 함수를 등록해준다.

axios.get() : 데이터 가져오기

axios.then() : 비동기 통신이 성공했을 경우 처리

axios.catch() : 통신시 오류가 발생했을 때의 처리

 

위의 소스와 같이 작성할 때 주의해야 할 점은 vue 인스턴스 내부에 있는 데이터에 접근하기 위해 this를 사용하게 되는데

함수 내에서 this를 사용하게되면 함수 내부에서 this를 가르키게 되어 this는 윈도우를 가르키게 된다.

 

그렇게 때문에 getData 메소드 내부에 this를 별도 변수에 담아서 사용하거나

then에서 function(){} 이 아닌 => 화살표 함수 방식으로 작성하여 this가 원하는 방향으로 지정되도록 처리할 필요가 있다.

 

 


출처

인프런 'Vue.js 시작하기 - Age of Vue.js' 강좌

 

 

반응형

'Frontend > Vue.js' 카테고리의 다른 글

[vue 2 기초] 라우터  (1) 2023.11.06
[vue 2 기초] 컴포넌트 통신(props, event emit)  (1) 2023.11.04
[Vue 2 기초] 컴포넌트  (0) 2023.11.03
[Vue 2 기초] 인스턴스  (0) 2023.11.03
Vue.js 란?  (0) 2023.11.03
반응형

라우터는 뷰 라이브러리를 이용하여 SPA(Single Page Appliaction) 구현을 할 때 사용하는 라이브러리이다.

 

1. 라우터 연결

라우터를 사용하기 위해서는 CDN 방식 혹은 NPM 방식으로 설치할 수 있다.

 

CDN 방식은 script 태그에 CDN 링크를 추가해서 연결을 하는 방식이고

NPM 방식은 Vue CLI로 프로젝트를 생성하거나 NPM 기반으로 프로젝트를 생성해서 라우터 설치 명령어 입력을 통해 설치한다.

<!-- CDN 방식 -->
<script src="https://unpkg.com/vue@2"></script>
<script src="https://unpkg.com/vue-router@3"></script>
  
<!-- NPM 방식(아래 명령어 입력) -->
npm install vue-router@3

 

위와 같이 설치한 뒤 아래 소스와 같이 뷰 라우터를 등록할 수 있다.

 

var router = new VueRouter({
    // 라우터 옵션
});
 
// 인스턴스에 라우터 인스턴스를 등록
new Vue({
    router: router,     //  라우터 인스턴스의 변수명으로 등록
});

 

2. 라우터 옵션

라우터를 연결 및 등록한 뒤 라우터를 사용할 때 라우터 옵션을 지정한다.

2가지의 필수 옵션이 있다.

 

- routes: 라우팅 할 URL과 컴포넌트 값 지정(페이지의 라우팅 정보)

- mode: URL의 해쉬 값 제거 속성

 

var LoginComponent = {
    template: '<div>Login</div>'
}
var MainComponent = {
    template: '<div>Main</div>'
}
 
var router = new VueRouter({
    mode: 'history',
    routes: [   // 배열 형식으로 지정
        {
            // 페이지의  url 이름
            path:'/login',
            // 해당 url에서 표시될 컴포넌트
            component: LoginComponent,
        },
        {
            path: '/main',
            component: MainComponent,
        }
    ]
});

 

위와 같이 라우터 옵션들을 입력해서 특정 컴포넌트들을 세부 페이지처럼 사용할 수 있게 url을 지정하고 연결해준다.

 

3. router-view

라우터 옵션으로 라우터를 컴포넌트와 연결하고 화면에 해당 컴포넌트를 뿌릴 때 router-view를 사용한다.

<div id="app">
    <router-view></router-view><!-- 라우터에 등록한 LoginComponent 혹은 MainComponent를 화면에 보여준다 -->
</div>

 

localhost:0000/router.html/login 으로 url을 입력시 LoginComponent 내용이,

localhost:0000/router.html/main 으로 url을 입력시 MainComponent 내용이 <router-view> 영역에 뿌려진다.

 

4. router-link

사용자들이 일반적인 웹 페이지에서 a태그를 통해 링크 이동을 하여 페이지를 탐색하듯

vue 화면에서 특정 링크를 클릭해서 화면을 이동할 수 있게 해주는 것이 router-link 이다. (실제 html 소스상에 <router-link> 는 a태그로 출력된다.)

<div id="app">
    <!-- <router-link to="이동할 URL"></router-link> -->
    <router-link to="/login">로그인</router-link>
    <router-link to="/main">메인</router-link>
 
    <router-view></router-view> <!-- routerlink를 클릭할 때마다 to에 연결된 url의 화면이 출력된다. -->
</div>
 
<!-- 출력
<a href="/login">로그인</a>
<a href="/main">메인</a>
-->

 

 


출처

인프런 'Vue.js 시작하기 - Age of Vue.js' 강좌

 

 

반응형

'Frontend > Vue.js' 카테고리의 다른 글

[vue 2 기초] Axios (액시오스)  (0) 2023.11.07
[vue 2 기초] 컴포넌트 통신(props, event emit)  (1) 2023.11.04
[Vue 2 기초] 컴포넌트  (0) 2023.11.03
[Vue 2 기초] 인스턴스  (0) 2023.11.03
Vue.js 란?  (0) 2023.11.03
반응형

컴포넌트의 데이터는 각 컴포넌트에서 관리하기 때문에 각 컴포넌트는 고유한 데이터 유효범위를 갖는다.

따라서 컴포넌트간 데이터를 주고 받기 위해서는 특별한 규칙을 따라야한다.

 

 

위와 같이 상→하로 props를 통해 데이터를 전달하고

하→상은 이벤트를 올려서 전달한다.

 

*컴포넌트 통신 규칙 : 직접 연관이 되어있는 상, 하위 컴포넌트 간의 데이터 전달이 가능하다. (데이터 오류 발생시 추적에 용이)

 

 

1. props (상 -> 하)

props는 위에서 설명한 것과 같이 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하는 방법이다.

 

부모 컴포넌트에 등록되어 있는 데이터를 가져오는 기본적인 사용 방법은 

 

1. 자식(하위) 컴포넌트에 props 속성을 사용해 프롭스 속성명을 등록해준다.

var appHeader = {
    template: '<h1>Title</h1>',
    props: ['propsdata']    // 프롭스 속성 명은 임의로 지정이 가능하다.
}
 
new Vue({
    el: '#app',
    component: {
        'app-header' : appHeader,
    },
    data: {
        message: 'hi',
    }
})

 

2. 자식 컴포넌트에서 v-bind를 사용해서 상위 컴포넌트의 데이터 속성을 해당하는 자식 컴포넌트로 전달한다. 

<!-- v-bind:프롭스 명="상위 컴포넌트의 data 속성" -->
<app-header v-bind:propsdata="message"></app-header>

 

크게 위 1, 2번의 방법으로 적용할 수 있다.

 

<div id="app">
    <app-header v-bind:propsdata="message"></app-header>
    <app-content v-bind:propsnum="num"></app-content>
</div>
 
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    var appHeader = {
        template: '<h1>{{ propsdata }}</h1>',   // {{ 데이터 }} 이중 괄호를 통해 부모 컴포넌트로부터 받은 props 값을 그대로 뿌려줄 수 있다.
        props: ['propsdata'],
    }
    var appContent = {
        // 각 영역 안에 데이터는 고유의 데이터로 가진다. 따라서 appHeader와 마찬가지로 propsdata라는 같은 이름으로 사용해도 된다.
        template: '<div>{{ propsnum }}</div>',
        props: ['propsnum'],
    }
    new Vue({
        el: '#app',
        components: {
            'app-header' : appHeader,
            'app-content' : appContent,
        },
        data: {
            message: 'hi',
            num: 10
        }
    });
</script>

 

 

2. Event Emit (하 -> 상)

부모 컴포넌트의 데이터를 가져올 때 props 속성을 사용한다면

자식 컴포넌트에서는 이벤트 발생(emit)을 통해 상위로 이벤트를 전달해준다.

 

방법은

1. 자식 컴포넌트에 event emit 메소드를 등록해준다.

// appHeader에 있는 button을 클릭하면 passEvent 함수가 실행되고 그 함수는 'pass'라는 이벤트명을 가진 event emit용 함수이다.
var appHeader = {
    template: '<button v-on:click="passEvent">click me</button>',
    methods: {
        passEvent: function(){  //passEvent라는 메소드를 통해 해당 자식 컴포넌트에 $emit 이벤트명은 정의한다.
            this.$emit('pass'); // 이벤트 이름 등록 : $emit('이벤트명')
        }
    }
}
 
new Vue({
    el: '#app',
    components: {
        'app-header': appHeader,
    },
    methods: {
        logText: function(){
            console.log('hi');
        },
    }
})

 

2. 자식 컴포넌트에 $emit으로 등록한 이벤트 명을 통해 부모 컴포넌트의 메소드를 가져온다.

<!-- v:on:하위 컴포넌트에서 발생한 이벤트 이름="상위 컴포넌트의 메소드 이름" -->
<app-header v-on:pass="logText"></app-header>

 

위와 같이 작업하면 app-header 영역에 버튼이 생기고

해당 버튼을 클릭하면 자식 요소의 버튼을 클릭하지만 부모 요소에 logText() 메소드가 실행된다.

<div id="app">
    <!-- v:on:하위 컴포넌트에서 발생한 이벤트 이름="상위 컴포넌트의 메소드 이름" -->
    <app-header v-on:pass="logText"></app-header>
    <app-content v-on:number="addNum"></app-content>
</div>
 
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    var appHeader = {
        template: '<button v-on:click="passEvent">click me</button>',
        methods: {
            passEvent: function(){
                this.$emit('pass');
            }
        }
    }
    var appContent = {
        template: '<button v-on:click="addNumber">add</button>',
        methods: {
            addNumber: function(){
                this.$emit('number');
            }
        }
    }
    new Vue({
        el: '#app',
        components: {
            'app-header': appHeader,
            'app-content': appContent,
        },
        methods: {
            logText: function(){
                console.log('hi');
            },
            addNum: function(){
                this.num++
                console.log(this.num);
            }
        },
        data: {
            num: 10,
        }
    })
</script>

 

위와 같은 소스로 자식 컴포넌트에서의 이벤트로 부모 컴포넌트의 data 값을 변경할 수도 있다.

 

3. 같은 레벨에서의 컴포넌트 통신 방법

같은 레벨의 컴포넌트에서는 다이렉트로 데이터를 통신할 수 없고 연결되어 있는 부모 컴포넌트를 통해서 데이터를 주고받을 수 있다.

 

AppContent에서 버튼을 클릭해서 10이라는 숫자를 AppHeader로 보내기 위해서는 다음과 같이 적용할 수 있다.

 

1. AppContent에서 Root 컴포넌트로 event emit을 이용해서 이벤트를 전달한다.

<div id="app">
    <app-header></app-header>
    <app-content v-on:pass="deliverNum"></app-content>
</div>
 
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    var appHeader = {
        template: '<div>header</div>',
    }
    var appContent = {
        template: '<div>Content <button v-on:click="passNum">pass</button></div>',
        methods: {
            passNum: function(){
                this.$emit('pass', 10); // 10이라는 숫자를 pass 이벤트에 담아 보낸다.
            },
        }
    }
    new Vue({
        el: '#app',
        components: {
            'app-header' : appHeader,
            'app-content' : appContent,
        },
        data: {
            num: 0,
        },
        methods: {
            deliverNum: function(value){    // value는 $emit으로 보낸 10이 담겨온다.
                this.num = value;
            }
        }
    });
</script>

 

2. 부모 컴포넌트에서 AppContent로부터 받아온 이벤트를 AppHeader로 props를 통해 전달한다.

<div id="app">
    <app-header v-bind:propsdata="num"></app-header>
    <app-content v-on:pass="deliverNum"></app-content>
</div>
 
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    var appHeader = {
        template: '<div>header</div>',
        props: ['propsdata']    // props 등록
    }
    var appContent = {
        template: '<div>Content <button v-on:click="passNum">pass</button></div>',
        methods: {
            passNum: function(){
                this.$emit('pass', 10);
            },
        }
    }
    new Vue({
        el: '#app',
        components: {
            'app-header' : appHeader,
            'app-content' : appContent,
        },
        data: {
            num: 0,
        },
        methods: {
            deliverNum: function(value){
                this.num = value;
            }
        }
    });
</script>

 

위 처럼 작업하게 되면 AppContent에 있는 버튼을 클릭할 때 

AppHeader에 propsdata:10 값이 전달된다.

 

 

 


참고

인프런 'Vue.js 시작하기 - Age of Vue.js' 강좌

 

 

 

반응형

'Frontend > Vue.js' 카테고리의 다른 글

[vue 2 기초] Axios (액시오스)  (0) 2023.11.07
[vue 2 기초] 라우터  (1) 2023.11.06
[Vue 2 기초] 컴포넌트  (0) 2023.11.03
[Vue 2 기초] 인스턴스  (0) 2023.11.03
Vue.js 란?  (0) 2023.11.03
반응형

컴포넌트란 화면을 영역별로 구분하고 개발할 수 있는 뷰의 기능이다. 

컴포넌트 단위로 화면을 개발하게 되면 특정 영역을 여러 페이지에서 사용하게 될 때 특정한 컴포넌트를 가져와서 사용하면 되기 때문에 재사용성이 올라가고 효율이 올라간다.

 

1. 전역 컴포넌트

<div id="app">
    <app-header></app-header>   <!-- 컴포넌트를 생성하면 컴포넌트 태그를 써서 적용한다. -->
    <app-content></app-content>
</div>
 
<!-- 적용 후
<div id="app">
    <h1>Header</h1>
    <div>Content</div>
</div>
-->
 
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    // Vue.component('컴포넌트 이름', 컴포넌트 내용);
    // 전역 컴포넌트
    Vue.component('app-header', {
        template: '<h1>Header</h1>',
    });
    Vue.component('app-content', {
        template: '<div>Content</div>',
    });
 
    new Vue({
        el: '#app',
    });
</script>

 

위와 같은 소스를 Vue 개발자 도구에서 확인해보면

<Root>
    <app-header>
    <app-content>

 

위와 같이 생성된다.

(인스턴스를 생성하면 기본적으로 <Root> 컴포넌트가 만들어진다.)

 

위 방법은 전역 컴포넌트를 등록하는 방법이고 실 서비스에서는 주로 지역 컴포넌트를 사용하게 된다.

 

2. 지역 컴포넌트

<div id="app">
    <app-footer></app-footer>
</div>
 
<!-- 적용 후
<div id="app">
    <footer>Footer</footer>
</div>
-->
 
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    new Vue({
        el: '#app',
        // 지역 컴포넌트
        components: {
            // '키(컴포넌트 이름)' : 값(컴포넌트 내용)
            'app-footer' : {
                template: '<footer>Footer</footer>'
            }
        },
    });
</script>

 

3. 전역 컴포넌트, 지역 컴포넌트의 차이

- 지역 컴포넌트는 components 속성에 있는 컴포넌트들을 통해 해당 페이지를 봤을 때 어떤 컴포넌트들이 연결이 되어있는지 명시적으로 확인이 수월하다. (일반적으로는 지역 컴포넌트 사용)

- 전역 컴포넌트는 플러그인이나 라이브러리와 같이 전역으로 사용해야하는 컴포넌트만 등록해서 사용한다.

 

 

 


참고

인프런 'Vue.js 시작하기 - Age of Vue.js' 강좌

 

 

 

반응형

'Frontend > Vue.js' 카테고리의 다른 글

[vue 2 기초] 라우터  (1) 2023.11.06
[vue 2 기초] 컴포넌트 통신(props, event emit)  (1) 2023.11.04
[Vue 2 기초] 인스턴스  (0) 2023.11.03
Vue.js 란?  (0) 2023.11.03
SPA 란?  (0) 2020.04.20
반응형

인스턴스는 뷰를 개발할 때 필수로 생성해야하는 코드이다.

 

인스턴스는 기본적으로 new Vue() 로 생성한다.(Vue 2 기준)

 

<div id="app"></div>
 
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    new Vue({
        el: '#app',     // 인스턴스가 그려지는 화면의 시작점
        template: ,     // 화면에 표시할 요소 (HTML, CSS 등)
        data: ,         // 뷰의 반응성(Reactivity)이 반영된 데이터 속성
        methods: ,      // 화면의 동작과 이벤트 로직을 제어하는 메소드
        created: ,
        watch: ,
    });
</script>

 

vue에서 사용할 수 있는 속성과 API는 위와 같이 el, template, data, methods, created, watch 등이 있다.

 

*인스턴스 내 this

인스턴스 내에서 this는 인스턴스 객체 자체를 가르킨다.

var Vue = {
    el: '',
    data: {
        num: 10,
    },
    methods: {
        getNumber: function(){
            console.log(this.num);  // 10 출력
        }
    }
}

 

 

 


참고

인프런 'Vue.js 시작하기 - Age of Vue.js' 강좌

 

 

 

 

반응형

'Frontend > Vue.js' 카테고리의 다른 글

[vue 2 기초] 라우터  (1) 2023.11.06
[vue 2 기초] 컴포넌트 통신(props, event emit)  (1) 2023.11.04
[Vue 2 기초] 컴포넌트  (0) 2023.11.03
Vue.js 란?  (0) 2023.11.03
SPA 란?  (0) 2020.04.20
반응형

일반적인 웹에서는 html, css, javascript를 이용해서 화면을 구성하고 조작한다.

 

Vue.js 

 

 

<div id="app"></div>
 
<script>
    const app = document.querySelector('#app');
    let str = 'hello';
    app.innerHTML = str;    // #app 영역에 hello 텍스트 노출
 
    str = 'Hi';
    app.innerHTML = str;    // str 변수의 값을 바꿨지만 바뀐 내용이 즉시 반영되는 것이 아닌 innerHTML 을 통해 다시 선언해줘야 바뀐 변수값이 #app 영역에 적용된다.
</script>

 

반면 Vue.js는 MVVM(Model-View-ViewModel) 패턴으로 

화면에서 보여지는 내용들을(View) Vue의 Dom Listeners를 통해 자바스크립트에 있는 데이터를 바꿔주거나 특정 자바스크립트를 실행하게 된다.

그리고 Dom Listeners를 통해 자바스크립트의 데이터가 변경 됐을 때 Data Bindings를 통해 변경된 내용을 화면에 반영하게 된다.

<div id="app"></div>
 
<script>
    const app = document.querySelector('#app');
    const viewModel = {};
 
    // Object.defineProperty(대상 객체, 객체의 속성, {
    //      정의할 내용
    // })
 
    Object.defineProperty(viewModel, 'str', {
        // 속성에 접근했을 때의 동작을 정의
        get: function(){
            console.log('접근')       // 콘솔창에서 viewModel.str 을 입력하면 '접근' 출력
        }
        // 속성에 값을 할당했을 때의 동작을 정의
        set: function(newValue){
            console.log('할당', newValue)     // 콘솔창에서 viewModel.str = 'Hi' 를 입력하면 '할당 10'  출력
            app.innerHTML = newValue        // viewModel.str 값을 할당/변경할 때 마다 해당하는 값을 app에 뿌려준다.
        }
    });
</script>

 

* Object.defineProperty : 객체의 속성에 접근, 할당 했을 때의 동작을 정의한다.

 

위와 같이 데이터를 접근/변경할 때 마다 바로바로 데이터를 바인딩 시켜주는 것은 Vue.js의 가장 중요한 특징인 Reactivity이다.

 

 

반응형

'Frontend > Vue.js' 카테고리의 다른 글

[vue 2 기초] 라우터  (1) 2023.11.06
[vue 2 기초] 컴포넌트 통신(props, event emit)  (1) 2023.11.04
[Vue 2 기초] 컴포넌트  (0) 2023.11.03
[Vue 2 기초] 인스턴스  (0) 2023.11.03
SPA 란?  (0) 2020.04.20
반응형

ios에서 특정 버튼을 연속으로 클릭을 하게 되면

더블 탭 확대로 인식을해서 화면이 확대되고 버튼 클릭이 부자연스러워지는 경우가 있다.

 

위와 같은 경우에 touch-action 이라는 css 속성으로 해결할 수 있다.

 

1. touch-action

touch-action은 모바일이나 태블릿과 같이 터치 액션이 일어나는 디바이스에서 터치 이벤트를 처리할 때 특정 터치 액션만 가능하도록 적용하는 속성이다.

 

ex) touch-action:none 의 경우 모든 터치 이벤트를 비활성한다.

 

2. touch-action:manipulation

touch-action 속성 중 manipulation은 터치를 통한 수평, 수직 스크롤과 여러 손가락을 통한 확대/축소는 가능하게 해주고 그 외의 다른 동작들은 불가능하게 한다. 

.btn_tool {touch-action:manipulation;}

 

 

 

반응형
반응형

뷰포트 단위로 높이 값을 주고 싶을 때 vh 단위를 사용한다.

2020.04.22 - [Frontend/CSS] - [css 단위] vw, vh, vmin, vmax (Viewport 단위)

 

그치만 모바일 주소 표시줄 영역에 의해서 100vh로 높이 값을 설정했다고 하더라도

화면이 짤리는 경우가 발생할 수 있다.

이미지 출처 : https://css-tricks.com/the-trick-to-viewport-units-on-mobile/

 

그럴 때 해결할 수 있는 방법 중 하나는 

div {height:100vh; height:-webkit-fill-available;}

위와 같이 -webkit-fill-available 값을 height값에 적용하는 방법이 있는데

이 방법은 ios에서만 적용이 되고 안드로이드에서는 적용이 안되는 문제가 있기 때문에 권장하진 않는다.

 

이미지 출처 : https://blanche-toile.com/web/large-small-and-dynamic-viewport-units

 

1. dvh (Dynamic Viewport Height)

dvh는 주소 표시줄이 스크롤을 통해 축소가 되건 노출이 되고 있건 상관 없이

현재 보여지는 뷰포트 높이를 동적으로 가져온다. 

 

위 이미지의 예시 처럼 100dvh는 주소표시줄의 유무에 따라 값이 달라진다.

div {height:100dvh;}

 

2. svh (Short Viewport Height)

svh는 사용자 화면 기준으로 가장 짧은 뷰포트 값을 가져온다.

주소 표시줄이 없어져도 기존 주소표시줄의 높이 값을 뺀 나머지 값을 가져온다.

div {height:100svh;}

 

3. lvh (Large Viewport Height)

lvh는 svh와는 반대로 사용자 화면 기준으로 가장 긴 뷰표트 값을 가져온다.

주소 표시줄이 있더라도 주소 표시줄이 없을 때의 총 화면의 길이를 가져온다.

div {height:100lvh;}

 

 

위 3가지의 단위를 볼 때

주소 표시줄의 유무에 따라 뷰포트 높이가 가변적으로 변할 때를 고려해서

dvh를 사용하는 것이 가장 무난하고 우리가 원하는 화면을 보여주는데에 좋은 선택이 될 것이다.

 

 

*dvh, svh, lvh는 브라우저(pc, mobile) 하위 버전에서는 제공이 되지 않기 때문에 작업 환경을 고려한 적용이 필요하다.

 

 


참고

https://medium.com/quick-code/100vh-problem-with-ios-safari-92ab23c852a8

https://blanche-toile.com/web/large-small-and-dynamic-viewport-units

https://www.w3.org/TR/css-values-4/#viewport-relative-u'nits

 

 

 

 

반응형
반응형

css를 통해 animation을 사용하거나 마우스오버 혹은 특정 버튼을 클릭했을 때 클래스 추가하여 transform을 조정하게 될 경우 영역이 깜빡이거나 잠시 흐려지는 현상을 경험한 적이 있을 것이다.

 

1. CSS핵 (translateZ or translate3d) 사용

위와 같은 부자연스러운 transform, animation 현상이 발생하는 것을 해결하기 위해 '*하드웨어 가속'을 활성화해 대상이 되는 엘리먼트를 빠르게 렌더링 처리하는 방법을 사용했다.

*하드웨어 가속
- 하드웨어 가속은 중앙처리장치(CPU)가 하는 일을 그래픽 처리 장치(GPU)가 분담하여 처리해서 컴퓨터의 성능을 최대한으로 끌어올리고 브라우저 렌더링을 보다 빠르게 할 수 있게 만든다.

 

일반적인 css animation, transform, transition 속성에는 하드웨어 가속이 적용되지 않기 때문에 브라우저를 속여서 강제로 3D 처리를 하도록 브라우저에 지시해 하드웨어를 가속화 하는 방법을 사용했다.

그 방법이 translateZ 혹은 translate3d이다.

transform: translate3d(0,0,0);

transform: translateZ(0)

하지만 이러한 방법은 RAM이나 그래픽 처리 장치(GPU)의 사용량을 커지게하여 페이지에 병목 현상을 줘서 오히려 악영향을 끼치게 한다.

 

2. will-change

위 css핵을 대체할 수 있는 새로운 css 속성이 생겼다. 그 속성이 will-change 이다.

속성명 그대로 어떤 속성이 변경이 될 것인지 미리 엘리먼트에 적용하여 브라우저가 해당 css를 읽을 때 변경 될 속성을 알게하여 미리 그 변경에 대비할 수 있게 하는 것이다.

will-change: auto;		/* 기본 값 */
will-change: scroll-position;	/* 스크롤 위치가 변경 될 예정 */
will-change: contents;		/* 요소의 컨텐츠 내용이 변경 될 예정 */

/* 특정 css 속성 적용 */
will-change: transform;		/* transform 속성이 변경 될 예정 */
will-change: opacity;		/* opacity 속성이 변경 될 예정 */
will-change: left, top;		/* left, top 값이 변경 될 예정 */
will-change: transform, opacity

 

 

적용예시)

<style>
  .thmb {width:100px; height:100px; border-radius:50%; overflow:hidden;}
  .thmb img {transition:0.3s transform; will-change:transform;}
  .thmb:hover img {transform:scale(1.2);}
</style>

<!-- .thmb 마우스오버시 img 확대 -->
<div class="item">
  <div class="thmb">
    <img src="picture.jpg">
  </div>
</div>

위와 같은 소스상 .thmb에 마우스 오버시 img가 확대되는 효과를 구현하고자 할 때

transform을 통해 scale을 변경할 img태그에 미리 will-change 속성을 적용해줄 수 있다.

 

3. will-change 사용상의 주의사항

브라우저 성능을 최적화 하겠다고 will-change를 과다하게 사용하면 안된다.

브라우저는 기본적으로 브라우저가 사용할 수 있는 최적화를 최대한으로 적용한다.

그리고 브라우저가 적용한 최적화를 삭제하고 가능한한 빨리 브라우저가 처리해야할 다른 작업들을 실행한다.

그런데 will-change를 선언하게 되면 이러한 브라우저의 특성을 무시하고 브라우저가 최적화에 더 많은 시간을 쏟게 한다.

즉, 오히려 브라우저 최적화에 악영향을 끼친다.

 

그렇기 때문에 will-change는 정말 필요한 경우에만 적절하게 사용해야한다.

 

4. 자바스크립트를 이용한 will-change 적용

3번과 같은 브라우저 최적화 이슈가 있기 때문에 가능하면 will-change를 사용할 때는 자바스크립트를 사용해서 필요한 순간에만 적용하고 다시 will-change를 초기화 시키는 방법을 적용할 수 있다.

<!-- .thmb 마우스오버시 img 확대 -->
<div class="item">
  <div class="thmb">
    <img src="picture.jpg">
  </div>
</div>

<script>
  // 클릭할 때 애니메이션을 재생할 엘리먼트를 선택합니다.
  var item = document.querySelector('.item');

  // 엘리먼트의 조상 요소에 마우스 커서가 올라가면 will-change를 설정한다.
  item.addEventListener('mouseenter', hintBrowser);
  // 엘리먼트의 조상 요소에 마우스 커서가 내려가면 will-change를 초기화한다.
  item.addEventListener('mouseleave', removeHint);

  function hintBrowser() {
    this.querySelector('img').style.willChange = 'transform, opacity';
  }

  function removeHint() {
    this.querySelector('img').style.willChange = 'auto';
  }
 </script>

 

 


참조

https://dev.opera.com/articles/ko/css-will-change-property/

 

 

반응형
반응형

자바스크립트 공부를 하거나 면접을 보다보면 호이스팅에 대해 설명을 하거나 이해를 요구할 때가 많다.

그렇기 때문에 호이스팅에 대해 완벽하게 이해를 하고 있어야 원활한 자바스크립트 사용을 할 수 있다.

 

1. 호이스팅이란?

변수나 함수를 스코프 범위 안에서 언제든지 사용할 수 있도록 하는 것을 말한다.

예시를 보면 위의 말이 어떤 내용인지 이해할 수 있다.

console.log(a);  // undefined

var a = 123;

위의 코드를 보면 console.log를 통해 a라는 변수를 불러왔는데 해당 구문 위에는 a변수 선언이 없었음에도 에러메시지가 뜨는 것이 아니라 undefined가 뜨는 것을 볼 수 있다.

var a;

console.log(a);

a = 123;

즉 실제 코드의 실행은 위의 소스와 같이 적용이 됐다는 것을 알 수 있다. a라는 변수 선언을 호이스팅하고 그 a를 console.log를 통해 불러왔으니 아직 할당 된 값이 없기 때문에 undefined가 노출이 되는 것이다.

[!] 호이스팅은 선언된 변수가 스코프 범위 내에서 최상단으로 이동해서 하단에 있는 영역에서 그 변수들을 사용할 수 있도록 해주는 것이라 이해하면 쉽다. (실제로 변수의 위치가 상단으로 이동하는 것은 아니다.)

 

하지만 변수 선언은 var만 있는 것이 아니라 let, const 변수 선언이 있다.

그리고 let과 const는 var와 같은 호이스팅의 형태를 가지고 있지 않다.

 

2. var 변수 호이스팅

위의 예시와 같이 var는 변수 선언을 스코프 내에서 호이스팅한다. (선언과 동시에 초기화)

그리고 var 변수 선언은 특징이 있는데 함수(function) 내부에서 선언한 변수는 지역변수로 호이스팅이 되지만

함수 외에 if문, for문, while문 등의 문법에서는 해당 구문 안에서 사용된 var 변수 선언들을 전역변수로 호이스팅 시켜버린다.

 

- 함수

function test(){
  var a = 123;
  console.log(a);
}

test();  // 123 출력
console.log(a);  // 에러

- for 문

for (var i = 0; i < 5; i++){
  console.log(i);
  // 1
  // 2
  // 3
  // 4
}

console.log(i); // 5

위 for문의 예시를 보면 for문 안에서 변수 i를 선언했지만 for문 밖에서 console.log를 통해 i를 호출했을 때 for문이 다 돈 후 숫자 5가 출력이 되는 것을 볼 수 있다. 즉 for문 밖에서도 for문 안에서 선언한 변수를 사용할 수 있다는 것을 알 수 있다.

 

함수를 제외한 모든 구문 및 환경에서 var로 선언한 변수는 변수 선언 자체를 최상단으로 다 올린다고 생각하면 된다. (실제로 변수들을 제일 위로 올리는 것은 아니다)

 

이처럼 함수 내부에서만 지역 변수로 사용이 되는 var 변수는

함수 레벨 스코프 변수이다.

3. let, const 변수 호이스팅

위에서 설명한 var 변수 선언과 let, const 변수 선언은 다르다. 예시를 보면 쉽게 알 수 있다.

console.log(a);  // 에러

let a = 123;

console.log(a); // 123

var였다면 위의 소스는 undefined가 출력이 됐을텐데 let으로 변수 a를 선언하면 위의 소스는 에러가 출력된다. 이게 let과 var의 호이스팅에 가장 큰 차이인데, var는 선언된 변수들을 함수를 제외하곤 호이스팅하여 모든 곳에서 사용할 수 있도록 하고 let은 변수가 선언된 위치에 도달했을 때 그 기점을 기준으로 변수를 초기화한다. const 변수도 동일한 특성을 가진다.

즉 해당 let, const 변수 선언 이후에서야 비로소 해당 변수 값을 사용할 수 있다는 뜻이다.

 

그렇다고 let이나 const가 호이스팅을 안하는 것은 아니다. 위와 같이 변수가 선언된 이후에야 그 변수를 사용할 수 있는 이유는 TDZ(Temporal Dead Zone) 영역의 특징 때문이다.

let과 const변수가 선언되기 이전에 내용들은 일시적으로 죽은 영역이 된다. 즉 사용할 수 없는 영역이 된다.

console.log(a);

//////////////// TDZ (let으로 선언한 변수 a를 사용할 수 없다)

let a = 123;

console.log(a); // 123

그리고 var와는 또 다른 특징이 있는데 var는 함수에서만 지역변수로 호이스팅되고 for, if문 과 같은 다른 구문에서는 전역 변수로 호이스팅이 되었었는데 let, const는 모든 구문에서 지역변수 단위로 호이스팅이 된다. 이를 블록 레벨 스코프라고 부른다.

각 {} 중괄호 안에 있는 블록 블록마다의 요소들에 각각의 지역 변수를 선언해준다는 뜻이다.

for(let i = 0; i < 5; i++){
  console.log(i);
}

console.log(i);  // 에러

var에서 예시로 봤던 for문을 예로 들면

for문 안에 let으로 변수 i를 선언하고 그 바깥에서 console.log로 i를 호출했을 때 for문 안에서 i를 선언했기 때문에 에러메시지가 뜨는 것을 알 수 있다.

 

만약 for문 바깥에서도 i를 사용하고 싶다면 let i 변수 선언을 for문 바깥에서 해주는 것도 방법이다.

let i;

for(i = 0; i < 5; i++){
  console.log(i);
}

console.log(i);  // 5

 

var, let, const 변수 선언에 대한 추가적인 설명은 하단 링크에도 작성해놓았다.

2020.06.03 - [Frontend/Javascript] - Javascript (자바스크립트) - 변수 선언 (var, let, const)

 

4. 함수 호이스팅

변수와 마찬가지로 함수도 호이스팅이 있다.

(단, 함수 선언문 형식으로 선언 된 함수만 해당한다. ex. function aa(){})

aa(); // 'test' 출력

function aa(){
  console.log('test');
}

위와 같이 코드를 작성한다 했을 때 aa 함수는 aa() 호출보다 아래쪽에 있어서 함수 실행이 안될 것 같지만

함수 호이스팅으로 인해 function aa(){} 부분이 스코프 내에서 언제든지 사용할 수 있게 되고 스코프 범위 안에서는 어디서든 함수를 호출하고 실행할 수 있다.

aa(); // 'test' 출력

function aa(){
  console.log('test');
  
  function bb(){
    console.log('test2');
  }
}

bb(); // 함수 내부에서 선언한 함수는 밖에서 사용할 수 없기 때문에 에러 출력
반응형
반응형

원하는 영역에 배너나 이미지 썸네일을 넣게 될 경우에 정확한 사이즈의 이미지를 받아서 넣거나 2배, 3배와 같은 배수의 이미지를 받아서 넣는 경우 이미지가 흐려보이는 경우는 없겠지만 작업을 하다보면 내가 원하는 사이즈의 이미지를 넣을 수 없는 경우도 있다.

 

이미지를 원래의 사이즈 혹은 원본 사이즈의 배수에 해당하는 사이즈로 넣지 않을 경우 이미지가 흐려(blur) 보이는 경우가 많이 있는데 특히 크롬에서 이 현상이 더 심하게 보인다.

좌측은 이미지의 원본 사이즈를 그대로 놓은 것이고 우측은 원본 이미지를 축소해서 노출을 시킨 것이다.

 

한 눈에 보더라도 우측 이미지가 흐릿하게 보인다는걸 알 수 있다. 일반적인 이미지일 경우에 눈감아줄 수 있을 정도의 흐림인 경우도 있지만 텍스트가 들어간 이미지인 경우 흐린 효과가 더 부각되어 보인다.

 

이러한 현상을 해결하기 위해 css 처리가 필요하다.

 

1. image-rendering(이미지 랜더링 방식 변경)

image-rendering은 확대, 축소이미지에만 적용이 되는 css 이다.

원본 이미지를 그 사이즈 그대로 사용하게 될 경우엔 css가 따로 적용되는 내용이 없지만 특정 이미지를 확대하거나 축소를 할 경우에 해당 css를 사용하면 효과를 발현한다.

.banner{
  image-rendering: -moz-crisp-edges; /* firefox */
  image-rendering: -o-crisp-edges; /* opera */
  image-rendering: -webkit-optimize-contrast; /* chrome(비표준) */
  image-rendering: crisp-edges;
}

crisp-edges는 이미지의 색상대조를 최적화하여 랜더링하게 만들어 흐리거나 번져보이는 효과를 많이 잡아줄 수 있다.

위와 같이 image-rendering을 사용하는데 우린 유독 흐림 효과가 심한 크롬에 적용할 목적이므로 -webkit-optimize-contrast를 적용한다.

 

2. translateZ

transform을 사용하여 이미지의 Z축을 0으로 초기화 시켜 입체감을 없앤다.

.banner{
  transform:translateZ(0);
}

 

3. backface-visibility

backface-visibility는 요소의 뒷면을 보여줄 것인지 보여주지 않을 것인지를 정하는 css이다.

.banner{
  backface-visibility:hidden;
}

위와 같이 이미지의 뒷면을 hidden처리하여 보이지 않게, 입체감이 없게 처리할 수 있다.

 

 

4. 정리

.banner{
  image-rendering:-webkit-optimize-contrast;
  transform:translateZ(0);
  backface-visibility:hidden;
}

해당 3가지 css를 사용하면 위 이미지 예시처럼 보다 뚜렷한 이미지를 얻을 수 있다.

 

물론 가능하다면 위 css를 사용하는 일 없이 영역에 맞는 사이즈의 이미지를 가져와서 사용한다면 더할나위 없이 좋을 것이다.

 

 


참조

https://intrepidgeeks.com/tutorial/chrome-image-blur

https://lpla.tistory.com/139

https://developer.mozilla.org/ko/docs/Web/CSS/backface-visibility

 

 

 

반응형
반응형

자바스크립트를 사용하면서 내가 모든 소스를 짜고 최적화와 모든 예외 상황들을 고려하여 작업을 할 수 있다면 좋겠지만 많은 리소스가 들고 또 내가 생각하지 못하는 부분들도 존재하기 때문에 간단하게 사용할 수 있는 플러그인을 많이 사용한다. 대표적인 플러그인이 슬라이더(slick, swiper 등...), 차트(chart.js, 구글차트) 등이 있다.

 

그리고 오늘 알아볼 AOS라는 플러그인도 스크롤에 따른 애니메이션 효과를 구현할 때 많이 쓰인다.

 

1. AOS 란? 

AOS는 Animate on scroll library의 약자로 스크롤을 이용한 애니메이션 효과를 구현하는 플러그인이다. 스크롤을 내리는 위치에 따라 내가 지정한 영역이 fade효과와 더불어 아래쪽에서 올라오거나 왼쪽에서 오른쪽으로 이동하거나 하는 효과를 구현할 수 있다.

AOS에서 구현하는 애니메이션 효과는 css3 transition과 transform을 사용하며 작동한다.

 

2. AOS 사용 방법

AOS를 사용하기 위해서는 aos.css 파일과 aos.js 파일이 필요하다.

https://michalsnik.github.io/aos/

aos 페이지에서 관련 내용을 CDN으로 가져올 수도 파일을 다운 받을 수도 있다.

<head>
  <link rel="stylesheet" href="https://unpkg.com/aos@2.3.1/dist/aos.css">
</head>
<body>
  <div class="wrap">
    <div class="section">
      <div data-aos="fade-up"></div>
    </div>
  </div>
  
  <script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
</body>

위와 같이 AOS css와 js 파일을 문서에 연결시킨 다음 바로 aos를 실행 시킬 수 있는 것은 아니다.

script 태그 안에 AOS의 초기화를 선언해줘야 비로소 AOS를 사용할 수 있다.

 

<head>
  <link rel="stylesheet" href="https://unpkg.com/aos@2.3.1/dist/aos.css">
</head>
<body>
  <div class="wrap">
    <div class="section">
      <div data-aos="fade-up"></div>
    </div>
  </div>
  
  <script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
  <script>
    AOS.init();  // 초기화 선언 필수
  </script>
</body>

위와 같이 aos.js 파일 뒤에 AOS.init()을 통해 AOS 초기화를 선언해줘야 aos효과를 적용할 수 있다.

 

3. AOS 사용 방법 (옵션 값)

AOS 페이지에 이미 너무 자세히 예시와 함께 나와있기 때문에 간단하게 많이 사용하는 옵션 값을 정리하자면

 

1. data-aos : aos의 기본 효과를 선언한다. (fade-up, fade-down, fade-right, fade-left, fade-up-right, zoom-in, flip-up...와 같은 방향, 효과 설정)

2. data-aos-duration : aos를 통한 애니메이션의 움직임 시간을 설정한다. (data-aos-duration이 길수록 애니메이션 효과가 느리가 나타난다.)

3. data-aos-delay :  특정 스크롤 위치에 도달했을 때 애니메이션 효과가 나타나야 하지만 스크롤 위치에 도달 후에 일정 시간동안의 지연을 주고 싶을 때 사용한다.

 

<head>
  <link rel="stylesheet" href="https://unpkg.com/aos@2.3.1/dist/aos.css">
</head>
<body>
  <div class="wrap">
    <div class="section">
      <div data-aos="fade-up"></div> <!-- fade-up 효과로 나타난다. -->
      <div data-aos="fade-down" data-aos-duration="1000"></div> <!-- fade-down 효과로 1초간의 시간동안 나타난다. -->
      <div data-aos="fade-left" data-aos-delay="2000"></div> <!-- fade-left 효과로 2초간의 시간 지연 후에 나타난다. -->
    </div>
  </div>
  
  <script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
</body>

 

 

 


참고

https://michalsnik.github.io/aos/

 

 

 

반응형
반응형

웹 페이지 개발을 하면서 유튜브 영상을 가져와서 페이지에서 뿌려주는 경우가 종종 있다.

그리고 영상을 직접 가져오진 않더라도 썸네일을 가져와서 영상 url과 연결을 시켜줄 때가 있다.

 

1. 유튜브 주소에 따른 썸네일 가져오기

유튜브 영상의 주소가 https://www.youtube.com/watch?v=YwC0m0XaD2E 해당 url일 경우에 썸네일을 가져오는 방법은

 

1-1. 유튜브 영상의 id값 알기

영상 url의 v= 이후에 나오는 텍스트가 영상의 고유 id 값인데 썸네일을 가져오려면 해당 id값을 알아야한다.

위 영상을 예로 들면 YwC0m0XaD2E 값이 영상의 id 값이다.

 

1-2. 썸네일 주소 형식에 id값 넣기

id값을 알았으면 유튜브 썸네일 주소 형식에 id값만 넣어주면 된다.

썸네일 주소는 https://img.youtube.com/vi/ + id값 + /0.jpg 이다.

위 영상을 예로 들면 https://img.youtube.com/vi/YwC0m0XaD2E/0.jpg url이 메인 썸네일을 가져오는 주소다.

썸네일 주소 뒤에 0~3.jpg의 숫자를 입력할 수 있는다. 0은 메인, 1~3은 영상 진행별 썸네일 (영상 캡쳐화면)을 가져올 수 있다.

 

2. 썸네일 상하 여백 잘라서 가져오기 (mqdefault, maxdefault)

1번과 같은 방법으로 썸네일을 가져와서 페이지에 뿌릴 경우 내가 원하는 비율이 나오지 않을 것 이다.

그건 0.jpg 형식으로 썸네일을 가져오게 되면 유튜브 자체에서 넣어주는 상하에 검은색 여백이 존재하기 때문이다.

이런 여백 자르기 위해 css로 강제로 이미지를 조정하는 방법도 있겠지만

애초에 0.jpg 형식이 아닌 mqdefault.jpg 형식으로 썸네일을 가져오면 상하에 여백 없이 내가 원하는 썸네일을 가져올 수 있다.

(https://img.youtube.com/vi/YwC0m0XaD2E/mqdefault.jpg)

 

그리고 mqdefault.jpg로 이미지를 가져왔는데 이미지가 너무 작아서 실제 페이지에 적용 했을 때 깨지거나 번져 보인다면 maxresdefault.jpg 형식으로 이미지를 가져오면 등록된 썸네일 최대 퀄리티와 크기로 이미지를 가져올 수 있다.

(https://img.youtube.com/vi/YwC0m0XaD2E/maxresdefault.jpg)

 

[!] maxresdefault.jpg 형식의 이미지 파일은 유튜브 영상에 따라 불러오지 못하는 경우가 있다.

 

 

3. 정리

- 영상 가져오기 (기본/상하여백 o)

: https://img.youtube.com/ + id + /0.jpg

(ex. https://img.youtube.com/vi/YwC0m0XaD2E/0.jpg)

 

- 영상 가져오기 (기본/상하여백 x)

: https://img.youtube.com/ + id + /default.jpg

(ex. https://img.youtube.com/vi/YwC0m0XaD2E/default.jpg

 

- 영상 가져오기 (상하 여백 없는 mqdefault)

: https://img.youtube.com/ + id + /mqdefault.jpg

(ex. https://img.youtube.com/vi/YwC0m0XaD2E/mqdefault.jpg

 

- 영상 가져오기 (상하 여백없는 default, 최대 크기)

: https://img.youtube.com/ + id + /maxresdefault.jpg

(ex. https://img.youtube.com/vi/YwC0m0XaD2E/maxresdefault.jpg)

 

 

 

반응형
반응형

pc에서는 문제가 없지만 모바일에서의 table 태그에 작성한 내용들이

내가 원하는 width값을 먹지 못하고 틀어지는 경우가 종종 있다.

 

<colgroup>과 <col>태그를 사용해서 width 값을 줘도,

css를 통해 width값을 아무리 줘도 내가 원하는 값을 출력하지 못하는 경우가 있는데

 

이 경우 caption 태그 사용에 대해 살펴봐야한다.

 

1. table태그 width값 적용 문제 (with. caption태그)

<style>
  table{table-layout:fixed; border-collapse:collapse; border-spacing:0;}
  table caption{position:absolute; width:1px; height:1px; overflow:hidden; font-size:0;}
  table thead th{background:#999; color:#fff;}
  table th, td{height:30px; text-align:center;}
</style>

<table>
  <caption>학생별 과목 평균점수</caption>
  <colgroup>
    <col style="width:40%">
    <col style="width:30%;">
    <col style="width:30%;">
  </colgroup>
  <thead>
    <tr>
      <th scope="col">이름</th>
      <th scope="col">과목</th>
      <th scope="col">평균점수</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>김철수</td>
      <td>영어</td>
      <td>80점</td>
    </tr>
    <tr>
      <td>김세미</td>
      <td>수학</td>
      <td>60점</td>
    </tr>
    <tr>
      <td>김티스토리</td>
      <td>체육</td>
      <td>40점</td>
    </tr>
    <tr>
      <td>철수세미</td>
      <td>음악</td>
      <td>100점</td>
    </tr>
  </tbody>
</table>

위와 같이 table을 만들었을 때 pc에서는 내가 원하는 모양대로 노출이 되지만

모바일 safari 혹은 chrome에서

위와 같은 모습으로 너비 값을 인식하지 못하고 틀어져 노출이 되는 경우가 있을 것이다.

위와 같은 현상이 생길 경우 가장 먼저 확인해야할 것이 caption 태그이다.

 

2. table 너비 인식 문제 해결 (caption: absolute)

caption태그는 테이블의 접근성 향상을 위해 해당 table이 어떤 내용들을 담고 있는지

요약하여 설명해주는 텍스트를 담고 있다. 그래서 caption을 쓰는 경우가 많은데

2021.01.27 - [Frontend/HTML] - [html 태그] caption 태그 (table 태그, summary 속성)

 

위 1번에서 사용한 css를 보면 caption태그를 html 구조상에만 남겨두고 실제 페이지 노출시 안보이게 하기 위해

position:absolute를 사용하여 숨기는 내용의 css를 사용했는데

 

caption 태그에 absolute가 들어가게 되면 모바일 safari, chrome 등에서 table의 너비 자체를 인식 못하는 버그가 있기 때문에 caption태그를 숨기기 위해서는 absolute를 사용해서 띄워서 숨기는 것이 아닌 다른 방식으로 caption태그를 숨겨줘야한다.

<style>
  table{table-layout:fixed; border-collapse:collapse; border-spacing:0;}
  table caption{font-size:0; text-indent:-9999px;}
  table thead th{background:#999; color:#fff;}
  table th, td{height:30px; text-align:center;}
</style>

caption에 해당하는 css를 font-size, text-indent를 사용해서 숨겨주는 방식을 사용해서 absolute를 사용하지 않는다면

내가 원하는 위와 같은 table의 너비 값을 얻을 수 있다.

 

 

 

 

반응형
반응형

일반적으로 버튼을 만들고 해당 버튼을 클릭하면

내부에 있는 내용이 노출될 수 있도록 작업을 하는 경우에

자바스크립트를 사용해서 관련 내용을 만든다.

 

하지만 html5에 들어서면서 이러한 내용들을 html만을 이용해서 만들 수 있게 되었다.

 

1. details 태그의 사용

<details> 태그는 html5에서 추가 된 태그로 

하위 요소로 <summary> 태그를 사용해서 <detail> 태그 안의 내용을 열기/닫기를 통해 보여준다.

<details>
  <summary>사내 공지사항</summary>
  <ul>
    <li>1. 공지사항 1번입니다.</li>
    <li>2. 공지사항 2번입니다.</li>
    <li>3. 공지사항 3번입니다.</li>
  </ul>
</details>

<details>
  <summary>사내 최고봉</summary>
  <div>사장님</div>
</details>

 

 

기본적인 사용 방법은 위와 같다.

<details>태그 안에 하나의 <summary>태그와 그 뒤에 추가적인 태그로 구성된다.

 

이 때, <summary> 태그 뒤에는 <ul>, <li>태그와 같이 리스트 형식으로 태그를 작성해도 되고

<span>이나 <div>를 나열해도 원하는 결과를 받을 수 있다.

(보여지는 내용이 리스트 형식이라면 리스트 태그 사용 추천)

 

2. details 태그의 활용

details 태그에 css로 스타일을 입혀서 좀 더 보기 좋게 만들 수 있다.

그리고 <summary> 태그 뒤에 오는 내용들에 <a>태그나 <button> 태그를 사용해서

버튼의 역할을 부여할 수도 있다.

<style>
  details{margin-bottom:10px;}
  details summary{padding:0 10px; background:#000; color:#fff; height:35px; line-height:35px; font-weight:bold; cursor:pointer;}
  details summary::marker{font-size:0;}
  details ul{border:1px solid #aaa;}
  details ul li a{display:block; padding:5px 10px;}
  details ul li a:hover{background:#333; color:#fff;}
  details .txt{border:1px solid #999; padding:5px 10px; text-align:center;}
</style>

<details>
  <summary>사내 공지사항</summary>
  <ul>
    <li><a href="#">1. 공지사항 1번입니다.</a></li>
    <li><a href="#">2. 공지사항 2번입니다.</a></li>
    <li><a href="#">3. 공지사항 3번입니다.</a></li>
  </ul>
</details>

<details>
  <summary>사내 최고봉</summary>
  <div class="txt">사장님</div>
</details>

 

 

위 예시와 같이 css는 내가 원하는대로 정의할 수 있다.

cursor:pointer를 사용해서 버튼과 같이 마우스 오버했을 때 손가락 모양으로 노출시킬 수도

:marker{font-size:0;}을 통해 <summary>태그 앞에 붙은 화살표 모양을 없앨 수도 있다.

 

3. details 태그 사용의 주의사항

<details> 태그는 인터넷 익스플로어에서는 사용할 수 없기 때문에 익스플로어를 고려한 작업시에는 사용하지 말아야한다.

인터넷 익스플로어의 사용이 줄고 있고 곧 서비스가 종료가 되지만 아직까지 지원하려고 하는 회사가 있기 때문에

사용시 주의가 필요하다.

 

 

 


참고

https://developer.mozilla.org/ko/docs/Web/HTML/Element/details

 

 

 

반응형
반응형

마우스를 오버해서 나타나는 드롭다운 메뉴를 만드는데에

html 구조와 css만을 이용해서 작업할 수 있다.

 

1. html 구조 만들기

단순히 css만 이용한다고해서 드롭다운 메뉴를 만들 수 있는 것은 아니다.

css의 :hover 가상클래스를 사용해서 드롭다운 메뉴를 노출시켜야 하기 때문에

:hover 가상클래스를 사용할 수 있는 구조로 만들어야 한다.

<ul class="menu">
  <li>
    <a href="#">MENU 1</a>
    <ul class="depth_1">
      <li><a href="#">MENU 1_1</a></li>
      <li><a href="#">MENU 1_2</a></li>
      <li><a href="#">MENU 1_3</a></li>
    </ul>
  </li>
  <li>
    <a href="#">MENU 2</a>
  </li>
  <li>
    <a href="#">MENU 3</a>
    <ul class="depth_1">
      <li><a href="#">MENU 3_1</a></li>
      <li><a href="#">MENU 3_2</a></li>
    </ul>
  </li>
</ul>

위와 같이 각 MENU 1 ~ MENU 3 영역과 그 내부에 있는 depth_1을 하나의 li태그에 넣어야

css로 제어할 수 있다.

 

2. css 사용

위 html 구조에서 설명한 바와 같이

:hover를 걸 요소와 노출을 시킬 드롭다운 영역을 부모 자식 요소로 배치를 해야

:hover 가상클래스를 걸 수 있다.

.menu:after{display:block; content:''; clear:both;}
.menu > li{position:relative; float:left; margin-right:5px;}
.menu > li > a{display:block; padding:0 15px; background:#333; height:40px; line-height:40px; color:#fff;}
.menu > li:hover .depth_1 {display:block;}
.menu .depth_1{display:none; position:absolute; left:0; right:0; text-align:center;}
.menu .depth_1 a{display:block; padding:5px; background:#666; color:#fff;}

css를 넣을 때 주의 할 점은 .menu > li > a:hover 처럼 a태그에 hover를 걸게 되면

마우스가 a태그를 벗어나면 바로 .depth_1 영역이 닫히기 때문에 li태그에 :hover를 걸어줘야한다.

 

3. 예시

 

 

 

반응형
반응형

브라우저를 통해 페이지를 탐색할 때 비장애인의 경우엔 내가 보고 싶은 컨텐츠를 마우스로 편하게 클릭하고 이용할 수 있다. 하지만 시각 장애인의 경우 tab키, 방향키 등을 사용한 스크린리더를 통해 페이지에서 원하는 정보를 얻을 수 있다. 그렇기 때문에 페이지의 양이 많다면 그리고 그 정보가 페이지 하단부에 있다면 정보를 얻기까지 수도 없이 많은 tab, 방향키 입력이 필요할 것이다. 이러한 접근성이 부족한 내용을 해결하기 위해 사용하는 것이 스킵 네비게이션이라는 방법이다.

 

1. 스킵 네비게이션(skip navigation)

스킵 네비게이션은 원래는 말 그대로 네비게이션 영역의 많은 링크들을 건너뛰고 바로 컨텐츠 정보를 탐색하기 위해 사용하는 용도를 가지고 있다.

<body>
  <div class="skip_nav">
    <a href="#content">컨텐츠 바로가기</a>
  </div>
  
  <header>
    <h1><a href="#">logo</a></h1>
    <nav>
      <ul>
        <li><a href="#">회사소개</a></li>
        <li>
          <a href="#">제품정보</a>
          <ul>
            <li><a href="#">스낵</a></li>
            <li><a href="#">음료</a></li>
            <li><a href="#">아이스크림</a></li>
            <li><a href="#">조미료</a></li>
          </ul>
        </li>
        <li><a href="#">채용</a></li>
        <li><a href="#">CONTACT</a></li>
      </ul>
    </nav>
  </header>
  
  <div id="content" class="wrap">
    <section id="mainService">...</section>
    <section id="mainLogin">...</section>
    <section id="mainShopping">...</section>
    <section id="mainShopping">...</section>
    <section id="mainNotice">...</section>
  </div>
</body>

위의 예시와 같은 구조를 가지고 있는 페이지들이 많이 있다.

위의 경우에 메인 컨텐츠를 탐색하기 위해선 nav 영역의 많은 링크들을 거쳐야 메인 컨텐츠들에 접근이 가능하다.

이러한 경우를 위해 스킵 네비게이션을 사용해 네비게이션 영역을 건너뛰고 메인 컨텐츠(#content) 영역으로 한번에 접근 가능하도록 작업할 수 있다.

 

2. 스킵 네비게이션의 활용

<body>
  <div class="skip_nav">
    <a href="#mainService">주요 서비스 바로가기</a>
    <a href="#mainMedia">미디어 정보 바로가기</a>
    <a href="#mainShopping">쇼핑 바로가기</a>
    <a href="#mainNotice">공지사항 바로가기</a>
    <a href="#mainLogin">로그인 바로가기</a>
  </div>
  
  <header>...</header>
  
  <div class="wrap">
    <section id="mainService">...</section>
    <section id="mainLogin">...</section>
    <section id="mainShopping">...</section>
    <section id="mainShopping">...</section>
    <section id="mainNotice">...</section>
  </div>
</body>

1번에서 설명한 예시와 달리 위의 방법은 각 주요 서비스 섹션을 스킵 네비게이션으로 연결하여 바로바로 접근할 수 있도록 작업하였다.

 

스킵 네비게이션은 위에서 설명한바와 같이 기본적으로 네비게이션을 스킵(건너뛰기)하기 위해서 사용을 한다.

그리고 과거에는 그 용도 외에 페이지 각 세부 영역별로 스킵 네비게이션과 연결하면 오히려 접근성을 저해하고 페이지 이용을 복잡하게 만든다고 추천하지 않았다. 

하지만 최근들어 많은 대규모 페이지나 플랫폼에서 스킵 네비게이션을 단순 네비게이션을 스킵하기 위한 용도가 아닌 원하는 서비스나 정보를 한번에 이용할 수 있게 만든 하나의 링크 묶음으로 사용하고 있다.

 

이 부분은 명확히 어떤게 옳고 그르다고 말하기엔 방법에 대한 고민이 필요해보인다.

 

3. 스킵 네비게이션 스타일

스킵 네비게이션은 비장애인 사용자들이 페이지를 이용할 때 화면에서 보이지 않게 작업해야한다. 키보드나 스크린리더로 페이지를 tab 하였을 때 스킵 네비게이션이 노출되고 사용될 수 있게 해야한다.

 

그렇기 때문에 페이지에 맨 처음 접근하게 되면 일반적인 상황에서는 스킵 네비게이션을 확인할 수 없다. 그리고 그러한 이유로 스킵 네비게이션에 따로 css 스타일을 입히지 않아도 된다고 생각할 수 있다.

 

하지만 보통의 상황에서는 스킵 네비게이션 링크에 tab이 되었을 경우 tab이 활성화 된 링크에 css 스타일 효과를 줘서 화면에 노출되게끔 처리하는 것이 많이들 사용하는 방법이고 좋은 방법이라 생각한다.

스킵 네비게이션 예시 (네이버 메인)

.skip_nav a{position:absolute; top:-30px; left:0; background:#000; height:30px; line-height:30px; color:#fff; font-size:12px; padding:0 6px;}
.skip_nav a:focus,
.skip_nav a:active{top:0;} <!-- 해당 a태그가 focus 혹은 active 되었을 경우 a태그가 화면에 노출되게 처리 -->

 

4. 스킵 네비게이션 사용시 주의사항

스킵 네비게이션을 사용할 때에 몇 가지 주의 사항이 있다.

 

1. 스킵 네비게이션의 위치는 가능하면 최대한 body태그 맨 처음으로 위치해야 한다.

: 이유는 스킵 네비게이션의 목적이 네비게이션 영역을 스킵하기 위해서 혹은 주요 서비스로 페이지 초입에서 바로 접근하기 위해 사용을 하기 때문에 네비게이션 영역 뒤에나 컨텐츠 영역 뒤에 위치하게 되면 스킵 네비게이션을 쓰는 목적이 사라지기 때문이다.

 

2. 스킵 네비게이션은 페이지 구석구석 여러번 사용하는 것이 아니라 처음에 한번만 사용해야 한다.

: 시각 장애인 입장에서 페이지를 구성한다고 했을 때 스킵 네비게이션이 페이지 이용하는 중간 중간에 위치해서 페이지 요소들을 넘나들며 여기저기 바로바로 이용할 수 있게 한다면 좋을 것 같다는 생각을 가끔했다. 하지만 스킵 네비게이션은 페이지 첫 부분에 딱 한번 사용하는 것이 적절하다.

오히려 과한 스킵 네비게이션의 사용은 페이지의 구조와 페이지 이용을 더 복잡하게 할 뿐이다.

 

 


참조

https://jangkunblog.com/wp/skip-navigation-is-not-a-quick-link/

 

 

반응형
반응형

마크업을 하고 페이지를 만드는 과정에서 스크립트를 이용해 만드는 이벤트 중 스크롤, 탭과 마찬가지로 많이 사용하는게 있다면 아마 팝업이 될 것이다.

특정 버튼이나 영역을 클릭하면 그 버튼에 해당하는 내용을 가진 팝업은 웹페이지 작업시 빼놓을 수 없는 이벤트이다.

 

1. 클릭 이벤트 발생 시키기

팝업을 노출시키기 위해선 특정 버튼이나 영역을 클릭해야한다.

이 때 .addEventListener 메소드를 사용한다.

<a href="#pop_info_1" class="btn_open">팝업 열기</a>
<a href="#pop_info_2" class="btn_open">팝업 열기2</a>

<!-- 팝업1 -->
<div id="pop_info_1" class="pop_wrap" style="display:none;">
  <div class="pop_inner">
    <p class="dsc">팝업 안내문구 입니다.</p>
    <button type="button" class="btn_close">닫기</button>
  </div>
</div>

<!-- 팝업2 -->
<div id="pop_info_2" class="pop_wrap" style="display:none;">
  <div class="pop_inner">
    <p class="dsc">팝업 안내문구 입니다222.</p>
    <button type="button" class="btn_close">닫기</button>
  </div>
</div>


<script>
  var target = document.querySelectorAll('.btn_open'); // 클릭할 버튼요소를 변수 처리

  // 팝업 열기
  for(var i = 0; i < target.length; i++){
    target[i].addEventListener('click', function(){
      targetID = this.getAttribute('href');
      document.querySelector(targetID).style.display = 'block';
    });
  }
</script>

위와 같이 target이라는 팝업을 열 버튼을 변수 처리한 후에

addEventListner로 click 이벤트를 걸어준다.

 

이 때, 클릭 이벤트는 .btn_open이 하나가 아니기 때문에

for 문을 돌려 .btn_open의 갯수에 맞춰 각각 버튼에 클릭 이벤트를 걸어준다.

 

2. 버튼 클릭 후 노출 될 팝업 영역 호출하기

버튼을 클릭하는 액션은 만들었다면

버튼 클릭 액션 동작 후 팝업 영역을 노출되게 만들어야한다.

<a href="#pop_info_1" class="btn_open">팝업 열기</a>
<a href="#pop_info_2" class="btn_open">팝업 열기2</a>

<!-- 팝업1 -->
<div id="pop_info_1" class="pop_wrap" style="display:none;">
  <div class="pop_inner">
    <p class="dsc">팝업 안내문구 입니다.</p>
    <button type="button" class="btn_close">닫기</button>
  </div>
</div>

<!-- 팝업2 -->
<div id="pop_info_2" class="pop_wrap" style="display:none;">
  <div class="pop_inner">
    <p class="dsc">팝업 안내문구 입니다222.</p>
    <button type="button" class="btn_close">닫기</button>
  </div>
</div>


<script>
  var target = document.querySelectorAll('.btn_open');
  var targetID;  // 버튼 클릭시 버튼에 해당하는 팝업의 id값 담는 변수

  // 팝업 열기
  for(var i = 0; i < target.length; i++){
    target[i].addEventListener('click', function(){
      targetID = this.getAttribute('href');
      document.querySelector(targetID).style.display = 'block';
    });
  }
</script>

팝업열기 버튼을 (btn_open)을 클릭 할 때

a 태그의 href의 값과 팝업의 id값을 일치시켜줘서 접근성으로도 해당 버튼과 팝업이 연결되도록 작업하고

추가로 여러 버튼과 팝업이 나오더라도 스크립트를 추가하지 않고

a태그의 href와 팝업의 id값만 맞춘다면 언제든지 수월하게 팝업을 만들 수 있게 위와 같이 작업하였다.

(현재 클릭한 a태그의 href를 가져오는 this.getAttribute('href');)

 

3. 팝업 닫기

팝업을 열었으면 닫기 버튼의 동작도 필요하다.

팝업을 열 때와 마찬가지로 페이지에 있는 .pop_wrap 내부에 있는 .btn_close 닫기 버튼을

for문을 돌려 각각 클릭 이벤트를 걸어준 뒤

해당 버튼의 부모 노드(pop_wrap)을 찾아서 해당 div를 닫아준다.

<a href="#pop_info_1" class="btn_open">팝업 열기</a>
<a href="#pop_info_2" class="btn_open">팝업 열기2</a>

<!-- 팝업1 -->
<div id="pop_info_1" class="pop_wrap" style="display:none;">
  <div class="pop_inner">
    <p class="dsc">팝업 안내문구 입니다.</p>
    <button type="button" class="btn_close">닫기</button>
  </div>
</div>

<!-- 팝업2 -->
<div id="pop_info_2" class="pop_wrap" style="display:none;">
  <div class="pop_inner">
    <p class="dsc">팝업 안내문구 입니다222.</p>
    <button type="button" class="btn_close">닫기</button>
  </div>
</div>


<script>
  var target = document.querySelectorAll('.btn_open');
  var targetID;

  // 팝업 열기
  for(var i = 0; i < target.length; i++){
    target[i].addEventListener('click', function(){
      targetID = this.getAttribute('href');
      document.querySelector(targetID).style.display = 'block';
    });
  }
  
  // 팝업 닫기
  for(var j = 0; j < target.length; j++){
    btnPopClose[j].addEventListener('click', function(){
      this.parentNode.parentNode.style.display = 'none';
    });
  }
</script>

 

 

4. 예시 화면

버튼이 여러개, 팝업이 여러개인 예시를 통해서

왜 위와 같이 a태그의 href 속성을 통한 팝업을 작업하는지 예시를 들어보려한다.

 

 

 

 

반응형
반응형

아이폰X로 들어서면서 아이폰에는 노치라는 영역이 생겼다.

표시된 부분이 노치 영역이다.

1. 노치 영역의 문제

노치라 불리는 이 영역은 작업을 진행할 때 많은 에로사항을 준다.

웹뷰에서는 문제없이 노출되던 영역들도 노치 영역 때문에 가려지거나 짤려 보이는 현상이 

아이폰X 이후로 나타나게 되었다.

 

예를 들어 가로모드로 화면을 봤을 때 오른쪽에 붙어서 노출되는 네비게이션을

만들었다는 가정을 하면 내가 의도했던 것과는 다르게

네비게이션 영역이 노치에 가려지는 현상을 보게 될 것이다.

위와 같이 네비 메뉴가 노치에 가려지는 현상이 생기기 때문에

우리는 이 방법을 해결하기 위한 내용을 알아봐야한다.

 

2. 노치에 가려지는 문제 해결

* html

먼저 노치 문제를 해결하기 위해서는 html 소스에서 viewport의 content 속성에

viewport-fit=cover 를 넣어줘야한다. (viewport-fit의 기본값은 auto이다.)

<meta name="viewport" content="viewport-fit=cover">

 

* css

html 소스에 viewport를 적용해주고 css에 env() 속성을 적용해주면 된다.

/* iOS 11.0 버전 */
constant(safe-area-inset-top)
constant(safe-area-inset-right)
constant(safe-area-inset-bottom)
constant(safe-area-inset-left)

/* iOS 11.2 이상 */
env(safe-area-inset-top)
env(safe-area-inset-right)
env(safe-area-inset-bottom)
env(safe-area-inset-left)

위와 같이 ios의 버전에 따라 입력되는 속성이 다른데

ios 11.0 버전에서는 constant() 속성을.

ios 11.2 버전 이상부터는 constant() 속성이 없어지고 env() 으로 대체되었기 때문에 env()로 사용한다.

 

ios 11.0과 11.2 버전 이상 모두를 만족하려면 두 속성을 다 적용하면 되겠다.

 

사용의 예

 

env() 미적용

.nav{right:0;}

 

 

env() 적용

.nav{right:0; right:constant(safe-area-inset-right); right:env(safe-area-inset-right);}

위쪽 env() 미적용 예시와 같이 단순 right:0만 입력하게 되면

노치가 있는 아이폰의 경우 해당 영역이 노치에 가려지게 된다.

 

하지만 env() 속성을 적용하게 되면

예시와 같이 노치 영역을 제외하고 right:0을 계산하기 때문에 노치에 가려지는 현상은 없게 된다.

 

 

 


참고

https://wit.nts-corp.com/2019/10/24/5731

https://velog.io/@devcmkim/IOS-%EB%85%B8%EC%B9%98%EB%8C%80%EC%9D%91-Css

 

 

 

 

반응형
반응형

페이지를 만들 때 탭 메뉴만큼 많이 사용하는게

스크롤과 관련된 스크립트이다.

 

특히 스크롤과 함께 특정 영역을 고정하는 식의 작업이 많이 있다.

 

1. 고정을 시작 할 영역의 좌표 값 구하기

스크롤을 통한 영역 고정을 하려면

우선, 스크롤이 어느 정도에 위치했을 때 고정할 영역을 구할 지에 대한 생각을 해야한다.

<style>
  *{margin:0; padding:0;}
  .wrap{width:1100px; margin:0 auto;}
  .header{height:200px; background:palegreen;}
  .content{position:relative;}
  .content:after{display:block; clear:both; content:'';}
  .main{float:left; margin-right:10px; background:skyblue; width:790px; height:1500px;}
  .wing{overflow:hidden;}
  .wing .inner{width:300px; height:300px; background:plum;}
  /* 스크롤시 고정 될 윙배너 css */
  .wing.fixed .inner{position:fixed; top:0;} 
</style>

<div class="wrap">
  <div class="header">Header</div>
  
  <div class="content">
    <div class="main">Main Area</div>

    <div class="wing">
      <div class="inner">Wing Banner</div>
    </div>
  </div>
</div>

위와 같은 구조로

header와 content 영역이 분리 되어 있고

content 영역 안에 스크롤시 고정 될 윙배너 영역이 있다고 가정할 때

 

표시해놓은 스크롤 기준선의 좌표값을 구해서

그 값을 윈도우 스크롤이 넘어갈 경우에 윙배너에 클래스를 추가해서 fixed 효과를 구현할 수 있다.

const content = document.querySelector('.content');

// 컨텐츠 영역부터 브라우저 최상단까지의 길이 구하기
const contentTop = content.getBoundingClientRect().top + window.scrollY;

main영역과 wing 영역을 품고 있는 content라는 변수를 선언해준 후

해당 영역의 좌표 값을 getBoundingClientRect().top 와 window.scrollY를 더해줘서 구한다.

 

*getBoundingClientRect().top는 뷰포트 상의 좌표값을 나타내고

window.scrollY는 브라우저 최상단에서 부터 현재까지 스크롤 된 좌표 값을 구한다.

 

2. 스크롤이 지정해놓은 좌표 값을 넘어갈 때 클래스 주기 (window.scrollY >= 좌표 값)

위 1번에서 구한 content의 좌표 값을 윈도우 스크롤이 넘어갈 경우

.wing에 클래스를 추가해줘서 fixed 효과를 구현할 수 있다.

const content = document.querySelector('.content');
const wing = document.querySelector('.wing');

// 컨텐츠 영역부터 브라우저 최상단까지의 길이 구하기
const contentTop = content.getBoundingClientRect().top + window.scrollY;

window.addEventListener('scroll', function(){
  if(window.scrollY >= contentTop){
    wing.classList.add('fixed');
  }else{
    wing.classList.remove('fixed');
  }
});

wing이라는 변수를 선언해줘서 클래스를 추가할 영역을 미리 담아둔다.

 

그리고 window에 addEventListener로 'scroll' 이벤트를 걸어준 후

if문을 사용해 1번에서 구한 content 영역의 위치값을 윈도우 스크롤이 넘길 경우에

미리 구해놓은 wing에 fixed 클래스를 추가해준다.

 wing.classList.add('fixed') 

 

 

 

(*CodePen 화면은 0.25x 로 축소해서 확인하시면 수월하게 예시화면을 확인할 수 있습니다.)

 

 

반응형
반응형

처음 마크업을 시작하고 어느정도 내가 원하는 화면을 디자인과 같이 구현할 수 있게 되었을 때부터,

단순한 코드 작성에 의미를 두는 것이 아닌 효율적인 코딩을 하기위해 노력하기 시작했다.

 

단순히 효율적인 레이아웃을 짜고 반복되는 css를 최소화 하는 등의 운용상의 효율이 아닌

내가 만든 마크업 페이지를 장애인, 비장애인 모두가 보다 쉽게 사용할 수 있도록 접근성을 고려한 코딩을 하는 것에 대한 고려가 필요하단 생각을 했다.

 

오늘은 웹 접근성을 고려하여 마크업을 하기 위한 방법 중 인식의 용이성이라는 주제 중 대체 텍스트에 대한 내용으로 포스팅을 하려한다.

(포스팅 내용은 한국형웹콘텐츠접근성지침2.1을 기준으로 작성하였다.)

 

1. 인식의 용이성

인식의 용이성은 사용자의 장애 유무를 떠나 모든 사용자가 페이지에 노출되는 컨턴체들을 동등하게 이용할 수 있게 작업 하는 것을 말한다.

 

인식의 용이성에는 크게 3개의 지침이 있다.

 

- 대체 텍스트

- 멀티미디어 대체 수단

- 명료성

 

2. 대체 텍스트 특징

비장애인의 경우 일반적인 웹 서핑을 할 때,

페이지에 있는 이미지를 한 눈에 알아보고 어떤 이미지인지, 뭘 나타내는 지를 바로 파악할 수 있지만

장애를 가진 이용자의 경우 '스크린 리더' 라는 프로그램을 이용해서 이미지에 대한 설명을 귀로 듣게 된다.

 

그렇기 때문에 이미지의 용도나 성격을 설명할 수 있는 대체 텍스트를 작업자가 따로 넣지 않는다면

장애를 가진 사용자들은 그 이미지가 어떤 이미지인지 전혀 알 방법이 없다.

때문에 이미지가 정보의 주를 이루는 페이지의 경우는 대부분의 정보를 습득할 수 없게된다.

 

대체 텍스트를 작성할 때 유의해야할 점은 다음과 같다.

 

1. 대체 텍스트는 간단하고 명료하게 제공되어야 한다.

 

2. 이미지를 클릭하면 링크가 이동하거나 이미지를 이용한 버튼의 경우 링크이동, 클릭 이벤트 등의 용도가 분명하기 때문에 해당 이미지를 클릭하면 어떤 기능이 나타나는지에 대한 설명을 대체 텍스트로 제공 해야한다.

<!-- QR코드 이미지 링크 -->
<a href="QRCode.com">
  <img src="qr.jpg" alt="QRcode.com으로 이동하는 QR코드">
</a>

<!-- 더보기 버튼 -->
<button type="button>
  <img src="more.png" alt="쇼핑 아이템 더보기">
</button>

위의 예시와 같이 QR코드 이미지를 클릭하면 QR코드를 인증할 수 있는 페이지로 이동한다고 했을 때

해당 이미지가 어떤 페이지로 이동이 되는지에 대해 alt="" 대체 텍스트를 통해 설명하고 있다.

 

그리고 일반적인 이벤트 버튼의 경우에도

단순히 이미지 설명을 '더보기'에서 끝내는 것이 아닌 해당 이미지 버튼을 누르면 어떤 영역이 더보기가 되는지에 대해 명확하게 표기할 필요가 있다.

 

3. 의미있는 배경 이미지의 경우 이미지에 대한 정보를 제공해야한다.

출처: 서울시청

위와 같이 배경 이미지 안에 텍스트가 들어가 있는 경우

<img src="banner.jpg" alt="코로나19 나의 백신 예방접종 장소는?">

img태그의 alt 속성에 이미지에 들어가 있는 텍스트 정보를 명시해줘야한다.

 

4. 데이터 차트와 같이 내용이 복잡한 이미지나 컨텐츠의 경우에 사용자가 해당 내용을 파악할 수 있도록 충분한 대체 텍스트가 제공 되어야한다.

출처: 코로나바이러스감염증-19

위와 같은 그래프 이지미가 있다고 했을 때

<img src="graph.png" alt="코로나 확진환자 지역별 비율 그래프 서울 31.2%, 기타 29.3%, 경기 27.9%, 대구 7.1%, 인천 4.5%">

alt 속성 값에 차트와 관련된 데이터 내용을 명시해줘야한다.

 

 

3. 대체 텍스트를 제공하지 않거나 제한적으로 제공하는 경우

접근성을 위해 대체 텍스트의 사용은 필수이다.

하지만 무분별한 대체 텍스트의 사용은 페이지 이용을 어지럽게 만들 수 있기 때문에

대체 텍스트를 제공하지 않아도 되는 경우나 아주 제한적으로만 간략하게 제공해도 되는 경우에 대해서도

알아둘 필요가 있다.

 

1. 수화 동영상과 같이 이미 해당 컨텐츠의 역할이 대체 콘텐츠의 역할을 하는 경우 별도의 텍스트를 제공해줄 필요가 없다.

 

2. 생방송과 같이 지속적으로 예측할 수 없는 내용의 변화가 있는 컨텐츠라면 하나하나 대체 텍스트를 제공하기 어렵기 때문에 간략하게 어떤 주제를 가진 방송인지 정도의 용도를 설명해주면 된다.

또한 색맹검사, 청각검사, 시력검사, 받아쓰기 등 검사 및 시험의 경우도 간략한 대체 텍스트로 용도를 설명해주면 된다.

 

3. 악기의 연주나 미술품 등의 시각적 예술 작품의 경우에도 해당 콘텐츠의 주제나 간단한 용도에 대한 대체 텍스트만 제공한다.

 

4. 특별한 의미가 없는 단순 이미지의 경우 대체 텍스트를 제공하지 않는다.

 

 

 

 


참고

https://www.wah.or.kr:444/Participation/%ED%95%9C%EA%B5%AD%ED%98%95%EC%9B%B9%EC%BD%98%ED%85%90%EC%B8%A0%EC%A0%91%EA%B7%BC%EC%84%B1%EC%A7%80%EC%B9%A82.1.pdf

 

https://accessibility.naver.com/acc/guide_01

 

 

 

 

반응형
반응형

Sass 문법에서 많이 사용하진 않지만 

알고 있으면 유용하게 사용할 상황이 생기지 않을까하여 포스팅하게 되었다.

unique-id 는 말 그대로 고유한 id값을 생성해서 Sass 문법을 작성할 수 있게 도와준다.

 

1. unique-id() 란

예시를 보면 이해할 수 있다.

/* SCSS */
$id: unique-id();

.#{$id}{color:red}


/* CSS Compiled */
.u4a1gxy {
  color: red;
}

$id라는 변수에 unique-id() 를 사용하게 되면

임의의 문자열이 정의된다.

css로 컴파일 된 내용을 봤을때도 .u4a1gxy라는 임의의 값이 출력 된 것을 볼 수 있다.

물론 새로 저장해서 컴파일을 다시 하게되면 해달 값은 또 바뀐다.

 

2. unique-id() 응용

위의 예시와 같이 클래스를 unique-id를 통해 사용하게 된다면

html 구조상 동일한 이름의 클래스가 없으면 소용이 없게된다.

 

그렇다면 어떤 상황에서 사용하면 좋을지에 대해서 생각해보니

css 문법안에서 정의하고 그 정의한 내용을 css 문법 안에서 사용할 수 있는 경우에

사용하면 유용할 것이라 생각했다.

 

대표적으로 animation 속성이 있다.

animation은 특정 name을 정의하고

그 name값에 해당하는 내용을 keyframes로 애니메이션 효과를 적용하기 때문에

css 문법 내에 정의하는 animation-name과 keyframes를 unique-id()로 사용할 수 있겠다.

 

이때, 주의해야할 점은 unique-id()를 사용해서 임의의 변수를 만들어

그 변수를 통해 animation-name과 keyframes 값을 적용시켜야 할 것이다.

/* SCSS */
.box{
  $name: unique-id();

  animation-name: $name;
  animation-duration: 0.5s;
  animation-iteration-count: infinite;

  @keyframes #{$name} {
    from { transform:scale(1.5); }
    to { transform:scale(1); }
  }
}


/* CSS Compiled */
.box {
  animation-name: u4a1h1g;
  animation-duration: 0.5s;
  animation-iteration-count: infinite;
}
@keyframes u4a1h1g {
  from {
    transform: scale(1.5);
  }
  to {
    transform: scale(1);
  }
}

위의 예시와 같이 $name이라는 변수에 unique-id()를 담고

해당 $name 변수를 animation-name과 @keyframes 의 이름 값을 일치시켜 준다.

 

3. unique-id() 사용시 주의사항

위의 예제들과 같이 사용하게 되면 특별한 문제는 없겠지만

2번에서 설명한 것 처럼 하나의 $name과 같은 변수에 unique-id()를 담아서 사용하는 것이 아니라

각각 unique-id()를 적용하면 내가 원하는 값을 받아올 수 없을 것이다.

/* SCSS */
.box{
  animation-name: unique-id();
  animation-duration: 0.5s;
  animation-iteration-count: infinite;

  @keyframes #{unique-id()} {
    from { transform:scale(1.5); }
    to { transform:scale(1); }
  }
}


/* CSS Compiled */
.box {
  animation-name: u4a1h6k;
  animation-duration: 0.5s;
  animation-iteration-count: infinite;
}
@keyframes u4a1h6l {
  from {
    transform: scale(1.5);
  }
  to {
    transform: scale(1);
  }
}

위의 소스를 보면 확인할 수 있다.

 

unique-id()를 animation-name, @keyframes에 각각 적용을 하게 되면

위와 같이 비슷한듯 다른 unique-id값이 출력이 되기 때문에

애니메이션 효과 자체를 구현할 수 없게된다.

 

 

 


참고

sass-lang.com/documentation/interpolation

 

 

 

반응형
반응형

자바스크립트를 이용해서 작업을 하게 될 경우에

html 요소의 높이나 너비를 구해야 할 경우가 많이 있다.

 

높이나 너비를 구해야할 때 고려해야할 점이 있다면

내가 구하는 높, 너비가 padding이나 border의 값이 포함된 값인지 아닌지를 고려해서

아래의 프로퍼티들을 구분하여 사용할 수 있다.

 

1. offsetWidth, offsetHeight

높이와 너비를 구할 때 가장 많이 쓰이는 속성으로

padding, border 값을 포함한 컨텐츠의 높이를 가져온다. (margin은 포함하지 않는다.)

<style>
  .box1{
    padding:10px; 
    border:5px solid red; 
    width:250px; 
    height:300px; 
    background:skyblue; 
    overflow:auto; 
    color:#fff;
  }
</style>

<div class="box1">
  <p>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.
  </p>
</div>

<script>
  const box = document.querySelector('.box1');

  console.log(box.offsetWidth, box.offsetHeight);
  // 280 330 출력
</script>

위의 예시와 같이 border padding값과 내부 컨텐츠 높, 너비를 포함한 값이 콘솔창에 출력된다.

 

 

2. clientWidth, clientHeight

1번에서 소개한 offsetWidth, offsetHeight와 다르게 border 값은 포함하지 않는다.

즉, 컨텐츠 높, 너비과 padding 값만 적용해서 값을 계산한다.

<style>
  .box1{
    padding:10px; 
    border:5px solid red; 
    width:250px; 
    height:300px; 
    background:skyblue; 
    overflow:auto; 
    color:#fff;
  }
</style>

<div class="box1">
  <p>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.
  </p>
</div>

<script>
  const box = document.querySelector('.box1');

  console.log(box.clientWidth, box.clientHeight);
  // 253 320 출력
</script>

높이의 경우 offsetHeight값에서 border 위 아래 5px씩을 뺀 320이 출력이 되었지만

너비의 경우 border 5px씩 즉, 10px이 빠진 270px이 아닌 253이 출력되었다.

 

이유는 가로 영역에 있는 스크롤의 크기를

clientWidth 값이 계산하지 않았기 때문이다.

 

스크롤이 생기면 해당 스크롤의 크기를 뺀 나머지 영역이 너비나 높이 값이 되기 때문에

내가 .box1에 적용시킨 width:250px 값은 실제론 스크롤의 크기를 제외한 233px만 값이 적용이 된 것이다.

 

 

3. scrollWidth, scrollHeight

컨텐츠 안에 숨겨진 스크롤 영역까지 계산한다.

그리고 padding값을 포함한다.

 

예시를 보면 이해할 수 있다.

<style>
  .box1{
    padding:10px; 
    border:5px solid red; 
    width:250px; 
    height:300px; 
    background:skyblue; 
    overflow:auto; 
    color:#fff;
  }
</style>

<div class="box1">
  <p>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.<br>
    테스트용 텍스트입니다.
  </p>
</div>

<script>
  const box = document.querySelector('.box1');

  console.log(box.scrollWidth, box.scrollHeight);
  // 253 360 출력
</script>

scrollWidth값은 clientWidth 값과 동일하게 스크롤바와 border를 제외한 너비값이 들어갔고

scrollHeight의 경우 .box1 안에 있는 p태그의 총 영역까지 계산하여

360이 출력되었다.

 

 

위의 예시들 처럼

내가 단순 영역에 border를 포함하고 싶다면 offsetWidth, offsetHeight,

단순 영역에 border를 포함하지 않고 스크롤바의 크기도 고려할 필요가 없을 때clientWidth, clientHeight,

영역 내부의 컨텐츠 크기까지 (총 스크롤 크기) 고려해야 한다면 scrollWidth, scrollHeight 값을 사용하면 될 것이다.

 

 

 

 

반응형
반응형

작업을 하면서 가장 많이 구현하게 되는 기능 중 하나인 탭메뉴를 바닐라 스크립트를 사용하여 구현하려한다.

구현은 크게 2가지 방법으로 할 것이다.

 

1. 탭 버튼과 탭 컨텐츠가 한 영역으로 묶여 있는 경우

<style>
  .tab_menu{position:relative;}
  .tab_menu .list{overflow:hidden;}
  .tab_menu .list li{float:left;}
  .tab_menu .list .btn{font-size:13px; margin-right:14px;}
  .tab_menu .list .cont{display:none; position:absolute; background:#555; color:#fff; text-align:center; width:250px; height:100px; line-height:100px;}
  .tab_menu .list li.is_on .btn{font-weight:bold; color:green;}
  .tab_menu .list li.is_on .cont{display:block;}
</style>

<div class="tab_menu">
  <ul class="list">
    <li class="is_on">
      <a href="#tab1" class="btn">Tab Button1</a>
      <div id="tab1" class="cont">Tab Content1</div>
    </li>
    <li>
      <a href="#tab2" class="btn">Tab Button2</a>
      <div id="tab2" class="cont">Tab Content2</div>
    </li>
    <li>
      <a href="#tab3" class="btn">Tab Button3</a>
      <div id="tab3" class="cont">Tab Content3</div>
    </li>
  </ul>
</div>

위와 같이 .btn과 .cont가 하나의 li 태그로 묶여서 li태그를 제어하면

버튼과 컨텐츠 영역을 모두 제어할 수 있게 마크업을 하는 방법에선

특별하게 많은 스크립트가 필요하지 않다.

 

이 방법은 스크립트를 간소화 할 수 있는 장점과 동시에

마크업 구조도 접근성을 높일 수 있는 구조로 작성을 했기 때문에 좀 더 괜찮은 소스라 생각한다.

(!)
키보드 접근성을 고려하면 위의 소스처럼 버튼 영역 다음에 바로 해당 버튼과 연관 된 컨텐츠가 잡히기 때문에
접근성 측면에서 위와 같은 구조로 탭 메뉴를 작성하는 것을 추천한다.

 

<style>
  .tab_menu{position:relative;}
  .tab_menu .list{overflow:hidden;}
  .tab_menu .list li{float:left; margin-right:14px;}
  .tab_menu .list .btn{font-size:13px;}
  .tab_menu .list .cont{display:none; position:absolute; background:#555; color:#fff; text-align:center; width:250px; height:100px; line-height:100px;}
  .tab_menu .list li.is_on .btn{font-weight:bold; color:green;}
  .tab_menu .list li.is_on .cont{display:block;}
</style>

<div class="tab_menu">
  <ul class="list">
    <li class="is_on">
      <a href="#tab1" class="btn">Tab Button1</a>
      <div id="tab1" class="cont">Tab Content1</div>
    </li>
    <li>
      <a href="#tab2" class="btn">Tab Button2</a>
      <div id="tab2" class="cont">Tab Content2</div>
    </li>
    <li>
      <a href="#tab3" class="btn">Tab Button3</a>
      <div id="tab3" class="cont">Tab Content3</div>
    </li>
  </ul>
</div>

<script>
  const tabList = document.querySelectorAll('.tab_menu .list li');
  
  for(var i = 0; i < tabList.length; i++){
    tabList[i].querySelector('.btn').addEventListener('click', function(e){
      e.preventDefault();
      for(var j = 0; j < tabList.length; j++){
        tabList[j].classList.remove('is_on');
      }
      this.parentNode.classList.add('is_on');
    });
  }
</script>

위와 같이 스크립트를 작성하면 간단하게 탭을 구현할 수 있다.

 

 

1. .tab_menu .list의 li안에 있는 각 버튼에 addEventListener로 'click' 이벤트를 걸어준다. (for문을 통해)

2. 해당 이벤트에 preventDefault()로 a태그 클릭시 href로 인한 화면 이동을 막아준다.

3. 버튼을 클릭시 해당 버튼의 부모요소(li태그)에 is_on 클래스를 준다.

4. 3번 단계 전에 버튼 클릭했을 때 다른 부모요소(다른 li태그들)의 is_on 클래스를 해제시켜준다.

 

* 기존에 css로 .is_on 클래스에 의해 컨텐츠의 display가 none/block 되도록 설정해놓았기 때문에 is_on 클래스로 탭을 구현한다.

 

*버튼 (a태그)마다 href를 통해 해당 탭에 해당하는 컨텐츠의 id값과 연결시켜준 이유는
해당 버튼과 컨텐츠의 접근성을 위해 연결해놓은 것이다. (버튼과 컨텐츠를 연결)

 

 

2. 탭 버튼과 탭 컨텐츠가 다른 영역에 위치하고 있을 때

<style>
  .tab_menu{position:relative;}
  .tab_menu .list{overflow:hidden;}
  .tab_menu .list li{float:left; margin-right:14px;}
  .tab_menu .list li.is_on .btn{font-weight:bold; color:green;}
  .tab_menu .list .btn{font-size:13px;}
  .tab_menu .cont_area .cont{position:absolute; top:25px; left:0; background:#555; color:#fff; text-align:center; width:250px; height:100px; line-height:100px;}
</style>

<div class="tab_menu">
  <ul class="list">
    <li class="is_on">
      <a href="#tab1" class="btn">Tab Button1</a>
    </li>
    <li>
      <a href="#tab2" class="btn">Tab Button2</a>
    </li>
    <li>
      <a href="#tab3" class="btn">Tab Button3</a>
    </li>
  </ul>
  
  <div class="cont_area">
    <div id="tab1" class="cont">
      Tab Content1
    </div>
    <div id="tab2" class="cont">
      Tab Content2
    </div>
    <div id="tab3" class="cont">
      Tab Content3
    </div>
  </div>
</div>

작업을 하다보면 1번에서 소개한 바와 같이 버튼과 컨텐츠를 한 군데 묶기 힘든 경우도 많이 생긴다.

위의 소스와 같이 탭과 컨텐츠가 마크업상 분리 되어있을 때 사용할 수 있는 탭 구현 방법을 소개한다.

 

여기서 중요한 점은 1번과 마찬가지로 버튼(a태그)의 href 속성과
각 탭에 해당하는 컨텐츠 영역의 id값을 일치 시켜서 접근성을 높여주는 것이다.

 

<style>
  .tab_menu{position:relative;}
  .tab_menu .list{overflow:hidden;}
  .tab_menu .list li{float:left; margin-right:14px;}
  .tab_menu .list li.is_on .btn{font-weight:bold; color:green;}
  .tab_menu .list .btn{font-size:13px;}
  .tab_menu .cont_area{margin-top:10px;}
  .tab_menu .cont_area .cont{display:none; background:#555; color:#fff; text-align:center; width:250px; height:100px; line-height:100px;}
</style>

<div class="tab_menu">
  <ul class="list">
    <li class="is_on">
      <a href="#tab1" class="btn">Tab Button1</a>
    </li>
    <li>
      <a href="#tab2" class="btn">Tab Button2</a>
    </li>
    <li>
      <a href="#tab3" class="btn">Tab Button3</a>
    </li>
  </ul>
  
  <div class="cont_area">
    <div id="tab1" class="cont">
      Tab Content1
    </div>
    <div id="tab2" class="cont">
      Tab Content2
    </div>
    <div id="tab3" class="cont">
      Tab Content3
    </div>
  </div>
</div>

<script>
  const tabList = document.querySelectorAll('.tab_menu .list li');
  const contents = document.querySelectorAll('.tab_menu .cont_area .cont')
  let activeCont = ''; // 현재 활성화 된 컨텐츠 (기본:#tab1 활성화)

  for(var i = 0; i < tabList.length; i++){
    tabList[i].querySelector('.btn').addEventListener('click', function(e){
      e.preventDefault();
      for(var j = 0; j < tabList.length; j++){
        // 나머지 버튼 클래스 제거
        tabList[j].classList.remove('is_on');

        // 나머지 컨텐츠 display:none 처리
        contents[j].style.display = 'none';
      }

      // 버튼 관련 이벤트
      this.parentNode.classList.add('is_on');

      // 버튼 클릭시 컨텐츠 전환
      activeCont = this.getAttribute('href');
      document.querySelector(activeCont).style.display = 'block';
    });
  }
</script>

위와 같이 작성할 경우 아래의 화면처럼

 

 

동일한 탭 효과를 구현할 수 있다.

 

1번과 다르게 탭 버튼과 탭 컨텐츠가 따로 떨어져 있더라도

위의 소스의 activeCont 처럼 현재 활성화 할 탭 컨텐츠를 변수로 받아서

탭 버튼을 클릭할 때마다 a태그의 href 값을 받아와 해당하는 id값을 가진 컨텐츠를 보여준다.

 

 

반응형
반응형

Sass 문법에서 placeholder 선택자는 일반적인 클래스와 같이 쓰이지만

일반적인 클래스와 다르게 단독으로 사용하면 css에 컴파일이 되지 않고

%(퍼센트) 문자와 함께 사용이 된다.

 

1. placeholder 선택자 특징

- % 문자와 함께 사용

- 단독으로 사용하면 css로 컴파일되지 않는다.

- 함수나 변수를 사용하듯 내가 원하는 요소에 불러와서 사용할 수 있다.

- 만들어 놓은 placeholder 선택자는 @extend를 통해 호출한다.

 

 

앞서 설명했 듯 placeholder 선택자는 사용 자체는 일반적인 클래스나 아이디와 같은 선택자를 사용하듯 사용한다.

그리고 (% + 선택자명) 으로 사용한다.

/* SCSS */
.box, %box-style{
  border:1px solid #333;
  background:#fff;
}


/* CSS */
.box{
  border:1px solid #333;
  background:#fff;
}

위의 예시를 보면 .box와 %box-style에 border, background css를 적용했지만

컴파일 된 내용을 본다면 .box에만 css가 적용된 것을 볼 수 있다.

 

일종의 변수나 함수의 개념으로 보면 된다.

 

%box-style에 스타일을 적용해놓고 그것을 호출할때만 사용을 할 수 있다.

호출할 때는 @extend를 통해 호출한다.

/* SCSS */
.box, %box-style{
  border:1px solid #333;
  background:#fff;
}

.box2{
  @extend %box-style;
}


/* CSS */
.box, .box2{
  border:1px solid #333;
  background:#fff;
}

 

2. placeholder 선택자 응용

위의 특징을 바탕으로 반복되는 공통된 css 스타일을 %placeholder 선택자로 만들어 놓고

원하는 곳에 손쉽게 사용할 수 있다.

/* SCSS */
%common-btn{
  border:1px solid #333;
  padding:5px 10px;
  
  &:hover{
    border-color:red;
  }
}

.button1{
  @extend %common-btn;
  color:#555;
}

.button2{
  @extend %common-btn;
  color:#999;
}


/* CSS */
.button1, button2{
  border:1px solid #333;
  padding:5px 10px;
}

.button1:hover, button2:hover{
  border-color:red;
}

.button1{
  color:#555;
}

.button2{
  color:#999;
}

위 예시와 같이 .button1과 .button2에 반복되는 스타일인 border, padding, hover을 %common-btn으로 만들고

각 .button1, .button2에 @extend로 호출해주면 반복되는 내용을 여러번 입력하지 않아도 되고

공통되는 스타일이 수정이 된다고하면 유지보수하기도 원활할 것이다.

 

 

 

반응형
반응형

2020.03.31 - [Frontend/CSS] - [css 속성] z-index 우선순위 주기, z-index -(음수) 처리

 

이전에 z-index의 특징과 함께 각 영역별 우선순위를 주는 방법에 대해 포스팅 한 적이 있다.

그와 더불어 z-index 값을 음수로 처리하게 되면 어떤 문제가 생기고 어떻게 해결해야할지에 대해서도 포스팅을 했다.

 

실무를 하면서 z-index를 사용하게 될 일이 많은데 

z-index가 생각보다 다루기 어렵고 여러가지 상황을 고려해야하는 속성이라는걸 사용할 때마다 느낀다.

 

z-index 버그 중 최근에 알게된 버그가 있어서 포스팅을 하게 되었다.

 

관련 내용은 '쌓임 순서 (Stacking Order)' 라는 내용으로 많은 포스팅이 있지만

우선 순전히 실무 작업용으로 간단하게 버그 내용과 해결방법을 소개해보려한다.

 

1. opacity와 함께 사용시

<style>
  .wrap{position:relative; width:130px; height:130px; background:#ccc; color:#fff; font-size:13px;}
  .wrap .inner_box{position:absolute; width:40px; height:40px;}
  .box1{opacity:0.9;}
  .box1 .inner_box{top:10px; left:18px; background:purple; z-index:10;}
  .box2 .inner_box{top:18px; left:10px; background:red; z-index:1;}
</style>
  
<div class="wrap">
  <div class="box1">
    <div class="inner_box">BOX1</div>
  </div>

  <div class="box2">
    <div class="inner_box">BOX2</div>
  </div>
</div>

위와 같은 css와 html을 적용했을 때

원하는 출력 화면은 box1이 box2보다 위에 있는 모습일 것이다.

 

우선 .box1, .box2 요소가 z-index를 따로 선언해주지 않았기 때문에 큰 영향이 없는 상황에서

.box1 .inner_box가 z-index:10

.box2 .inner_box가 z-index:1 을 주었기 때문에

생각하고있는 이론상엔 box1 영역이 제일 위로 와야하지만

box2 영역이 오히려 위쪽으로 위치하게된다.

 

그 이유는 바로 .box1에 적용한 opacity 값 때문이다.

.box1의 .inner_box에 아무리 높은 z-index를 주게되어도

.box1에 opacity값을 주게되면 .box1영역은 z-index:0으로 임의 지정이 되기 때문이다.

 

<style>
  .wrap{position:relative; width:130px; height:130px; background:#ccc; color:#fff; font-size:13px;}
  .wrap .inner_box{position:absolute; width:40px; height:40px;}
  .box1{opacity:0.9;}  /* .box1{opacity:0.9; --> z-index:0} */
  .box1 .inner_box{top:10px; left:18px; background:purple; z-index:10;}
  .box2 .inner_box{top:18px; left:10px; background:red; z-index:1;}
</style>

<div class="wrap">
  <!-- box1 = z-index:0 -->
  <div class="box1">
    <div class="inner_box">BOX1</div>
  </div>

  <!-- box2 = z-index:auto -->
  <div class="box2">
    <div class="inner_box">BOX2</div>
  </div>
</div>

위 css 내용처럼 가

 

.box1 (z-index:0)

.box2 (z-index:auto) 로 적용이 되기 때문에 .box2에 있는 영역들이 위쪽으로 위치하게 된다.

 

2. transform과 함께 사용시

opacity 사용 때와 마찬가지로 특정 영역에 transform을 사용하게 되면

해당 영역은 z-index:0으로 적용이 된다.

<style>
  .wrap{position:relative; width:130px; height:130px; background:#ccc; color:#fff; font-size:13px;}
  .wrap .inner_box{position:absolute; width:40px; height:40px;}
  .box1{transform:translateX(-1px);}
  .box1 .inner_box{top:10px; left:18px; background:purple; z-index:10;}
  .box2 .inner_box{top:18px; left:10px; background:red; z-index:100;}
</style>
  
<div class="wrap">
  <div class="box1">
    <div class="inner_box">BOX1</div>
  </div>

  <div class="box2">
    <div class="inner_box">BOX2</div>
  </div>
</div>

위의 소스도 달라진 내용은 opacity대신 transform을 사용한 것 뿐인데

역시나 box2 영역이 위쪽으로 위치하게 된다.

 

3. 해결방법

위와 같이 opacity, transform 을 사용하게 되면

자식요소에 아무리 z-index 값을 높게 줘도 부모요소에서 이미 z-index:0 이 적용되기 때문에

원하는 화면을 출력하는데에 어려움이 있다.

 

그렇기 때문에 이 문제를 해결하기 위해서는 내가 앞쪽에 위치시킬 영역의 부모요소와

그것과 겹치는 영역의 부모요소와의 z-index 값을 조정하는 것이 중요하다.

<style>
  .wrap{position:relative; width:130px; height:130px; background:#ccc; color:#fff; font-size:13px;}
  .wrap .inner_box{position:absolute; width:40px; height:40px;}
  
  .box1{position:relatvie; z-index:10; transform:translateX(-1px);}
  .box1 .inner_box{top:10px; left:18px; background:purple; z-index:10;}
  
  .box2{position:relative; z-index:1}
  .box2 .inner_box{top:18px; left:10px; background:red; z-index:100;}
</style>
  
<div class="wrap">
  <!-- box1 = z-index:10 -->
  <div class="box1">
    <div class="inner_box">BOX1</div>
  </div>

  <!-- box1 = z-index:1 -->
  <div class="box2">
    <div class="inner_box">BOX2</div>
  </div>
</div>

위의 소스와 같이 box1에 opacity를 줬지만 관련해서 생기는 문제를 해결하기위해

.box1에 z-index값을 새로 정의해주고

더불어 형제 요소인 .box2에 z-index값을 그보다 낮게 주는 방법으로

.box1 .inner_box 영역을 내가 원하는대로 더 위쪽으로 위치시킬 수 있게 되었다.

 

 

 

 

반응형
반응형

Sass 문법은 일반적인 자바스크립트 문법과 같이

조건문 반복문과 같은 문법을 사용하여 css를 작성할 수 있다.

 

Sass 문법에서 사용하는 for문에 대해 알아보려한다.

 

1. @for 문의 사용

기본적으로 Sass의 다른 문법 및 함수 사용과 마찬가지로 @(골뱅이) 문자와 함께 for문을 사용한다.

@for 변수 from (시작 숫자) to (끝 숫자){...}

의 형태로 작성하고 to 대신 through를 작성할 수 있다. 

to와 through의 차이는 포스팅 하단에 예시를 통해 다룰 것이다.

 

위의 @for 문 작성 형식을 통해 예시를 보이자면

/* SCSS */
@for $i from 1 to 3{
  .icon_#{$i}{
    background-position:0 ($i * -10px);
  }
}


/* CSS */
.icon_1{background-position:0 -10px;}
.icon_2{background-position:0 -20px;}

위와 같이 작성하고 컴파일 되는 것을 확인할 수 있다.

$i라는 임의의 변수와 함께 반복될 범위의 숫자를 입력해서 사용한다.

 

@for 문을 작성할 때 변수보간법으로 처리해야 컴파일 에러가 발생하지 않는다.

2021.04.01 - [Frontend/Sass (SCSS)] - [Sass 특징] Interpolation (보간법, #{})

 

 

 

2. @for $var from ~ to ~ {...}

@for 반복문의 사용은 크게 from~to와 from~through 2가지 방법이 있다.

그 중 to의 경우 to 뒤에 나오는 숫자 미만의 반복되는 값을 사용한다. (해당 숫자 비포함)

/* SCSS */
@for $i from 0 to 5{
  .animal_#{$i}{
    background-position:0 ($i * -10px);
  }
}


/* CSS */
.animal_0{background-position:0 0;}
.animal_1{background-position:0 -10px;}
.animal_2{background-position:0 -20px;}
.animal_3{background-position:0 -30px;}
.animal_4{background-position:0 -40px;}

 

3. @for $var from ~ through ~ {...}

두 번째 방법인 through을 사용하는 방법은

through 뒤에 나오는 숫자 이하로 반복되는 값을 사용한다. (해당 숫자 포함)

/* SCSS */
@for $i from 0 through 5{
  .animal_#{$i}{
    background-position:0 ($i * -10px);
  }
}


/* CSS */
.animal_0{background-position:0 0;}
.animal_1{background-position:0 -10px;}
.animal_2{background-position:0 -20px;}
.animal_3{background-position:0 -30px;}
.animal_4{background-position:0 -40px;}
.animal_5{background-position:0 -50px;}

다른 소스는 2번의 to와 같지만 to -> through만 달라졌는데

컴파일되는 css값은 .animal_5이 하나 더 출력이 되었다.

 

이처럼 내가 원하는 범위만큼의 숫자를 출력하기 위해서는

to와 through의 사용시 차이점에 대해 익히고 사용할 필요가 있을 것 같다.

 

 

 


참고

sass-lang.com/documentation/at-rules/control/for

 

 

 

 

 

 

반응형

+ Recent posts