웹에서 항목을 가리키는 것은 예전에는 간단했습니다. 마우스를 가지고 움직이거나 버튼을 누르는 정도였습니다. 마우스가 아닌 모든 것이 마우스로 에뮬레이션되었으며 개발자는 무엇을 기대할 수 있는지 정확히 알고 있었습니다.
단순하다고 해서 반드시 좋은 것은 아닙니다. 시간이 지남에 따라 모든 것이 마우스가 아니거나 마우스인 척하지 않는 것이 점점 더 중요해졌습니다. 압력 감지 및 기울기 감지 펜을 사용하면 놀라운 창의적 자유를 누릴 수 있었습니다. 손가락을 사용할 수 있으므로 기기와 손만 있으면 되었습니다. 한 손가락 이상을 사용하는 것도 좋습니다.
이를 지원하기 위해 한동안 터치 이벤트가 있었지만, 터치 전용의 완전히 별개의 API이므로 마우스와 터치를 모두 지원하려면 두 개의 별도 이벤트 모델을 코딩해야 합니다. Chrome 55에는 두 모델을 통합하는 최신 표준인 포인터 이벤트가 제공됩니다.
단일 이벤트 모델
포인터 이벤트는 브라우저의 포인터 입력 모델을 통합하여 터치, 펜, 마우스를 단일 이벤트 세트로 통합합니다. 예를 들면 다음과 같습니다.
document.addEventListener('pointermove',
ev => console.log('The pointer moved.'));
foo.addEventListener('pointerover',
ev => console.log('The pointer is now over foo.'));
다음은 사용 가능한 모든 이벤트 목록입니다. 마우스 이벤트에 익숙한 경우 매우 익숙하게 보일 것입니다.
pointerover
|
포인터가 요소의 경계 상자에 들어갔습니다.
마우스 오버를 지원하는 기기에서는 즉시, 지원하지 않는 기기에서는 pointerdown 이벤트 전에 발생합니다.
|
pointerenter
|
pointerover 와 유사하지만 버블링되지 않으며 자손을 다르게 처리합니다.
사양에 관한 세부정보
|
pointerdown
|
입력 기기의 시맨틱에 따라 버튼이 눌리거나 접촉이 설정된 활성 버튼 상태로 포인터가 전환되었습니다. |
pointermove
|
포인터의 위치가 변경되었습니다. |
pointerup
|
포인터가 활성 버튼 상태를 벗어났습니다. |
pointercancel
|
포인터가 더 이상 이벤트를 내보낼 가능성이 낮음을 의미하는 상황이 발생했습니다. 즉, 진행 중인 작업을 취소하고 중립 입력 상태로 돌아가야 합니다. |
pointerout
|
포인터가 요소 또는 화면의 경계 상자를 벗어났습니다. 기기가 마우스 오버를 지원하지 않는 경우 pointerup 뒤에도 적용됩니다.
|
pointerleave
|
pointerout 와 유사하지만 버블링되지 않으며 자손을 다르게 처리합니다.
사양에 관한 세부정보
|
gotpointercapture
|
요소가 포인터 캡처를 수신했습니다. |
lostpointercapture
|
캡처 중인 포인터가 해제되었습니다. |
다양한 입력 유형
일반적으로 포인터 이벤트를 사용하면 다양한 입력 장치에 대해 별도의 이벤트 핸들러를 등록하지 않고도 입력과 관계없는 방식으로 코드를 작성할 수 있습니다.
물론 마우스 오버 개념이 적용되는지와 같은 입력 유형 간의 차이점을 고려해야 합니다. 서로 다른 입력 기기 유형을 구분하려면(예: 서로 다른 입력에 별도의 코드/기능 제공) 동일한 이벤트 핸들러 내에서 PointerEvent
인터페이스의 pointerType
속성을 사용하면 됩니다. 예를 들어 측면 탐색 창을 코딩하는 경우 pointermove
이벤트에 다음 로직을 사용할 수 있습니다.
switch(ev.pointerType) {
case 'mouse':
// Do nothing.
break;
case 'touch':
// Allow drag gesture.
break;
case 'pen':
// Also allow drag gesture.
break;
default:
// Getting an empty string means the browser doesn't know
// what device type it is. Let's assume mouse and do nothing.
break;
}
기본 작업
터치 지원 브라우저에서는 특정 동작을 사용하여 페이지를 스크롤하거나, 확대/축소하거나, 새로고침합니다.
터치 이벤트의 경우 이러한 기본 작업이 실행되는 동안에도 이벤트가 계속 수신됩니다. 예를 들어 사용자가 스크롤하는 동안 touchmove
가 계속 실행됩니다.
포인터 이벤트를 사용하면 스크롤이나 확대/축소와 같은 기본 작업이 트리거될 때마다 브라우저가 포인터를 제어했음을 알리는 pointercancel
이벤트가 발생합니다. 예를 들면 다음과 같습니다.
document.addEventListener('pointercancel',
ev => console.log('Go home, the browser is in charge now.'));
기본 제공 속도: 이 모델은 동일한 수준의 응답성을 달성하기 위해 수동 이벤트 리스너를 사용해야 하는 터치 이벤트에 비해 기본적으로 더 나은 성능을 제공합니다.
touch-action
CSS 속성을 사용하여 브라우저가 제어하지 못하도록 할 수 있습니다. 요소에서 이 속성을 none
로 설정하면 해당 요소에서 시작된 모든 브라우저 정의 작업이 사용 중지됩니다. 하지만 브라우저가 x축의 움직임에는 반응하지만 y축의 움직임에는 반응하지 않도록 허용하는 pan-x
와 같이 더 세부적인 제어를 위한 다른 값도 많이 있습니다. Chrome 55는 다음 값을 지원합니다.
auto
|
기본값입니다. 브라우저에서 모든 기본 작업을 실행할 수 있습니다. |
none
|
브라우저는 기본 작업을 실행할 수 없습니다. |
pan-x
|
브라우저는 가로 스크롤 기본 작업만 실행할 수 있습니다. |
pan-y
|
브라우저는 세로 스크롤 기본 작업만 실행할 수 있습니다. |
pan-left
|
브라우저는 가로 스크롤 기본 작업만 실행하고 페이지를 왼쪽으로만 이동할 수 있습니다. |
pan-right
|
브라우저는 가로 스크롤 기본 작업만 실행하고 페이지를 오른쪽으로만 화면 이동할 수 있습니다. |
pan-up
|
브라우저는 세로 스크롤 기본 작업만 실행하고 페이지를 위로만 이동할 수 있습니다. |
pan-down
|
브라우저는 세로 스크롤 기본 작업만 실행하고 페이지를 아래로만 이동할 수 있습니다. |
manipulation
|
브라우저는 스크롤 및 확대/축소 작업만 실행할 수 있습니다. |
포인터 캡처
사용자가 클릭 타겟 외부의 버튼을 놓아서 mouseup
이벤트가 손상되었다는 사실을 깨닫기까지 한 시간 동안 짜증스러운 버그를 디버깅한 적이 있나요? 없으시다면 알겠습니다. 저만 그런 것일 수도 있습니다.
하지만 지금까지 이 문제를 해결할 만한 좋은 방법은 없었습니다. 물론 문서에 mouseup
핸들러를 설정하고 애플리케이션에 상태를 저장하여 항목을 추적할 수 있습니다. 하지만 이는 깔끔한 해결 방법은 아닙니다. 특히 웹 구성요소를 빌드하고 모든 것을 깔끔하게 격리하려는 경우 더욱 그렇습니다.
포인터 이벤트를 사용하면 훨씬 더 나은 솔루션을 사용할 수 있습니다. 포인터를 캡처하여 pointerup
이벤트 (또는 다른 포착하기 어려운 친구)를 확실히 가져올 수 있습니다.
const foo = document.querySelector('#foo');
foo.addEventListener('pointerdown', ev => {
console.log('Button down, capturing!');
// Every pointer has an ID, which you can read from the event.
foo.setPointerCapture(ev.pointerId);
});
foo.addEventListener('pointerup',
ev => console.log('Button up. Every time!'));
브라우저 지원
이 글을 작성하는 시점에서 포인터 이벤트는 Internet Explorer 11, Microsoft Edge, Chrome, Opera에서 지원되며 Firefox에서는 부분적으로 지원됩니다. caniuse.com에서 최신 목록을 확인할 수 있습니다.
포인터 이벤트 폴리필을 사용하여 공백을 메울 수 있습니다. 또는 런타임 시 브라우저 지원을 확인하는 것도 간단합니다.
if (window.PointerEvent) {
// Yay, we can use pointer events!
} else {
// Back to mouse and touch events, I guess.
}
포인터 이벤트는 점진적 개선을 위한 훌륭한 후보입니다. 초기화 메서드를 수정하여 위의 검사를 실행하고, if
블록에 포인터 이벤트 핸들러를 추가하고, 마우스/터치 이벤트 핸들러를 else
블록으로 이동하기만 하면 됩니다.
사용해 보고 의견을 알려주세요.