[Javascript] Quickly.js : Touch Device에서 Click Delay를 없애는 Library.
실제로 테스트를 해보면 약 300~500ms 정도의 Delay가 존재한다.
버튼에 클릭 효과가 발생할 때까지 300~500ms 정도의 Delay가 발생한다면 사용자가 답답하다고 느낄 수 있기 때문에 터치가 시작되는 시점에 클릭 효과를 추가하고 터치가 끝나는 시점에 클릭 효과를 제거하는 방법으로 Delay를 제거할 수 있다.
이러한 작업을 위해 iScroll의 개발자로 유명한 Matteo Spinell씨가 NoClickDelay라는 Object를 만들어놨다. (http://cubiq.org/remove-onclick-delay-on-webkit-for-iphone)
하지만 실제로 사용해보니 약간의 버그?(터치가 시작되는 Element와 터치가 끝나는 Element가 서로 다를 경우 추가된 css class가 제대로 제거되지 않는 문제)가 있고 PC 환경은 지원하지 않기 때문에 모바일/PC 환경에서 동일하게 사용할 수 있도록 Quickly라는 이름으로 Custom 하였다.
PS) touchend/mouseup 이벤트 리스너를 클릭된 element가 아닌 window object에 등록하였기 때문에 터치가 시작되는 Element와 터치가 끝나는 Element가 달라도 정상 동작한다.
- Source Code -
https://github.com/RegularMotion/Quickly/
- Demo -
- 사용법 -
1. new Quickly(‘element_id’);
Element의 ID 또는 Element Object를 이용하여 Quickly Object를 생성한다.
- 내부동작 -
1. 터치/클릭 이벤트가 시작되는 시점에 element에 clicked Class를 추가.
2. 터치/클릭 이벤트가 끝나는 시점에 element에서 clicked Class 제거.
- clicked CSS Class 정의 -
div#element_id { position: relative; margin: 0 auto; top: 25px; width: 150px; height: 150px; background: #548937; } div#element_id.clicked { opacity: .5; }
- Javascript 구현부 -
function Quickly(el, callback) { this.callback = callback; this.clicked = (typeof el === 'object')? el : document.getElementById(el); this.clicked.addEventListener(Quickly.EVT_START, this, false); } Quickly.isTouch = ('ontouchstart' in window)? true : false; Quickly.EVT_START = Quickly.isTouch? 'touchstart' : 'mousedown'; Quickly.EVT_END = Quickly.isTouch? 'touchend' : 'mouseup'; Quickly.prototype = { handleEvent: function(e) { switch (e.type) { case Quickly.EVT_START: this.onTouchStart(e); break; case Quickly.EVT_END: this.onTouchEnd(e); break; } e.stopPropagation(); e.preventDefault(); }, onTouchStart: function(e) { this.clicked.className += (this.clicked.className === '')? 'clicked':' clicked'; document.documentElement.addEventListener(Quickly.EVT_END, this, false); }, onTouchEnd: function(e) { document.documentElement.removeEventListener(Quickly.EVT_END, this, false); if ( this.clicked ) this.clicked.className = this.clicked.className.replace(/ ?clicked/gi, ''); if ( typeof this.callback === 'function' ) this.callback(e); } }; new Quickly('element_id || dom element', function(e) { });
- Hover Effect까지 처리해주는 Version -
function Quickly(el, callback) { this.callback = callback; this.clicked = (typeof el === 'object')? el : document.getElementById(el); this.subscribe(Quickly.EVT_START, this.clicked); if ( !Quickly.isTouch ) { this.hovered = this.clicked; this.subscribe(Quickly.EVT_HOVER, this.hovered); } } Quickly.isTouch = ('ontouchstart' in window)? true : false; Quickly.EVT_START = Quickly.isTouch? 'touchstart' : 'mousedown'; Quickly.EVT_END = Quickly.isTouch? 'touchend' : 'mouseup'; Quickly.EVT_HOVER = Quickly.isTouch? null : 'mouseover'; Quickly.EVT_LEAVE = Quickly.isTouch? null : 'mouseout'; Quickly.prototype = { handleEvent: function(e) { switch (e.type) { case Quickly.EVT_START: this.onTouchStart(e); break; case Quickly.EVT_END : this.onTouchEnd(e); break; case Quickly.EVT_HOVER: this.onHovered(e); break; case Quickly.EVT_LEAVE: this.onLeaved(e); break; } e.stopPropagation(); e.preventDefault(); }, onTouchStart: function(e) { this.clicked.className += (this.clicked.className === '')? 'clicked':' clicked'; this.subscribe(Quickly.EVT_END); }, onTouchEnd: function(e) { this.unsubscribe(Quickly.EVT_END); if ( this.clicked ) this.clicked.className = this.clicked.className.replace(/ ?clicked/gi, ''); if ( typeof this.callback === 'function' ) this.callback(e); }, onHovered: function(e) { this.hovered.className += (this.hovered.className === '')? 'hovered':' hovered'; this.subscribe(Quickly.EVT_LEAVE, this.hovered); }, onLeaved: function(e) { this.unsubscribe(Quickly.EVT_LEAVE, this.hovered); this.hovered.className = this.hovered.className.replace(/ ?hovered/gi, ''); }, subscribe: function(event, el) { var element = el || document.documentElement; element.addEventListener(event, this, false); }, unsubscribe: function(event, el) { var element = el || document.documentElement; element.removeEventListener(event, this, false); } };