简体   繁体   English

触摸设备上的jQuery拖放(iPad,Android)

[英]jQuery Drag and Drop on touch devices (iPad, Android)

We have a card game website that makes extensive use of jQuery Draggable & Droppable and which has worked nearly flawlessly (when using a mouse) for almost a year. 我们有一个卡片游戏网站,它广泛使用jQuery Draggable和Droppable,它使用了近一年(当使用鼠标时)几乎完美无缺。

We would REALLY like to have the site work on touch screen devices, but we cannot seem to get jQuery's drag and especially drop functionality to work reliably. 我们真的很想让网站在触摸屏设备上运行,但我们似乎无法获得jQuery的拖拽,尤其是降低功能以便可靠地工作。

Dragging works "ok" unless the div being dragged is inside another dom element with any kind of offset, margin, padding, etc. If it is, the dragged element is also offset from the user's finger by a similar amount. 拖动工作“ok”,除非被拖动的div位于具有任何类型的偏移,边距,填充等的另一个dom元素内。如果是,则拖动的元素也从用户的手指偏移相似的量。 May not sound like a big deal, but it makes the interface unusuable. 可能听起来不是什么大不了的事,但它使界面变得不可用。

Dropping just doesn't seem to work. 掉线似乎不起作用。

We've researched various options presented here on SO (will try to update this post with links to some of them if I can), but none work for us. 我们已经研究了SO上提供的各种选项(如果可以的话,会尝试更新这篇文章的链接,但是没有一个适用于我们)。

We've also researched jQuery Mobile but this is still in alpha and even so seems to be more of a framework for making a site emulate the UI of a phone vs what we're looking for. 我们还研究过jQuery Mobile,但它仍处于alpha状态,甚至更像是一个框架,使网站模拟手机的UI与我们正在寻找的。

Most of the SO and Google posts on this topic seem to trail off in late 2010 which makes me think there is an obvious answer that maybe we're just missing. 关于这一主题的大多数SO和谷歌帖子似乎都在2010年末落后,这让我觉得有一个明显的答案,也许我们只是遗漏了。

BTW, the functionality we're looking for is clearly technically possible because the YUI libraries for drag and drop work as expected. 顺便说一句,我们正在寻找的功能在技术上显而易见,因为用于拖放的YUI库按预期工作。 Unfortunatly, we can't justtify refactoring the site to switch from jQuery to YUI. 不幸的是,我们不能仅仅重构网站以从jQuery切换到YUI。

Anyone out there have any ideas? 那里有人有什么想法吗? We would settle for a answer that supports only iPad, but it really needs to not require we refactor the existing site. 我们会满足于只支持iPad的答案,但它确实不需要我们重构现有网站。

Thanks! 谢谢!

Paste this at the beginning of your .js file: 将其粘贴到.js文件的开头:

(function ($) {
    // Detect touch support
    $.support.touch = 'ontouchend' in document;
    // Ignore browsers without touch support
    if (!$.support.touch) {
    return;
    }
    var mouseProto = $.ui.mouse.prototype,
        _mouseInit = mouseProto._mouseInit,
        touchHandled;

    function simulateMouseEvent (event, simulatedType) { //use this function to simulate mouse event
    // Ignore multi-touch events
        if (event.originalEvent.touches.length > 1) {
        return;
        }
    event.preventDefault(); //use this to prevent scrolling during ui use

    var touch = event.originalEvent.changedTouches[0],
        simulatedEvent = document.createEvent('MouseEvents');
    // Initialize the simulated mouse event using the touch event's coordinates
    simulatedEvent.initMouseEvent(
        simulatedType,    // type
        true,             // bubbles                    
        true,             // cancelable                 
        window,           // view                       
        1,                // detail                     
        touch.screenX,    // screenX                    
        touch.screenY,    // screenY                    
        touch.clientX,    // clientX                    
        touch.clientY,    // clientY                    
        false,            // ctrlKey                    
        false,            // altKey                     
        false,            // shiftKey                   
        false,            // metaKey                    
        0,                // button                     
        null              // relatedTarget              
        );

    // Dispatch the simulated event to the target element
    event.target.dispatchEvent(simulatedEvent);
    }
    mouseProto._touchStart = function (event) {
    var self = this;
    // Ignore the event if another widget is already being handled
    if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
        return;
        }
    // Set the flag to prevent other widgets from inheriting the touch event
    touchHandled = true;
    // Track movement to determine if interaction was a click
    self._touchMoved = false;
    // Simulate the mouseover event
    simulateMouseEvent(event, 'mouseover');
    // Simulate the mousemove event
    simulateMouseEvent(event, 'mousemove');
    // Simulate the mousedown event
    simulateMouseEvent(event, 'mousedown');
    };

    mouseProto._touchMove = function (event) {
    // Ignore event if not handled
    if (!touchHandled) {
        return;
        }
    // Interaction was not a click
    this._touchMoved = true;
    // Simulate the mousemove event
    simulateMouseEvent(event, 'mousemove');
    };
    mouseProto._touchEnd = function (event) {
    // Ignore event if not handled
    if (!touchHandled) {
        return;
    }
    // Simulate the mouseup event
    simulateMouseEvent(event, 'mouseup');
    // Simulate the mouseout event
    simulateMouseEvent(event, 'mouseout');
    // If the touch interaction did not move, it should trigger a click
    if (!this._touchMoved) {
      // Simulate the click event
      simulateMouseEvent(event, 'click');
    }
    // Unset the flag to allow other widgets to inherit the touch event
    touchHandled = false;
    };
    mouseProto._mouseInit = function () {
    var self = this;
    // Delegate the touch handlers to the widget's element
    self.element
        .on('touchstart', $.proxy(self, '_touchStart'))
        .on('touchmove', $.proxy(self, '_touchMove'))
        .on('touchend', $.proxy(self, '_touchEnd'));

    // Call the original $.ui.mouse init method
    _mouseInit.call(self);
    };
})(jQuery);

