Skip to content

Instantly share code, notes, and snippets.

@myso-kr
Last active March 27, 2019 18:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save myso-kr/e96dd550444528655203a1589fc33496 to your computer and use it in GitHub Desktop.
Save myso-kr/e96dd550444528655203a1589fc33496 to your computer and use it in GitHub Desktop.
mobile-simulate-hover// source https://jsbin.com/gudufiw
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<title>mobile-simulate-hover</title>
</head>
<body>
<div class="container">
<input class="content simulate-hover" value="Focus me!" />
<span class="content simulate-hover simulate-hover-fixed" onmouseover="console.log('span hover!')">Fixed hover me!</span>
<span class="content simulate-hover" onmouseover="console.log('span hover!')">Hover me!</span>
<span class="content simulate-hover simulate-hover-fixed" onmouseover="console.log('span hover!')">Fixed hover me!</span>
<span class="content simulate-hover" onmouseover="console.log('span hover!')">Hover me!</span>
<span class="content simulate-hover simulate-hover-fixed" onmouseover="console.log('span hover!')">Fixed hover me!</span>
<span class="content simulate-hover" onmouseover="console.log('span hover!')">Hover me!</span>
<span class="content simulate-hover simulate-hover-fixed" onmouseover="console.log('span hover!')">Fixed hover me!</span>
<span class="content simulate-hover" onmouseover="console.log('span hover!')">Hover me!</span>
<span class="content simulate-hover simulate-hover-fixed" onmouseover="console.log('span hover!')">Fixed hover me!</span>
<span class="content simulate-hover" onmouseover="console.log('span hover!')">Hover me!</span>
<span class="content simulate-hover simulate-hover-fixed" onmouseover="console.log('span hover!')">Fixed hover me!</span>
<span class="content simulate-hover" onmouseover="console.log('span hover!')">Hover me!</span>
<span class="content simulate-hover simulate-hover-fixed" onmouseover="console.log('span hover!')">Fixed hover me!</span>
<span class="content simulate-hover" onmouseover="console.log('span hover!')">Hover me!</span>
<span class="content simulate-hover simulate-hover-fixed" onmouseover="console.log('span hover!')">Fixed hover me!</span>
<span class="content simulate-hover" onmouseover="console.log('span hover!')">Hover me!</span>
<span class="content simulate-hover simulate-hover-fixed" onmouseover="console.log('span hover!')">Fixed hover me!</span>
<span class="content simulate-hover" onmouseover="console.log('span hover!')">Hover me!</span>
<span class="content simulate-hover simulate-hover-fixed" onmouseover="console.log('span hover!')">Fixed hover me!</span>
<span class="content simulate-hover" onmouseover="console.log('span hover!')">Hover me!</span>
</div>
</body>
</html>
document.querySelector('input.content').addEventListener('mouseover', (e) => {
console.log('input hover !');
}, false)
// mouseover 이벤트를 강제 발생합니다.
function simulateHoverEvent (e, element, touch) {
// MouseEvent(mouseover) 관련 Property를 선언합니다.
const event_prop = {};
event_prop.button = 0;
event_prop.bubbles = true;
event_prop.cancelable = true;
event_prop.ctrlKey = false;
event_prop.altKey = false;
event_prop.shiftKey = false;
event_prop.metaKey = false;
event_prop.clientX = parseInt(touch.clientX);
event_prop.clientY = parseInt(touch.clientY);
event_prop.screenX = parseInt(touch.screenX);
event_prop.screenY = parseInt(touch.screenY);
event_prop.view = document.defaultView;
event_prop.relatedTarget = document.documentElement;
// MouseEvent를 생성하고 강제 호출합니다.
const event = new MouseEvent('mouseover', event_prop);
element.dispatchEvent(event);
}
// 멀티 터치를 지원합니다.
function simulateHoverTouch (e, touch) {
// 페이지에서 발생한 이벤트의 실제 좌표를 가져옵니다.
const { clientX, clientY } = touch;
// 위치를 기준으로 터치된 엘리먼트를 가져옵니다.
element = document.elementFromPoint(clientX, clientY);
// element가 없거나 .simulate-hover 클래스를 가지고 있지 않은 경우 무시합니다.
if(element && element.classList.contains('simulate-hover')){
// 멀티터치를 위해 활성화된 element를 리턴해줍니다.
// 리턴값이 없으면 비활성화로 간주하고 처리합니다.
if(e.type == 'touchstart' || e.type == 'touchmove') {
// flag를 체크하고 동작합니다.
if(element.dataset.simulateHover != 'true') {
// flag를 활성화합니다.
element.dataset.simulateHover = 'true';
// mouseover 이벤트를 강제 호출합니다.
simulateHoverEvent(e, element, touch);
}
return element;
}else
if(e.type == 'touchend') {
// flag를 초기화합니다.
element.dataset.simulateHover = 'false';
return
}else
if(e.type == 'mouseover') {
return element;
}else{
return;
}
}
}
// touchstart, touchend 이벤트가 발생하고 화면 전체의 스크롤을 잠금/잠금해제합니다.
function simulateScrollLock (e) {
if (e.touches.length > 1) return false;
if (e.preventDefault && e.cancelable) e.preventDefault();
return true;
}
// 잦은 이벤트 호출은 구형 단말기에서 Memory Leak을 유발할수 있기때문에 딜레이를 줘서 처리합니다.
function simulateHover (e) {
// 강제호출된 이벤트는 무시처리합니다.
if(e.relatedTarget == document.documentElement) return;
// 변수에 지정된 값을 메모리상에서 날리면서 clearTimeout을 할수 있어 제가 자주 활용하는 코드 스타일입니다.
simulateHover.timer = clearTimeout(simulateHover.timer);
simulateHover.timer = setTimeout(() => {
// 임시로 지정된 elements를 꺼내 활용합니다.
const elements = Array.from(document.querySelectorAll('.simulate-hover'));
// 멀티 터치 이벤트는 e.touches를, 마우스 이벤트는 e을 활용합니다.
const touches = /^touch/.test(e.type) ? Array.from(e.touches) : [e];
// 배열을 이용해 마우스오버 처리를 수행하고 element를 Array로 가져옵니다.
const elementsTouches = touches.map((touch) => simulateHoverTouch(e, touch));
// 처리된 된 .simulate-hover 객체를 모두 mouseover 처리합니다.
elementsTouches.forEach((element)=>element && element.classList.add('hover'));
// 마우스오버 이벤트를 처리하지 않는 객체를 필터링합니다.
const elementsTouchesNot = elements.filter((element)=>!elementsTouches.includes(element));
// 필터링 된 .simulate-hover 객체를 모두 초기화합니다.
elementsTouchesNot.forEach((element)=>element && element.classList.remove('hover'));
});
// 터치된 개체가 .simulate-hover-fixed 가 포함된 경우 무시처리합니다.
if(e.target.classList.contains('simulate-hover-fixed')) {
if(e.type == 'touchstart') {
e.target.addEventListener('touchmove', simulateScrollLock, { passive: false });
}
if(e.type == 'touchend') {
e.target.removeEventListener('touchmove', simulateScrollLock, { passive: false });
}
}
}
// 도큐먼트에 존재하는 모든 Element를 Listen하는 개념의 Delegate event를 정의합니다.
// 이 경우 event.target 등을 이용해 선택적인 Element 제어가 가능합니다.
// 모바일 브라우저를 위해 touch event를 mouseover로 처리합니다.
document.addEventListener('touchstart', simulateHover, false);
document.addEventListener('touchmove', simulateHover, false);
document.addEventListener('touchend', simulateHover, false);
// 데스크탑 브라우저를 위해 mouseover를 처리합니다.
document.addEventListener('mouseover', simulateHover, false);
// 버그픽스 목적 : 모바일 브라우저에서 빠르게 터치를 하는경우, touch 이벤트가 발행하지 않습니다.
document.addEventListener('click', simulateHover, false);
/* 불필요한 간격 조절을 초기화 했습니다. */
* { margin: 0; padding:0; zoom: 1; }
/* 모바일 브라우저의 pull-to-refresh의 기능을 제한하는 코드입니다. (일부 미적용) */
html, body { touch-action: none; }
/* 배경을 눈에 잘 띄는 시뻘건색으로 처리했습니다. */
.container {
width: 100vw;
height: 150vh;
background: #f00;
}
/* 터치 영역을 넓히기 위해 padding 값을 주었습니다. */
.content {
padding: 30px;
display: block;
}
/* 모바일 hover 시뮬레이터에 대한 스타일 처리입니다. */
.simulate-hover.hover {
background: #0f0;
}
/* --- 데스크탑 대응 소스코드 추가로 더이상 활용되지 않습니다. --- */
/* 화면 가로 길이가 1025px 이상인 단말기는 데스크탑으로 취급합니다. */
/* 굳이 이렇게 한이유는 :hover 플래그가 모바일 상에서는 focus와 동일?유사?하게 동작하기 때문... (안드 크롬 기준) */
/* 위와 같은 이유로 다른 곳을 터치하기 전까진 :hover의 스타일이 그대로 적용되는 것을 확인하실수 있습니다.*/
/* @media (min-width: 1025px) { */
/* 데스크탑은 :hover 셀렉터가 추가됩니다. */
/* .simulate-hover:hover, */
/* .simulate-hover.hover { */
/* background: #0f0; */
/* } */
/* } */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment