Last active
March 27, 2019 18:32
-
-
Save myso-kr/e96dd550444528655203a1589fc33496 to your computer and use it in GitHub Desktop.
mobile-simulate-hover// source https://jsbin.com/gudufiw
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 불필요한 간격 조절을 초기화 했습니다. */ | |
* { 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