| 1 | /*! |
| 2 | * swiped-events.js - v@version@ |
| 3 | * Pure JavaScript swipe events |
| 4 | * https://github.com/john-doherty/swiped-events |
| 5 | * @inspiration https://stackoverflow.com/questions/16348031/disable-scrolling-when-touch-moving-certain-element |
| 6 | * @author John Doherty <www.johndoherty.info> |
| 7 | * @license MIT |
| 8 | */ |
| 9 | (function (window, document) { |
| 10 | |
| 11 | 'use strict'; |
| 12 | |
| 13 | // patch CustomEvent to allow constructor creation (IE/Chrome) |
| 14 | if (typeof window.CustomEvent !== 'function') { |
| 15 | |
| 16 | window.CustomEvent = function (event, params) { |
| 17 | |
| 18 | params = params || { bubbles: false, cancelable: false, detail: undefined }; |
| 19 | |
| 20 | var evt = document.createEvent('CustomEvent'); |
| 21 | evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail); |
| 22 | return evt; |
| 23 | }; |
| 24 | |
| 25 | window.CustomEvent.prototype = window.Event.prototype; |
| 26 | } |
| 27 | |
| 28 | document.addEventListener('touchstart', handleTouchStart, false); |
| 29 | document.addEventListener('touchmove', handleTouchMove, false); |
| 30 | document.addEventListener('touchend', handleTouchEnd, false); |
| 31 | |
| 32 | var xDown = null; |
| 33 | var yDown = null; |
| 34 | var xDiff = null; |
| 35 | var yDiff = null; |
| 36 | var timeDown = null; |
| 37 | var startEl = null; |
| 38 | |
| 39 | /** |
| 40 | * Fires swiped event if swipe detected on touchend |
| 41 | * @param {object} e - browser event object |
| 42 | * @returns {void} |
| 43 | */ |
| 44 | function handleTouchEnd(e) { |
| 45 | |
| 46 | // if the user released on a different target, cancel! |
| 47 | if (startEl !== e.target) return; |
| 48 | |
| 49 | var swipeThreshold = parseInt(getNearestAttribute(startEl, 'data-swipe-threshold', '20'), 10); // default 20 units |
| 50 | var swipeUnit = getNearestAttribute(startEl, 'data-swipe-unit', 'px'); // default px |
| 51 | var swipeTimeout = parseInt(getNearestAttribute(startEl, 'data-swipe-timeout', '500'), 10); // default 500ms |
| 52 | var timeDiff = Date.now() - timeDown; |
| 53 | var eventType = ''; |
| 54 | var changedTouches = e.changedTouches || e.touches || []; |
| 55 | |
| 56 | if (swipeUnit === 'vh') { |
| 57 | swipeThreshold = Math.round((swipeThreshold / 100) * document.documentElement.clientHeight); // get percentage of viewport height in pixels |
| 58 | } |
| 59 | if (swipeUnit === 'vw') { |
| 60 | swipeThreshold = Math.round((swipeThreshold / 100) * document.documentElement.clientWidth); // get percentage of viewport height in pixels |
| 61 | } |
| 62 | |
| 63 | if (Math.abs(xDiff) > Math.abs(yDiff)) { // most significant |
| 64 | if (Math.abs(xDiff) > swipeThreshold && timeDiff < swipeTimeout) { |
| 65 | if (xDiff > 0) { |
| 66 | eventType = 'swiped-left'; |
| 67 | } |
| 68 | else { |
| 69 | eventType = 'swiped-right'; |
| 70 | } |
| 71 | } |
| 72 | } |
| 73 | else if (Math.abs(yDiff) > swipeThreshold && timeDiff < swipeTimeout) { |
| 74 | if (yDiff > 0) { |
| 75 | eventType = 'swiped-up'; |
| 76 | } |
| 77 | else { |
| 78 | eventType = 'swiped-down'; |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | if (eventType !== '') { |
| 83 | |
| 84 | var eventData = { |
| 85 | dir: eventType.replace(/swiped-/, ''), |
| 86 | touchType: (changedTouches[0] || {}).touchType || 'direct', |
| 87 | xStart: parseInt(xDown, 10), |
| 88 | xEnd: parseInt((changedTouches[0] || {}).clientX || -1, 10), |
| 89 | yStart: parseInt(yDown, 10), |
| 90 | yEnd: parseInt((changedTouches[0] || {}).clientY || -1, 10) |
| 91 | }; |
| 92 | |
| 93 | // fire `swiped` event event on the element that started the swipe |
| 94 | startEl.dispatchEvent(new CustomEvent('swiped', { bubbles: true, cancelable: true, detail: eventData })); |
| 95 | |
| 96 | // fire `swiped-dir` event on the element that started the swipe |
| 97 | startEl.dispatchEvent(new CustomEvent(eventType, { bubbles: true, cancelable: true, detail: eventData })); |
| 98 | } |
| 99 | |
| 100 | // reset values |
| 101 | xDown = null; |
| 102 | yDown = null; |
| 103 | timeDown = null; |
| 104 | } |
| 105 | |
| 106 | /** |
| 107 | * Records current location on touchstart event |
| 108 | * @param {object} e - browser event object |
| 109 | * @returns {void} |
| 110 | */ |
| 111 | function handleTouchStart(e) { |
| 112 | |
| 113 | // if the element has data-swipe-ignore="true" we stop listening for swipe events |
| 114 | if (e.target.getAttribute('data-swipe-ignore') === 'true') return; |
| 115 | |
| 116 | startEl = e.target; |
| 117 | |
| 118 | timeDown = Date.now(); |
| 119 | xDown = e.touches[0].clientX; |
| 120 | yDown = e.touches[0].clientY; |
| 121 | xDiff = 0; |
| 122 | yDiff = 0; |
| 123 | } |
| 124 | |
| 125 | /** |
| 126 | * Records location diff in px on touchmove event |
| 127 | * @param {object} e - browser event object |
| 128 | * @returns {void} |
| 129 | */ |
| 130 | function handleTouchMove(e) { |
| 131 | |
| 132 | if (!xDown || !yDown) return; |
| 133 | |
| 134 | var xUp = e.touches[0].clientX; |
| 135 | var yUp = e.touches[0].clientY; |
| 136 | |
| 137 | xDiff = xDown - xUp; |
| 138 | yDiff = yDown - yUp; |
| 139 | } |
| 140 | |
| 141 | /** |
| 142 | * Gets attribute off HTML element or nearest parent |
| 143 | * @param {object} el - HTML element to retrieve attribute from |
| 144 | * @param {string} attributeName - name of the attribute |
| 145 | * @param {any} defaultValue - default value to return if no match found |
| 146 | * @returns {any} attribute value or defaultValue |
| 147 | */ |
| 148 | function getNearestAttribute(el, attributeName, defaultValue) { |
| 149 | |
| 150 | // walk up the dom tree looking for attributeName |
| 151 | while (el && el !== document.documentElement) { |
| 152 | |
| 153 | var attributeValue = el.getAttribute(attributeName); |
| 154 | |
| 155 | if (attributeValue) { |
| 156 | return attributeValue; |
| 157 | } |
| 158 | |
| 159 | el = el.parentNode; |
| 160 | } |
| 161 | |
| 162 | return defaultValue; |
| 163 | } |
| 164 | |
| 165 | }(window, document)); |