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

发布于 2024-11-03 15:10:33 字数 669 浏览 1 评论 0原文

我们有一个纸牌游戏网站,它广泛使用 jQuery Draggable & jQuery。可放置并且几乎完美地工作(使用鼠标时)近一年了。

我们真的很希望该网站能够在触摸屏设备上运行,但我们似乎无法让 jQuery 的拖放功能(尤其是拖放功能)可靠地工作。

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

光是掉下来似乎并没有什么作用。

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

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

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

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

那里有人有什么想法吗?我们会满足于仅支持 iPad 的答案,但它确实不需要我们重构现有网站。

谢谢!

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.

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.

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. 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.

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.

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.

BTW, the functionality we're looking for is clearly technically possible because the YUI libraries for drag and drop work as expected. Unfortunatly, we can't justtify refactoring the site to switch from jQuery to 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.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

女中豪杰 2024-11-10 15:10:33

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

早上给我打电话;)(这真的很傲慢,我没有写这个解决方案,尽管我希望我有,如果我记得在哪里找到它,我会引用它,如果有人知道这段代码来自哪里,请发表评论并注明出处)

更新:在这里:

Paste this at the beginning of your .js file:

(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

戏剧牡丹亭 2024-11-10 15:10:33

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

I suggest jQuery UI Touch Punch. I've tested it on iOS 5 and Android 2.3 and it works great on both.

于我来说 2024-11-10 15:10:33

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

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

我已经在多个设备上进行了测试,它运行速度很快(与 ThreeDubMedia 的拖放功能结合使用(另请参阅 http://tridubmedia.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;
}; 

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

结果:
可拖动元素上的每个元素仍然有效。

快乐编码,问候,
欧文·汉杰斯

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. 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). 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)). It is a jQuery solution so you can use it only with jQuery libs. 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).

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.

Result:
Every element on a draggable element is still working.

Happy coding, greetz,
Erwin Haantjes

任谁 2024-11-10 15:10:33

我根据 Erwinus 的回答创建了一个 jQuery 插件: https://github.com/RushPL/jquery .触摸鼠标

I have created a jQuery plugin based on Erwinus' answer: https://github.com/RushPL/jquery.touch-mouse

野生奥特曼 2024-11-10 15:10:33

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

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

但是android仍在寻找解决方案。

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

You can try this plugin but seems to work for iphone, ipod and ipad. Not sure if solves you're problem. Can be a specific ...

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

But android still looking for a solution.

Let me know if it helps. Regards Ricardo Rodrigues

极致的悲 2024-11-10 15:10:33

LikWidT 从引用的网站中可以轻松找到最简单的解决方案。如果任何 JQuery UI 程序员可以添加此代码或类似的代码到他们的包中,那就太好了。 Jquery 无疑是为 Web 开发人员构建拖放内容的最简单的包……不幸的是,现在几乎所有设备都具有触摸屏,这是一个主要缺陷。我现在正在 Surface Pro 上进行编码,这让我对这些问题有了更深入的了解。

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

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

LikWidT has the simplest solution hands down from the website referenced. It would be great if any of the JQuery UI programmers could add this code or similar to their package. 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. I am now coding on a Surface Pro which is opening my eyes to these issues personally.

Just to reference the website again:
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. Then I just copy/pasted below the include files the Javascript code in the post above and that was it. 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.

不弃不离 2024-11-10 15:10:33

Given为我工作:

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

Given worked for me :

eventAfterRender: function (event, element, view ) {
         element.draggable();
},
决绝 2024-11-10 15:10:33

在 Android 6.13 / 三星 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 ;

} 

Tested on HTC One M8 under Android 6.13 / samsung Galaxy tab S2

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 ;

} 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文