Call me in the morning ;) (that's really arrogant, I didn't write this solution although I wish that I had, I'd reference it if I remember where I found it, if anyone know where this code came from please comment and credit that person) 早上打电话给我;)(这真的很傲慢,我没有写这个解决方案,虽然我希望我有,如果我记得我找到它的话我会参考它,如果有人知道这个代码的来源请注释和相信那个人)

UPDATE: Here you go: This is where I found this 更新:你走了: 这是我发现的地方

I suggest jQuery UI Touch Punch . 我建议使用jQuery UI Touch Punch I've tested it on iOS 5 and Android 2.3 and it works great on both. 我已经在iOS 5和Android 2.3上测试了它,它在两者上都很好用。

Old thread I know....... 旧线程我知道.......

Problem with the answer of @likwid_t is that it blocks also any input or other element that has to react on 'clicks' (for example inputs), so i wrote this solution. @likwid_t答案的问题在于它还会阻止任何输入或其他必须对“点击”做出反应的元素(例如输入),所以我写了这个解决方案。 This solution made it possible to use any existing drag and drop library that is based upon mousedown, mousemove and mouseup events on any touch device (or cumputer). 该解决方案使得可以在任何触摸设备(或cumputer)上使用基于mousedown,mousemove和mouseup事件的任何现有拖放库。 This is also a cross-browser solution. 这也是一种跨浏览器的解决方案。

I have tested in on several devices and it works fast (in combination with the drag and drop feature of ThreeDubMedia (see also http://threedubmedia.com/code/event/drag )). 我已经在几个设备上进行了测试,它运行速度很快(结合ThreeDubMedia的拖放功能(另请参阅http://threedubmedia.com/code/event/drag ))。 It is a jQuery solution so you can use it only with jQuery libs. 它是一个jQuery解决方案,因此您只能将它用于jQuery库。 I have used jQuery 1.5.1 for it because some newer functions don't work properly with IE9 and above (not tested with newer versions of jQuery). 我已经使用了jQuery 1.5.1 ,因为一些较新的函数在IE9及更高版本中无法正常工作 (未使用较新版本的jQuery进行测试)。

Before you add any drag or drop operation to an event you have to call this function first : 向事件添加任何拖放操作之前, 必须先调用此函数

simulateTouchEvents(<object>);

You can also block all components/children for input or to speed up event handling by using the following syntax: 您还可以使用以下语法阻止所有组件/子项以进行输入或加速事件处理:

simulateTouchEvents(<object>, true); // ignore events on childs

Here is the code i wrote. 这是我写的代码。 I used some nice tricks to speed up evaluating things (see code). 我使用了一些很好的技巧来加速评估事物(见代码)。

function simulateTouchEvents(oo,bIgnoreChilds)
{
 if( !$(oo)[0] )
  { return false; }

 if( !window.__touchTypes )
 {
   window.__touchTypes  = {touchstart:'mousedown',touchmove:'mousemove',touchend:'mouseup'};
   window.__touchInputs = {INPUT:1,TEXTAREA:1,SELECT:1,OPTION:1,'input':1,'textarea':1,'select':1,'option':1};
 }

$(oo).bind('touchstart touchmove touchend', function(ev)
{
    var bSame = (ev.target == this);
    if( bIgnoreChilds && !bSame )
     { return; }

    var b = (!bSame && ev.target.__ajqmeclk), // Get if object is already tested or input type
        e = ev.originalEvent;
    if( b === true || !e.touches || e.touches.length > 1 || !window.__touchTypes[e.type]  )
     { return; } //allow multi-touch gestures to work

    var oEv = ( !bSame && typeof b != 'boolean')?$(ev.target).data('events'):false,
        b = (!bSame)?(ev.target.__ajqmeclk = oEv?(oEv['click'] || oEv['mousedown'] || oEv['mouseup'] || oEv['mousemove']):false ):false;

    if( b || window.__touchInputs[ev.target.tagName] )
     { return; } //allow default clicks to work (and on inputs)

    // https://developer.mozilla.org/en/DOM/event.initMouseEvent for API
    var touch = e.changedTouches[0], newEvent = document.createEvent("MouseEvent");
    newEvent.initMouseEvent(window.__touchTypes[e.type], true, true, window, 1,
            touch.screenX, touch.screenY,
            touch.clientX, touch.clientY, false,
            false, false, false, 0, null);

    touch.target.dispatchEvent(newEvent);
    e.preventDefault();
    ev.stopImmediatePropagation();
    ev.stopPropagation();
    ev.preventDefault();
});
 return true;
}; 

What it does: At first, it translates single touch events into mouse events. 它的作用:首先,它将单个触摸事件转换为鼠标事件。 It checks if an event is caused by an element on/in the element that must be dragged around. 它检查事件是否由必须拖动的元素上/中的元素引起。 If it is an input element like input, textarea etc, it skips the translation, or if a standard mouse event is attached to it it will also skip a translation. 如果它是输入元素,例如input,textarea等,它会跳过翻译,或者如果附加了标准鼠标事件,它也会跳过翻译。

Result: Every element on a draggable element is still working. 结果:可拖动元素上的每个元素仍然有效。

Happy coding, greetz, Erwin Haantjes 快乐的编码,greetz,Erwin Haantjes

我根据Erwinus的答案创建了一个jQuery插件: https//github.com/RushPL/jquery.touch-mouse

LikWidT has the simplest solution hands down from the website referenced. LikWidT从引用的网站上获得最简单的解决方案。 It would be great if any of the JQuery UI programmers could add this code or similar to their package. 如果任何JQuery UI程序员可以将这些代码或类似代码添加到他们的包中,那就太好了。 Jquery is certainly the simplest package to build drag/drop content for web developers... this is unfortunately a major flaw with nearly all devices having touch screens nowdays. Jquery当然是为Web开发人员构建拖放内容的最简单的软件包......不幸的是,这是几乎所有设备都具有触摸屏的一个主要缺陷。 I am now coding on a Surface Pro which is opening my eyes to these issues personally. 我现在正在使用Surface Pro进行编码,这可以让我亲眼看到这些问题。

Just to reference the website again: https://github.com/furf/jquery-ui-touch-punch 只是再次引用该网站: https//github.com/furf/jquery-ui-touch-punch

Edit: Also to clarify how simple this fix is. 编辑:也是为了澄清这个修复是多么简单。 I had to first ensure I re-ordered my include files such that JQuery Javascript file was first then Jquery UI Javascript then my CSS Files. 我必须首先确保我重新订购我的包含文件,以便JQuery Javascript文件首先是Jquery UI Javascript然后我的CSS文件。 Then I just copy/pasted below the include files the Javascript code in the post above and that was it. 然后我只是在上面的帖子中复制/粘贴包含文件Javascript代码,就是这样。 I modified no code it is simply a function that looks for any touches in real time and converts them to equivalent mouse actions. 我没有修改代码它只是一个实时查找任何触摸并将它们转换为等效鼠标操作的函数。 Hence my above assertions for JQuery coders. 因此我对JQuery编码器的上述断言。

Given worked for me : 鉴于我的工作:

eventAfterRender: function (event, element, view ) {
         element.draggable();
},

Tested on HTC One M8 under Android 6.13 / samsung Galaxy tab S2 在Android 6.13 / samsung Galaxy tab S2下测试HTC One M8

function simulateMouseEvent (event, simulatedType) { //use this function to simulate mouse event

// restriction to preserve input use
    window.__touchInputs = {INPUT:1,TEXTAREA:1,SELECT:1,OPTION:1,'input':1,'textarea':1,'select':1,'option':1};
    if( window.__touchInputs[event.target.tagName] ) return ;

} 

You can try this plugin but seems to work for iphone, ipod and ipad. 你可以尝试这个插件,但似乎适用于iPhone,iPod和iPad。 Not sure if solves you're problem. 不确定是否解决了你的问题。 Can be a specific ... 可以是具体的......

http://code.google.com/p/jquery-ui-for-ipad-and-iphone/ http://code.google.com/p/jquery-ui-for-ipad-and-iphone/

But android still looking for a solution. 但android仍在寻找解决方案。

Let me know if it helps. 如果有帮助,请告诉我。 Regards Ricardo Rodrigues 关心里卡多罗德里格斯

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM