【问题标题】:jQuery Drag and Drop on touch devices (iPad, Android)触摸设备(iPad、Android)上的 jQuery 拖放
【发布时间】:2011-08-13 08:36:04
【问题描述】:

我们有一个纸牌游戏网站,它广泛使用了 jQuery Draggable & Droppable,并且在将近一年的时间里(使用鼠标时)几乎完美无缺。

我们真的很想让网站在触摸屏设备上运行,但我们似乎无法让 jQuery 的拖放功能可靠地运行。

拖动“正常”,除非被拖动的 div 位于另一个具有任何偏移、边距、填充等的 dom 元素内。如果是这样,则拖动的元素也会从用户的手指偏移类似的量。听起来可能没什么大不了,但它使界面无法使用。

丢弃似乎不起作用。

我们已经研究了这里提供的关于 SO 的各种选项(如果可以的话,将尝试用其中一些链接来更新这篇文章),但没有一个对我们有用。

我们还研究了 jQuery Mobile,但它仍处于 alpha 阶段,即便如此,它似乎更像是一个让网站模拟手机 UI 而非我们正在寻找的 UI 的框架。

大多数 SO 和 Google 关于这个主题的帖子似乎在 2010 年末都消失了,这让我觉得有一个明显的答案,也许我们只是错过了。

顺便说一句,我们正在寻找的功能在技术上显然是可行的,因为用于拖放的 YUI 库按预期工作。不幸的是,我们不能证明重构网站以从 jQuery 切换到 YUI。

有人有什么想法吗?我们会接受仅支持 iPad 的答案,但实际上不需要我们重构现有网站。

谢谢!

【问题讨论】:

标签: jquery android jquery-ui touch ipod-touch


【解决方案1】:

将此粘贴​​到 .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);

早上打电话给我;)(这真的很自大,我没有写这个解决方案,虽然我希望我有,如果我记得我在哪里找到它,我会参考它,如果有人知道这段代码来自哪里请评论并注明该人)

更新:给你:This is where I found this

【讨论】:

  • 除了带轨迹球的 BB 外,大多数情况下都适用
  • 很好,但是你能告诉我为什么这会破坏我的双击吗? jquery .dblclick()
  • 可能是因为模拟鼠标事件函数在多点触控事件上触发 event.preventDefault...也许您可以尝试创建一个 if 语句,使您的 dblclick 事件异常?
  • @iurisilvio 根据消息来源这实际上 jquery.touch-punch.js
  • 不是最好的解决方案:如果要拖动的元素占据整个视口,使用 jQuery UI Touch Punch 会阻止文档滚动。甚至可能是您希望仅水平拖动元素且容器隐藏溢出的问题,作为可拖动的轮播。在这种情况下,轮播会阻止用户上下滚动文档。我更喜欢 jQuery Touch (github.com/ajlkn/jquery.touch),它使得可拖动可能仅在移动设备上触摸(鼠标手势可以被过滤)并且不会阻止触摸事件传播(因此,要滚动的文档)。
【解决方案2】:

我建议jQuery UI Touch Punch。我已经在 iOS 5 和 Android 2.3 上对其进行了测试,并且两者都运行良好。

【讨论】:

  • 但在拖动开始之前它有一点滞后/延迟(在 Chrome Andriod 上测试)
  • 在 android 设备上不能正常工作范围滑块,请帮助我
  • 不是最好的解决方案:如果要拖动的元素占据整个视口,使用 jQuery UI Touch Punch 会阻止文档滚动。甚至可能是您希望仅水平拖动的元素的问题,其中容器隐藏溢出,作为可拖动的轮播。在这种情况下,轮播会阻止用户上下滚动文档。我更喜欢 jQuery Touch (github.com/ajlkn/jquery.touch),它使得可拖动可能仅在移动设备上触摸(鼠标手势可以被过滤)并且不会阻止触摸事件传播(因此,要滚动的文档)。
【解决方案3】:

我知道的旧线程......

