JavaScript触摸设备拖放

126

我正在寻找一个适用于触摸设备的拖放插件。

我希望它具有类似于jQuery UI插件的功能,可以允许元素被“放置”在特定区域。

jqtouch插件支持拖动,但不支持放置。

这里有一个仅支持iPhone/iPad的拖放插件。

谁能指导我一个适用于安卓/苹果iOS的拖放插件?

...或者可能可以更新jqtouch插件以实现放置功能,因为它已经可以在Andriod和IOS上运行。

谢谢!


1
这还在进行中,但我不知道哪些设备支持它:http://plugins.jquery.com/project/mobiledraganddrop ... 你看过jQuerymobile.com库了吗?我还发现了这个重复的SO问题:https://dev59.com/tXA75IYBdhLWcg3wqK__ - iivel
谢谢。是的,mobiledraganddrop仅支持在移动设备上进行点击和拖放操作。我在jQuerymobile.com上找不到任何信息。另一个问题已经有一个标记为正确的答案,它提到了http://www.sencha.com/products/touch/,该产品具有有限的源代码许可证。 - joe
我很好奇有哪些可在移动和桌面浏览器上使用的方案。 - Lime
5个回答

268

您可以使用Jquery UI进行拖放操作,并使用将鼠标事件转换为触摸事件的附加库,这正是您所需要的。我推荐的库是https://github.com/furf/jquery-ui-touch-punch。使用它,您的Jquery UI拖放应该可以在触摸设备上正常工作。

或者,您可以使用以下代码,我也在使用它,它会将鼠标事件转换为触摸事件,像魔术一样工作。

function touchHandler(event) {
    var touch = event.changedTouches[0];

    var simulatedEvent = document.createEvent("MouseEvent");
        simulatedEvent.initMouseEvent({
        touchstart: "mousedown",
        touchmove: "mousemove",
        touchend: "mouseup"
    }[event.type], true, true, window, 1,
        touch.screenX, touch.screenY,
        touch.clientX, touch.clientY, false,
        false, false, false, 0, null);

    touch.target.dispatchEvent(simulatedEvent);
    event.preventDefault();
}

function init() {
    document.addEventListener("touchstart", touchHandler, true);
    document.addEventListener("touchmove", touchHandler, true);
    document.addEventListener("touchend", touchHandler, true);
    document.addEventListener("touchcancel", touchHandler, true);
}
在document.ready中调用init()函数即可。
这里找到的代码。

11
感谢您,我在我的Android设备上现在可以拖动东西了。然而,当我点击它时,点击事件不再触发。有任何解决方法吗? - John Landheer
9
注意:由于事件监听器是为整个文档注册的,因此此解决方案还会禁用我的 Nexus S 上的滚动... - Ethan Leroy
30
谢谢分享。然而,你应该给发布的代码的原作者以功劳。http://ross.posterous.com/2008/08/19/iphone-touch-events-in-javascript/ - undefined
3
@JohnLandheer 我通过仅将事件侦听器附加到我想要可拖动的对象上来解决了这个问题,document.getElementById('draggableContainer').addEventListener(... - chiliNUT
1
Touch-punch很棒。它是那种“它只是工作”的东西之一。不幸的是,它不支持双触摸...我很想看到它。 - DA.
显示剩余14条评论

21

对于任何想要使用这个并保留“点击”功能(正如John Landheer在他的评论中提到的那样)的人,您只需进行一些修改即可实现:

添加一些全局变量:

var clickms = 100;
var lastTouchDown = -1;

然后将原始的switch语句修改为以下内容:

var d = new Date();
switch(event.type)
{
    case "touchstart": type = "mousedown"; lastTouchDown = d.getTime(); break;
    case "touchmove": type="mousemove"; lastTouchDown = -1; break;        
    case "touchend": if(lastTouchDown > -1 && (d.getTime() - lastTouchDown) < clickms){lastTouchDown = -1; type="click"; break;} type="mouseup"; break;
    default: return;
}

您可能希望根据自己的喜好调整 'clickms'。基本上,它只是监视 'touchstart' 紧接着 'touchend',以模拟点击。


@E. Z. Hart - 请问您将代码放在以上给出的代码中的哪个位置以启用点击事件? - Valay
1
@ChakoDesai - 我的回答发布后,答案中的代码已经被大幅修改。请查看http://stackoverflow.com/posts/6362527/revisions并查看2011年6月30日的代码版本;基于那个版本,我的回答将更加合理。 - E.Z. Hart
@E. Z. Hart - 我已经从给定的URL更新了我的代码。但是为什么有时候“click”起作用,有时候不起作用??? - Valay
@ChakoDesai - 没有看到你的代码,我不能确定。你可能想要为此打开一个新问题。 - E.Z. Hart
可以在这里找到一个更新的答案,使上面的代码能够与点击事件一起工作:http://stackoverflow.com/questions/28218888/touch-event-handler-overrides-click-handlers - Blunderfest

12

谢谢以上的代码!-我尝试了几个选项,这是正确的选择。我遇到了一个问题,就是preventDefault会阻止iPad上的滚动-我现在正在测试可拖动项目,目前效果非常好。

if (event.target.id == 'draggable_item' ) {
    event.preventDefault();
}

你在Windows Phone上测试过吗?或者除了iPhone之外的其他设备?根据过去的经验,这似乎在iPhone / iPad上运行得非常完美,但在其他设备上,特别是在Windows Phone上表现不佳。 - almost a beginner

10

我和gregpress的答案是一样的解决方案,但我的可拖动元素使用的是类而不是id。看起来它能够工作。

var $target = $(event.target);  
if( $target.hasClass('draggable') ) {  
    event.preventDefault();  
}

4
你仅仅使用了一个不同的选择器,并不能成为一个单独的答案,相反你应该在@gregpress的回答下进行评论。 - bPratik
2
@bPratik,除非他想要更好的格式化答案提供方式,以及他提供了整个代码示例,这确实值得一个单独的回答。不过应该留下评论链接到他的答案... - drzaus

8

虽然这是一个旧帖子,但是......

@ryuutatsuo的答案存在问题,它会阻止任何需要“点击”(例如输入框)或与之互动的元素,所以我写了这个解决方案。这个解决方案使得在任何触摸设备(或计算机)上使用基于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等输入元素,它就会跳过翻译;或者如果它附加了标准的鼠标事件,它也会跳过翻译。

结果: 可拖动元素上的每个元素仍然可以正常工作。

愉快的编码,问候, Erwin Haantjes


simulateTouchEvents(<object>, true); 中我应该传递哪个对象? - Valay
simulateTouchEvents($('#yourid'), true); 或 simulateTouchEvents(document.getElementById('yourid'), true); 等。 - Codebeat
你能否请看一下我的另一个问题链接 - Valay
使用http://threedubmedia.com/code/event/drag并禁用元素的x或y移动。当您在本文中使用代码时,可以“滚动”滚动条。但是要知道为什么要这样做,因为手机上的页面已经有了滚动条。不明白你想做什么。祝你成功! - Codebeat
我需要添加列的拖放和滚动功能。当我在Chrome中启用“触摸”事件时,我无法滚动表格。即使在移动设备上也是如此。 - Valay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接