Skip to main content

Search and Top Navigation

#14926 closed bug (duplicate)

Opened March 01, 2016 04:51PM UTC

Closed March 02, 2016 05:01PM UTC

Last modified March 04, 2016 04:06PM UTC

ui.mouse support for touch events

Reported by: gvanmat Owned by:
Priority: minor Milestone: none
Component: ui.mouse Version: 1.12.0-beta.1
Keywords: Cc:
Blocked by: Blocking:
Description

The ui.mouse headless component is used by a number of other component features (draggable.js, sortable.js, resizable.js). This component only distributes mouse events. This falls short when used in a hybrid app.

The proposal is to utilize touch events when the browser supports them instead of mouse events. To minimize change, the touch events are normalized as native mouse events. The last touch point is pulled up to a corresponding simulated mouse event. The native simulated event is wrapped in a jquery event object. The simulated mouse events are passed to the placeholder methods but the native event is not dispatched.

Consider the following mapping:

{'touchstart': 'mousedown', 'touchmove': 'mousemove', 'touchend': 'mouseup'}

Attachments (0)
Change History (4)

Changed March 01, 2016 06:28PM UTC by gvanmat comment:1

Created pull request with the proposed fix: https://github.com/gvanmat/jquery-ui/pull/1

Changed March 01, 2016 09:34PM UTC by gvanmat comment:2

Closed the last pull as it didn't pass lint or qunit tests. Please consider the following pull request: https://github.com/gvanmat/jquery-ui/pull/2

Changed March 02, 2016 05:01PM UTC by scottgonzalez comment:3

resolution: → duplicate
status: newclosed

Duplicate of #4143.

Changed March 04, 2016 04:00PM UTC by scottgonzalez comment:4

_comment0: I used the following duck punch workaround: \ \ var _mouseProto = $.ui["mouse"].prototype ? $.ui["mouse"].prototype : {}.prototype; \ var _mouseInit = _mouseProto["_mouseInit"]; \ var _mouseDestroy = _mouseProto["_mouseDestroy"]; \ \ function _wrapTouch(event) \ { \ if (["touchstart", "touchmove", "touchend", "touchcancel"].indexOf(event["type"]) === -1) \ { \ return event; \ } \ \ var _getFirstTouchPoint = function(evt) \ { \ if (evt["changedTouches"] && evt["changedTouches"].length) \ { \ return evt["changedTouches"][0]; \ } \ else if (evt["targetTouches"] && evt["targetTouches"].length) \ { \ return evt["targetTouches"][0]; \ } \ else if (evt["touches"] && evt["touches"].length) \ { \ return evt["touches"][0]; \ } \ return null; \ }; \ \ var _TOUCH_TO_MOUSE_XREF = {"touchstart": "mousedown", "touchmove": "mousemove", "touchend": "mouseup", "touchcancel": "mouseup"}; \ var _COPY_SAFE_EVENT_PROPERTIES = {"altKey": true, "bubbles": true, "cancelable": true, "ctrlKey": true, \ "currentTarget": true, "eventPhase": true, "metaKey": true, "target": true, \ "relatedTarget": true, "shiftKey": true, "timeStamp": true, \ "view": true, "which": true, "button": true, "buttons": true, "clientX": true, \ "clientY": true, "offsetX": true, "offsetY": true, "pageX": true, \ "pageY": true, "screenX": true, "screenY": true, "toElement": true, \ "char": true, "charCode": true, "key": true, "keyCode": true}; \ var simulatedEvent = document.createEvent("MouseEvent"); \ var touch = _getFirstTouchPoint(event["originalEvent"]); \ simulatedEvent.initMouseEvent(_TOUCH_TO_MOUSE_XREF[ event["type"] ], true, true, window, 1, \ touch["screenX"], touch["screenY"], touch["clientX"], touch["clientY"], false, \ false, false, false, 0, null); \ \ // Capture all interesting event properties. Similar to jQuery.event.fix. \ var props = {}; \ if (Object.defineProperty) \ { \ var descriptor = {"value": touch["target"], "writable": false, "enumerable": true, "configurable": false}; \ Object.defineProperty(simulatedEvent, "target", descriptor); \ } \ \ for (var key in simulatedEvent) \ { \ if (_COPY_SAFE_EVENT_PROPERTIES[ key ] && !$.isFunction(simulatedEvent[ key ])) \ { \ props[ key ] = simulatedEvent[ key ]; \ } \ } \ if (!props["target"]) \ props["target"] = touch["target"]; \ \ // Wrap a native simulated event in a jQuery.Event extending propertiea \ var dragEvent = $.Event(simulatedEvent, props); \ return dragEvent; \ }; \ \ _mouseProto._touchStartDelegate = function(event) \ { \ if (this["_mouseDown"](_wrapTouch(event))) \ { \ this._on(this.document, {"touchmove": this._touchMoveDelegate}); \ this._on(this.document, {"touchend": this._touchEndDelegate, "touchcancel": this._touchEndDelegate}); \ } \ }; \ \ _mouseProto._touchMoveDelegate = function(event) \ { \ var e = _wrapTouch(event); \ if (this["_mouseCapture"](e)) \ { \ event.preventDefault(); // prevent browser scroll \ this["_mouseMove"](_wrapTouch(e)); \ } \ }; \ \ _mouseProto._touchEndDelegate = function(event) \ { \ this._off(this.document, "touchmove touchend touchcancel"); \ this["_mouseUp"](_wrapTouch(event)); \ }; \ \ _mouseProto["_mouseInit"] = function() \ { \ this._on(this.element, {"touchstart": this._touchStartDelegate}); \ _mouseInit.call(this); \ }; \ \ _mouseProto["_mouseDestroy"] = function() \ { \ this._off(this.element, "touchstart"); \ this._off(this.document, "touchmove touchend touchcancel"); \ _mouseDestroy.call(this); \ };1457107587066326

I used the following duck punch workaround:

NOTE: Code removed from comment. Use http://touchpunch.furf.com/ for now.