@likwid_t 的答案的问题是它还阻止了任何必须对“点击”做出反应的输入或其他元素(例如输入),所以我编写了这个解决方案。该解决方案可以在任何触摸设备(或计算机)上使用基于 mousedown、mousemove 和 mouseup 事件的任何现有拖放库。这也是一种跨浏览器的解决方案。

我已经在多种设备上进行了测试,它运行速度很快(结合 ThreeDubMedia 的拖放功能(另请参阅 http://threedubmedia.com/code/event/drag))。它是一个 jQuery 解决方案,因此您只能将它与 jQuery 库一起使用。我使用了 jQuery 1.5.1,因为一些 较新的函数在 IE9 及更高版本中无法正常工作(未使用较新版本的 jQuery 进行测试)。

之前向事件添加任何拖放操作您必须先调用此函数

simulateTouchEvents(<object>);

您还可以使用以下语法阻止所有组件/子组件进行输入或加速事件处理:

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

这是我写的代码。我使用了一些不错的技巧来加快评估速度(参见代码)。

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;
}; 

它的作用: 首先,它将单点触摸事件转换为鼠标事件。它检查事件是否由必须拖动的元素上/中的元素引起。如果它是一个输入元素,如 input、textarea 等,它会跳过翻译,或者如果附加了标准鼠标事件,它也会跳过翻译。

结果: 可拖动元素上的每个元素仍在工作。

快乐的编码,greetz, 欧文·汉杰斯

【讨论】:

  • 您的代码很有帮助。我已经将它与 FullCalendar 一起使用,但不是开箱即用的。我做到了,只有在你按住半秒钟后才能模拟鼠标。那是滚动仍然有效。我希望你把这段代码放在 Github 上,以便我做出贡献。我认为它也可以使用一些改进..例如“b”..变量正在为冗长的名称而哭泣。 :-) 但是,嘿,它有效!非常感谢。
  • 我知道详细名称很重要,但我在此代码中使用“b”作为多用途布尔值(b 来自布尔值)以检查某些条件以使其更好地阅读并限制临时变量。但无论如何感谢您的贡献。
【解决方案4】:

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

【讨论】:

  • 感谢您的贡献,但点击链接时出现错误:“存储库暂时不可用。”
【解决方案5】:

你可以试试这个插件,但似乎适用于 iphone、ipod 和 ipad。不确定是否能解决你的问题。可以是特定的...

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

但android仍在寻找解决方案。

如果有帮助,请告诉我。问候里卡多·罗德里格斯

【讨论】:

  • 发现这解决了android问题。但在旧版本中仍然是一个问题..例如 2.1 android HTC Hero。估计要慢。
【解决方案6】:

LikWidT 提供了参考网站中最简单的解决方案。如果任何 JQuery UI 程序员可以将此代码或类似代码添加到他们的包中,那就太好了。 Jquery 无疑是为 Web 开发人员构建拖放内容的最简单的包......不幸的是,这是当今几乎所有具有触摸屏的设备的主要缺陷。我现在正在使用 Surface Pro 进行编码,这让我对这些问题大开眼界。

只是再次引用该网站: https://github.com/furf/jquery-ui-touch-punch

编辑:同时澄清这个修复是多么简单。我必须首先确保我重新排序了我的包含文件,以便首先是 JQuery Javascript 文件,然后是 Jquery UI Javascript,然后是我的 CSS 文件。然后我只是将上面帖子中的 Javascript 代码复制/粘贴到包含文件下方,仅此而已。我没有修改任何代码,它只是一个实时查找任何触摸并将它们转换为等效鼠标操作的函数。因此,我对 JQuery 编码器的上述断言。

【讨论】:

    【解决方案7】:

    鉴于为我工作:

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

    【讨论】:

      【解决方案8】:

      在 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 ;
      
      } 
      

      【讨论】:

        猜你喜欢
        • 2011-03-23
        • 2012-11-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-08
        • 1970-01-01
        • 1970-01-01
        • 2012-08-02
        相关资源
        最近更新 更多