2016년 1월 8일 금요일

jQuery animate() 메소드의 중첩(중복) 실행 방지하기

인터넷 익스플로러(이하 IE)가 웹 브라우저 시장을 독점하던 시절, 파이어폭스, 오페라, 크롬 등 비주류였던 웹 브라우저들은 꾸준한 업데이트를 통해 빠르게 웹 표준을 받아들였지만, IE 시리즈는 2011년 출시 된 9부터 비로소 HTML5 표준 권고안을 일부 채용하면서 새로운 웹 표준에 한 발짝 다가섰다. 공정히 말하면 표준을 받아들인 시기는 크게 달랐다고 할 수 없지만 브라우저 자체를 업데이트하지 않고 레거시 엔진을 별도 탑재한 새로운 버전을 발표했다는 점이 IE의 가장 큰 문제였다.

IE가 왜 그렇게 큰 난관으로 불리우는지 모르는 사람들을 위해 한국의 기형적인 웹 생태계 문제부터 시작해서 밤을 새우며 이야기를 하고 싶지만 생략한다. Trident의 레거시 코드를 대거 삭제하고 표준 웹 브라우저를 표방한 Edge에서도 여전히 다양한 문제가 발생하고 있는 마당에 IE8 이하의 호환성에 대해서는 논할 필요도 없을 것이다.

IE의 시장 점유율을 무시할 수 없었던 2010년대 초중반의 웹 사이트 개발 프로젝트에서는 레거시 브라우저에 대한 지원이 당연한 이슈로 받아들여졌고, 웹 디자이너와 퍼블리셔에게 polyfill이나 shim(혹은 shiv)은 아주 친근한 단어였다. 하지만 근래에는 웹 브라우저가 다양해지고 모바일 기기를 통한 인터넷 접속 비율이 높아지면서 IE8 이하의 웹 브라우저를 위한 fallback을 기획 단계부터 배제하고 프로젝트를 진행하는 경우가 많다.

불과 3, 4년 전의 상황에 비하면 장족의 발전이라고 할 수 있지만 문제는 현재 크로스브라우징의 마지노선으로 여겨지는 IE9도 완벽하게 HTML5와 CSS3를 지원하지는 않는다는 점이다.

코드 불러오는 중...

CSS3의 transition 속성은 개체에 부드러운 전환 효과를 적용하기 위해 자주 사용된다. 위 코드펜의 단어에 마우스를 hover하면 IE10 이상의 브라우저(크롬 26.0+, 파이어폭스 16.0+, 사파리 6.1+, 오페라 12.1+)에서만 정상적으로 transition 효과가 출력된다. 모바일 브라우저에서는 hover 효과가 실행되지 않고 브라우저에 따라 클릭시 호버 상태로 고정된다.

이러한 이유로 transition 속성을 대체하기 위한 크로스브라우징 기술로 흔히 사용되는 것이 jQuery의 animate() 메소드이다.

코드 불러오는 중...

위 코드는 일견 문제가 없는 것처럼 보이지만 마우스를 여러 번 빠르게 hover하여 mouseenter와 mouseout을 반복해 보면 animation() 메소드가 중첩 실행된다는 것을 알 수 있다. 이는 위와 같은 단순한 코드 뿐만 아니라 모든 형태의 animate 효과에서 나타나는 문제이다. 이러한 현상을 방지할 수 있는 가장 쉬운 방법은 stop() 메소드를 이용하는 것이다.

코드 불러오는 중...

stop()은 호출과 동시에 현재 해당 객체에 실행되고 있는 animation을 정지시키기 때문에 animation() 메소드에 체이닝을 시켜두면 간단히 중첩 실행을 방지할 수 있다. 편리하고 직관적인 해법이지만 아래와 같은 경우에는 오히려 stop() 메소드가 독이 된다.

코드 불러오는 중...

위 코드에서는 개체가 정해진 거리를 부여된 duration 매개변수에 따라 이동하는 동안 stop() 메소드가 animation을 중단시키고 새로운 animation이 시작되기 때문에 이동 구간이 중첩되는 문제가 발생한다. duration 단위 이상의 rate로 animation을 갱신하면 정상적으로 동작하지만 duration 값보다 빠르게 갱신을 시도하면 animation 시퀀스가 깨지게 된다. 이 경우 stop() 체이닝을 대체할 수 있는 수단은 아래와 같다.

코드 불러오는 중...

