├── README.md
├── jquery-ui-notes.txt
├── jquery.ui.touch-punch.js
└── package.json
/README.md:
--------------------------------------------------------------------------------
1 | # jQuery UI Touch Punch
2 | ## Touch Event Support for jQuery UI
3 |
4 | > **jQuery UI Touch Punch is a small hack that enables the use of touch events on sites using the jQuery UI user interface library.**
5 |
6 | Currently, [jQuery UI](http://jqueryui.com/) user interface library does not support the use of touch events in their widgets and interactions. This means that the slick UI you designed and tested in your desktop browser will fail on most, if not all, touch-enabled mobile devices, because jQuery UI listens to mouse events—mouseover, mousemove and mouseout—not touch events—touchstart, touchmove and touchend.
7 |
8 | That's where jQuery UI Touch Punch comes in. Touch Punch works by using [simulated events](https://developer.mozilla.org/en/DOM/document.createEvent) to map [touch events](http://www.html5rocks.com/en/mobile/touch/) to their mouse event analogs. Simply include the script on your page and your touch events will be turned into their corresponding mouse events to which jQuery UI will respond as expected.
9 |
10 | As I said, Touch Punch is a hack. It [duck punches](http://en.wikipedia.org/wiki/Monkey_patch) some of jQuery UI's core functionality to handle the mapping of touch events. Touch Punch works with all basic implementations of jQuery UI's interactions and widgets. However, you may find more complex cases where Touch Punch fails. If so, scroll down to learn how you can file and/or fix issues.
11 |
12 | This code is dual licensed under the MIT or GPL Version 2 licenses and is therefore free to use, modify and/or distribute, but if you include Touch Punch in other software packages or plugins, please include an attribution to the original software and a link to [this repo](https://github.com/RWAP/jquery-ui-touch-punch).
13 |
14 | Fork: https://github.com/RWAP/jquery-ui-touch-punch
15 |
16 | RWAP Version (2019-2024)
17 |
18 | The [original repo](https://github.com/furf/jquery-ui-touch-punch) was last updated in 2014.
19 |
20 | I have created a new fork which contains various suggested improvements to the code when it became clear that touch-punch does not work too well on Android devices, and actually stopped the close button on jquery-ui dialogs from working on some devices.
21 |
22 | www.rwapsoftware.co.uk
23 |
24 |
25 | ## Using Touch Punch is as easy as 1, 2…
26 |
27 | Just follow these simple steps to enable touch events in your jQuery UI app:
28 |
29 | 1. Include jQuery and jQuery UI on your page.
30 |
31 | ```html
32 |
33 |
34 | ```
35 |
36 | 2. Include Touch Punch after jQuery UI and before its first use.
37 |
38 | Please note that if you are using jQuery UI's components, Touch Punch must be included after jquery.ui.mouse.js, as Touch Punch modifies its behavior.
39 |
40 | ```html
41 |
42 | ```
43 |
44 | 3. There is no 3. Just use jQuery UI as expected and watch it work at the touch of a finger.
45 |
46 | ```html
47 |
48 | ```
49 |
50 | _Tested on iPad, iPhone, Android and other touch-enabled mobile devices._
51 |
52 | ## If it does not seem to work…
53 |
54 | Remember that Touch Punch is just allowing various key strokes (such as touch, cliuck and double click) to emulate mouse movements. We do nothing with the underlying jQuery code.
55 |
56 | If it does not appear to work for you:
57 |
58 | 1. Ensure that you are using the latest version of this plugin (and not the [original repo](https://github.com/furf/jquery-ui-touch-punch) ).
59 |
60 | 2. Ensure that you have the latest versions of jQuery and jQuery UI linked in your webpage, BEFORE this code
61 |
62 | 3. Ensure that the original code works as expected on desktop.
63 |
--------------------------------------------------------------------------------
/jquery-ui-notes.txt:
--------------------------------------------------------------------------------
1 | Whilst implementing this, we noticed that on some mobile phones there were still some issues:
2 |
3 | a) If you have a jquery-ui dialog box open, it could be nigh impossible to use the close icon in the top right hand
4 | corner on some phones.
5 | After some experimentation, we found that we had to check if the screen size <= 480 and in that case, set the dialog
6 | "draggable" setting to false
7 |
8 | Presumably the phone was struggling to work out if we were trying to drag a box which might only be a couple of pixels smaller
9 | than the width of the screen, or we wanted to hit the close icon.
10 |
--------------------------------------------------------------------------------
/jquery.ui.touch-punch.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery UI Touch Punch 1.1.5 as modified by RWAP Software
3 | * based on original touchpunch v0.2.3 which has not been updated since 2014
4 | *
5 | * Updates by RWAP Software to take account of various suggested changes on the original code issues
6 | *
7 | * Original: https://github.com/furf/jquery-ui-touch-punch
8 | * Copyright 2011–2014, Dave Furfero
9 | * Dual licensed under the MIT or GPL Version 2 licenses.
10 | *
11 | * Fork: https://github.com/RWAP/jquery-ui-touch-punch
12 | *
13 | * Depends:
14 | * jquery.ui.widget.js
15 | * jquery.ui.mouse.js
16 | */
17 |
18 | (function( factory ) {
19 | if ( typeof define === "function" && define.amd ) {
20 |
21 | // AMD. Register as an anonymous module.
22 | define([ "jquery", "jquery-ui" ], factory );
23 | } else {
24 |
25 | // Browser globals
26 | factory( jQuery );
27 | }
28 | }(function ($) {
29 |
30 | // Detect touch support - Windows Surface devices and other touch devices
31 | $.mspointer = window.navigator.msPointerEnabled;
32 | $.touch = ( 'ontouchstart' in document
33 | || 'ontouchstart' in window
34 | || window.TouchEvent
35 | || (window.DocumentTouch && document instanceof DocumentTouch)
36 | || navigator.maxTouchPoints > 0
37 | || navigator.msMaxTouchPoints > 0
38 | );
39 |
40 | // Ignore browsers without touch or mouse support
41 | if ((!$.touch && !$.mspointer) || !$.ui.mouse) {
42 | return;
43 | }
44 |
45 | let mouseProto = $.ui.mouse.prototype,
46 | _mouseInit = mouseProto._mouseInit,
47 | _mouseDestroy = mouseProto._mouseDestroy,
48 | touchHandled, lastClickTime = 0;
49 |
50 | /**
51 | * Get the x,y position of a touch event
52 | * @param {Object} event A touch event
53 | */
54 | function getTouchCoords (event) {
55 | return {
56 | x: event.originalEvent.changedTouches[0].pageX,
57 | y: event.originalEvent.changedTouches[0].pageY
58 | };
59 | }
60 |
61 | /**
62 | * Simulate a mouse event based on a corresponding touch event
63 | * @param {Object} event A touch event
64 | * @param {String} simulatedType The corresponding mouse event
65 | */
66 | function simulateMouseEvent (event, simulatedType) {
67 |
68 | // Ignore multi-touch events
69 | if (event.originalEvent.touches.length > 1) {
70 | return;
71 | }
72 |
73 | //Ignore input or textarea elements so user can still enter text
74 | if ($(event.target).is("input") || $(event.target).is("textarea")) {
75 | return;
76 | }
77 |
78 | // Prevent "Ignored attempt to cancel a touchmove event with cancelable=false" errors
79 | if (event.cancelable) {
80 | event.preventDefault();
81 | }
82 |
83 | let touch = event.originalEvent.changedTouches[0],
84 | simulatedEvent = new MouseEvent(simulatedType, {
85 | bubbles: true,
86 | cancelable: true,
87 | view:window,
88 | screenX:touch.screenX,
89 | screenY:touch.screenY,
90 | clientX:touch.clientX,
91 | clientY:touch.clientY
92 | });
93 |
94 | // Dispatch the simulated event to the target element
95 | event.target.dispatchEvent(simulatedEvent);
96 | }
97 |
98 | /**
99 | * Handle the jQuery UI widget's touchstart events
100 | * @param {Object} event The widget element's touchstart event
101 | */
102 | mouseProto._touchStart = function (event) {
103 |
104 | let self = this;
105 |
106 | // Interaction time
107 | this._startedMove = event.timeStamp;
108 |
109 | // Track movement to determine if interaction was a click
110 | self._startPos = getTouchCoords(event);
111 |
112 | // Ignore the event if another widget is already being handled
113 | if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
114 | return;
115 | }
116 |
117 | // Set the flag to prevent other widgets from inheriting the touch event
118 | touchHandled = true;
119 |
120 | // Track movement to determine if interaction was a click
121 | self._touchMoved = false;
122 |
123 | // Simulate the mouseover event
124 | simulateMouseEvent(event, 'mouseover');
125 |
126 | // Simulate the mousemove event
127 | simulateMouseEvent(event, 'mousemove');
128 |
129 | // Simulate the mousedown event
130 | simulateMouseEvent(event, 'mousedown');
131 | };
132 |
133 | /**
134 | * Handle the jQuery UI widget's touchmove events
135 | * @param {Object} event The document's touchmove event
136 | */
137 | mouseProto._touchMove = function (event) {
138 |
139 | // Ignore event if not handled
140 | if (!touchHandled) {
141 | return;
142 | }
143 |
144 | // Interaction was moved
145 | this._touchMoved = true;
146 |
147 | // Simulate the mousemove event
148 | simulateMouseEvent(event, 'mousemove');
149 | };
150 |
151 | /**
152 | * Handle the jQuery UI widget's touchend events
153 | * @param {Object} event The document's touchend event
154 | */
155 | mouseProto._touchEnd = function (event) {
156 |
157 | // Ignore event if not handled
158 | if (!touchHandled) {
159 | return;
160 | }
161 |
162 | // Simulate the mouseup event
163 | simulateMouseEvent(event, 'mouseup');
164 |
165 | // Simulate the mouseout event
166 | simulateMouseEvent(event, 'mouseout');
167 |
168 | // If the touch interaction did not move, it should trigger a click
169 | // Check for this in two ways - length of time of simulation and distance moved
170 | // Allow for Apple Stylus to be used also
171 | let timeMoving = event.timeStamp - this._startedMove;
172 | if (!this._touchMoved || timeMoving < 500) {
173 | // Simulate the click event
174 | if( event.timeStamp - lastClickTime < 400 )
175 | simulateMouseEvent(event, 'dblclick');
176 | else
177 | simulateMouseEvent(event, 'click');
178 | lastClickTime = event.timeStamp;
179 | } else {
180 | let endPos = getTouchCoords(event);
181 | if ((Math.abs(endPos.x - this._startPos.x) < 10) && (Math.abs(endPos.y - this._startPos.y) < 10)) {
182 |
183 | // If the touch interaction did not move, it should trigger a click
184 | if (!this._touchMoved || event.originalEvent.changedTouches[0].touchType === 'stylus') {
185 | // Simulate the click event
186 | simulateMouseEvent(event, 'click');
187 | }
188 | }
189 | }
190 |
191 | // Unset the flag to determine the touch movement stopped
192 | this._touchMoved = false;
193 |
194 | // Unset the flag to allow other widgets to inherit the touch event
195 | touchHandled = false;
196 | };
197 |
198 | let _touchStartBound;
199 | let _touchMoveBound;
200 | let _touchEndBound
201 |
202 | /**
203 | * A duck punch of the $.ui.mouse _mouseInit method to support touch events.
204 | * This method extends the widget with bound touch event handlers that
205 | * translate touch events to mouse events and pass them to the widget's
206 | * original mouse event handling methods.
207 | */
208 | mouseProto._mouseInit = function () {
209 |
210 | let self = this;
211 |
212 | // Microsoft Surface Support = remove original touch Action
213 | if ($.support.mspointer) {
214 | self.element[0].style.msTouchAction = 'none';
215 | }
216 |
217 | _touchStartBound = mouseProto._touchStart.bind(self);
218 | _touchMoveBound = mouseProto._touchMove.bind(self);
219 | _touchEndBound = mouseProto._touchEnd.bind(self);
220 |
221 | // Delegate the touch handlers to the widget's element
222 | self.element.on({
223 | touchstart: _touchStartBound,
224 | touchmove: _touchMoveBound,
225 | touchend: _touchEndBound
226 | });
227 |
228 | // Call the original $.ui.mouse init method
229 | _mouseInit.call(self);
230 | };
231 |
232 | /**
233 | * Remove the touch event handlers
234 | */
235 | mouseProto._mouseDestroy = function () {
236 |
237 | let self = this;
238 |
239 | // Delegate the touch handlers to the widget's element
240 | self.element.off({
241 | touchstart: _touchStartBound,
242 | touchmove: _touchMoveBound,
243 | touchend: _touchEndBound
244 | });
245 |
246 | // Call the original $.ui.mouse destroy method
247 | _mouseDestroy.call(self);
248 | };
249 |
250 | }));
251 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@rwap/jquery-ui-touch-punch",
3 | "version": "1.1.5",
4 | "description": "A duck punch for adding touch events to jQuery UI",
5 | "browser": "jquery.ui.touch-punch.js",
6 | "repository": "https://github.com/RWAP/jquery-ui-touch-punch.git",
7 | "author": "Rich M ",
8 | "contributors": [
9 | "Dave Furfero "
10 | ],
11 | "license": "MIT or GPL version 2",
12 | "dependencies": {
13 | "jquery-ui": ">=1.8"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------