위 코드에서는 절대값을 구하는 Math.abs 함수와 현재 시간을 출력하는 $.now를 이용, timer 변수에 animation 시작 시간을 기록하고 이전 animation 시작 시간과의 차이가 duration 값 이상일 경우에만 animation을 실행하도록 입력 이벤트를 제어한다. 모바일 브라우저에서 코드펜 렌더링 오류가 발생하는 경우가 있으나 실적용시에는 정상 작동한다. 이 방법은 슬라이더를 제작할 때 유용하게 사용할 수 있다.

2016년 1월 6일 수요일

자바스크립트 replace() 전체 문서에 적용하기

개체 내에서 특정 문자(character) 또는 문자열(string)을 치환하여 출력하거나 인자로서 전달하고자 할 때 자바스크립트의 replace() 메소드를 사용할 수 있다. replace()는 찾고자 하는 값과 치환할 값을 매개변수로 전달받는다.

코드 불러오는 중...

위의 예시에 있는 버튼을 클릭하면 치환 함수가 실행되는데, 첫 번째 매개변수 값과 일치하는 최초의 값만이 두 번째 매개변수 값으로 치환된다는 것을 알 수 있다. 이는 버그가 아니라 최초 일치값만 치환하도록 설계된 replace() 메소드의 스펙이다.

Java 라이브러리에는 정규식 치환을 위한 replaceAll() 클래스가 존재하지만 자바스크립트에는 이러한 메소드가 제공되지 않는다. jQuery에 동명의 메소드가 있지만 Java의 replaceAll()과는 다른 기능을 갖는다.

그렇다면 치환 범위 내에서 검색된 모든 일치값을 치환하기 위해서는 어떤 방법을 생각해볼 수 있을까?

replace() 메소드의 매개변수에는 정규 표현식(regexp)의 사용이 가능하다. 정규 표현식에서 전역 매치를 위한 제어자(modifier)인 g를 첫 번째 매개변수에 적용하면 아래와 같은 결과를 출력한다.

코드 불러오는 중...

제어자 i는 소문자, 대문자 여부를 무시하고(case insensitive) 문자 또는 문자열의 일치를 판단한다. /gi는 소문자, 대문자 여부를 무시하고 전역 매칭을 수행하여 아래와 같은 결과를 출력한다. 매개변수에 특수문자가 포함되어 있을 경우 정규식 문법으로 인식하기 때문에 특수문자 앞에는 반드시 백슬래쉬(\)를 삽입하여 escape 처리하도록 한다. 정규 표현식에 관한 자세한 내용은 MDN 문서 중 정규식 부분을 참고(한글, 클릭하면 이동).

코드 불러오는 중...

위의 예시에서는 간단히 문자열을 매개변수로 전달했지만 변수(variable)를 전달해야 할 때는 아래 코드와 같이 RegExp(변수명, '제어자') 함수를 이용하도록 한다. 이 경우 특수문자의 escape 처리도 정규식을 활용하는 것이 효율적이다.

코드 불러오는 중...

2015년 12월 24일 목요일

유투브(YouTube) 동영상을 웹 사이트 배경으로 사용하기

2000년대 중반부터 고해상도 사진이나 일러스트를 메인 페이지 배경으로 사용하는 웹 사이트들이 하나 둘 등장하기 시작했다. 브라우저 화면 전체를 덮는 배경 그림의 활용은 개인 혹은 단체, 제품의 이미지 전달에 있어 종래의 패턴이나 단색으로 이루어진 배경보다 효과적이었고 반응형 디자인(responsive web design), 패럴랙스 스크롤(parallax scrolling) 등 새로운 웹 디자인 기술과 시너지 효과를 발휘하면서 주요 트렌드로 자리잡게 되었다.

통신망 성능의 발달과 함께 근래에는 이미지 배경에서 더욱 나아가 동영상을 페이지 배경으로 삽입하는 웹 사이트도 늘어나고 있다. 동영상 배경의 모범적인 활용 사례로 추천하는 웹 사이트들은 아래와 같다.

- Y.co Yacht : http://y.co/
- Maple : https://maple.com/
- Switzerland Inc : http://switzerlandinc.com/
- BKWLD : http://www.bkwld.com/
- Big Cartel : https://www.bigcartel.com/

고품질의 동영상 배경은 웹 사이트를 한 폭의 예술 작품처럼 보이도록 만들기도 하지만 서버 입장에서는 어마어마한 트래픽을 잡아먹는 괴물이기도 하다. 이러한 문제를 해결하기 위해 스트리밍 서버를 따로 운용하거나 동영상 화질을 낮추어 인코딩하는 경우가 많다.

하지만 만약, 대용량 무료 스트리밍 서비스를 활용할 수 있다면 어떨까?

바로바로퀵서비스(http://www.allbaro.co.kr/)의 메인 페이지는 YouTube에 업로드 된 동영상을 embed하여 배경으로 출력한다. 소스는 다음과 같다. 결과가 정상적으로 출력되지 않을 경우 RERUN 버튼을 클릭하면 동영상이 다시 로드된다. 단, 모바일에서는 동영상 자동 재생이 지원되지 않기 때문에 플레이어 iframe을 숨기고 필요에 따라 배경 이미지로 대체한다.

코드 불러오는 중...

위 코드에서 YouTube 동영상의 아이디 값 뒤에 붙는 매개변수(parameter)가 제어하는 기능은 아래와 같다.

- autoplay : 자동 재생 여부
- controls : 재생 컨트롤 표기 여부
- showinfo : 영상 제목 출력 여부
- wmode : 윈도우 모드 (flash와 동일)
- autohide : 컨트롤 자동 숨김 여부
- loop : 영상 반복 재생 여부
- playlist : 다음 플레이 영상 지정

YouTube 내장 플레이어의 매개변수에 대한 더 자세한 사항은 Google Developers 기술문서 참고(한글, 클릭하면 이동).

스크립트 부분을 보면, YouTube 동영상은 종횡비가 고정된 상태로 container의 크기 속성 중 상수를 기준으로 크기가 변하기 때문에 위 코드에서는 브라우저 너비와 종횡비의 곱을 구하여 높이 속성을 대체한다. 실제 개발시에는 DOM ready 이후 최초 한 번 실행하고 $(window).resize()에도 바인드하면 더욱 효과적이다.

위 코드의 개념을 이해한 뒤에는 입맛에 맞게 코딩하면 되는데 OS와 브라우저 환경에 따라 동영상 container에 z-index 속성이 적용되지 않고 동영상 플레이어가 오버레이 개체를 덮어버리는 경우가 있다. 보통 Windows 7/8 + IE 조합에서 자주 발생하는 버그인데 wmode=opaque 패러미터를 위 코드와 같이 영상 아이디 뒤에 추가하여 해결할 수 있다.

* 2016년 1월 6일 내용 추가

tubular(http://www.seanmccambridge.com/tubular/)는 YouTube 동영상을 간편하게 웹 페이지 배경으로 지정할 수 있는 jQuery 플러그인이다. ratio, repeat, mute 등 다양한 옵션을 통해 영상을 제어할 수 있다.

2015년 12월 23일 수요일

복수의 inline-block 엘리먼트 사이에 발생하는 간격 제거하기

코드 :

코드 불러오는 중...

원하는 결과물 :

A
B
C

실제 출력결과 :

A
B
C

스타일의 display 속성에 inline-block 값이 지정된 개체(element)들을 일렬로 배치하면 위와 같이 개체와 개체가 만나는 부분에 작은 간격(space)이 생기는 것을 볼 수 있다. 이 문제는 각 개체를 글자처럼 표현하여 수평 정렬하는 inline-block 속성의 근본적인 원리에 기인하는 것으로 보인다.

이를 제거하기 위해 다양한 해법들이 제시되어 왔는데, 그 중 가장 유명한 것이 부모 개체의 font-size 또는 letter-spacing 속성을 0으로 만들고 inline-block 형태의 배치 속성을 가진 자식 엘리먼트에 다시 속성값을 지정해주는 방식이다. 배치 원리를 그대로 이용한 해법으로 이해와 활용이 쉽지만 코드가 다소 복잡해지고 em 또는 % 단위의 활용이 어렵다는 단점이 있다.

또 다른 고전적인 해법으로는 클로징 태그 생략, 강제 마진 적용, 태그 붙여쓰기 등이 있지만 이러한 방식들은 직관적이지 못하거나 브라우저에 따라 다른 결과를 출력할 수 있기 때문에 사용을 권장하지 않는다. flexbox 레이아웃을 사용하면 간단히 해결할 수 있지만 아직까지 한국의 인터넷 사용 환경에서는 레거시 브라우저를 고려하지 않을 수 없기 때문에 이는 논외로 한다.

그렇다면 inline-block 개체 간격을 제거하기 위해 어떤 방법을 사용하는 것이 가장 이상적일까?

코드 불러오는 중...

HTML 마크업에서 선행 개체의 클로징 태그 뒷부분과 후위 개체의 오프닝 태그 앞부분에 주석 태그를 붙이면 각 개체의 클로징, 오프닝 태그를 붙여 쓴 것과 같은 효과를 얻을 수 있다. 이를 응용하여 위 코드와 같이 두 inline-block 개체 사이에 주석 태그를 삽입하면 원하지 않는 간격의 발생을 방지할 수 있다.