web-dev-qa-db-ja.com

スワイプ時にタッチスタートを防ぐ

モバイルデバイスにスクロール可能なリストがあります。彼らは、人々がスワイプでリストをスクロールできるようにしたり、タップして行を選択したりすることを望んでいます。

キャッチは2つを組み合わせています。実際にリストをスクロールしている場合、行を選択したくありません。私が見つけたものは次のとおりです。

スクロールしてもトリガーされません:

  • クリック
  • マウスアップ

スクロール時にトリガーします:

  • マウスダウン
  • タッチスタート
  • タッチエンド

単純な解決策は、クリックイベントに固執することです。ただし、特定のブラックベリーデバイスでは、タッチスタートとクリックまたはマウスアップのトリガーとの間に非常に顕著な遅延があることがわかっています。この遅延は、それらのデバイスで使用できなくなるほど重要です。

そのため、他のオプションが残ります。ただし、これらのオプションを使用すると、タッチした行をトリガーせずにリストをスクロールして、スクロールを開始できます。

これを解決するためのここでのベストプラクティスは何ですか?

39
DA.
var touchmoved;
$('button').on('touchend', function(e){
    if(touchmoved != true){
        // button click action
    }
}).on('touchmove', function(e){
    touchmoved = true;
}).on('touchstart', function(){
    touchmoved = false;
});
45

基本的にあなたがしたいことは、何がスワイプで何がクリックであるかを検出することです。

いくつかの条件を設定できます。

  1. スワイプは、ポイントp1をタッチしてから、指を画面p2に移動したまま、画面に指を置いてから離すことです。
  2. クリックは、同じ要素をタップして開始と終了をタップしたときです。

したがって、touchStartが発生した場所の座標を保存すると、touchEndで差を測定できます。変更が十分に大きい場合はスワイプと見なし、そうでない場合はクリックと見なします。

また、本当にきれいにしたい場合は、touchMoveの間に指で「ホバリング」している要素を検出することもできます。また、まだ開始した要素にいない場合は、クリックすると、ハイライトなどを削除するclickCancelメソッドを実行できます。

// grab an element which you can click just as an example
var clickable = document.getElementById("clickableItem"),
// set up some variables that we need for later
currentElement,
clickedElement;

// set up touchStart event handler
var onTouchStart = function(e) {
    // store which element we're currently clicking on
    clickedElement = this;
    // listen to when the user moves finger
    this.addEventListener("touchMove" onTouchMove);
    // add listener to when touch end occurs
    this.addEventListener("touchEnd", onTouchEnd);
};
// when the user swipes, update element positions to swipe
var onTouchMove = function(e) {
    // ... do your scrolling here

    // store current element
    currentElement = document.elementFromPoint(x, y);
    // if the current element is no longer the same as we clicked from the beginning, remove highlight
    if(clickedElement !== currentElement) {
        removeHighlight(clickedElement);
    }
};
// this is what is executed when the user stops the movement
var onTouchEnd = function(e) {
    if(clickedElement === currentElement) {
        removeHighlight(clickedElement);
        // .... execute click action
    }

    // clean up event listeners
    this.removeEventListener("touchMove" onTouchMove);
    this.removeEventListener("touchEnd", onTouchEnd);
};
function addHighlight(element) {
    element.className = "highlighted";
}
function removeHighlight(element) {
    element.className = "";
}
clickable.addEventListener("touchStart", onTouchStart);

次に、スクロール可能な要素にもリスナーを追加する必要がありますが、touchStarttouchEndの間で指が移動した場合に何が起こるかを心配する必要はありません。

var scrollable = document.getElementById("scrollableItem");

// set up touchStart event handler
var onTouchStartScrollable = function(e) {
    // listen to when the user moves finger
    this.addEventListener("touchMove" onTouchMoveScrollable);
    // add listener to when touch end occurs
    this.addEventListener("touchEnd", onTouchEndScrollable);
};
// when the user swipes, update element positions to swipe
var onTouchMoveScrollable = function(e) {
    // ... do your scrolling here
};
// this is what is executed when the user stops the movement
var onTouchEndScrollable = function(e) {
    // clean up event listeners
    this.removeEventListener("touchMove" onTouchMoveScrollable);
    this.removeEventListener("touchEnd", onTouchEndScrollable);
};
scrollable.addEventListener("touchStart", onTouchStartScrollable);

//サイモンA.

これが最終的に思いついたもので、アイテムのリストをスワイプでスクロールできるようにするだけでなく、各アイテムをタップで「トリガー可能」にすることもできます。さらに、キーボードで使用することもできます(onclickを使用)。

これはNetlight_Digital_Mediaの答えに似ていると思います。もう少し勉強する必要があります。

$(document)
// log the position of the touchstart interaction
.bind('touchstart', function(e){ 
  touchStartPos = $(window).scrollTop();
})
// log the position of the touchend interaction
.bind('touchend', function(e){
  // calculate how far the page has moved between
  // touchstart and end. 
  var distance = touchStartPos - $(window).scrollTop();

  var $clickableItem; // the item I want to be clickable if it's NOT a swipe

  // adding this class for devices that
  // will trigger a click event after
  // the touchend event finishes. This 
  // tells the click event that we've 
  // already done things so don't repeat

  $clickableItem.addClass("touched");      

  if (distance > 20 || distance < -20){
        // the distance was more than 20px
        // so we're assuming they intended
        // to swipe to scroll the list and
        // not selecting a row. 
    } else {
        // we'll assume it was a tap 
        whateverFunctionYouWantToTriggerOnTapOrClick()
    }
});


$($clickableItem).live('click',function(e){
 // for any non-touch device, we need 
 // to still apply a click event
 // but we'll first check to see
 // if there was a previous touch
 // event by checking for the class
 // that was left by the touch event.
if ($(this).hasClass("touched")){
  // this item's event was already triggered via touch
  // so we won't call the function and reset this for
  // the next touch by removing the class
  $(this).removeClass("touched");
} else {
  // there wasn't a touch event. We're
  // instead using a mouse or keyboard
  whateverFunctionYouWantToTriggerOnTapOrClick()
}
});
17
DA.

DAからの引用:

これは実際の例です:

var touch_pos;
$(document).on('touchstart', '.action-feature', function(e) {
  e.preventDefault();
  touch_pos = $(window).scrollTop();
}).on('click touchend', '.action-feature', function(e) {
  e.preventDefault();
  if(e.type=='touchend' && (Math.abs(touch_pos-$(window).scrollTop())>3)) return;
  alert("only accessed when it's a click or not a swipe");
});
6
josualeonard

これらのソリューションのいくつかは私にとってはうまくいきましたが、結局、この軽量ライブラリのセットアップが簡単であることがわかりました。

Tocca.js: https://github.com/GianlucaGuarini/Tocca.js

非常に柔軟性があり、タッチやスワイプ、ダブルタップなどを検出します。

3
Patrice Wrex

私は同じ問題を抱えていました、ここで私のために働く簡単な解決策があります

$(document).on('touchstart', 'button', function(evt){ 
    var oldScrollTop = $(window).scrollTop();
    window.setTimeout( function() {
        var newScrollTop = $(window).scrollTop();
        if (Math.abs(oldScrollTop-newScrollTop)<3) $button.addClass('touchactive');
    }, 200);
});

基本的に、すぐにタッチスタートを処理する代わりに、数ミリ秒(この例では200ミリ秒)待ってから、スクロール位置を確認し、スクロール位置を変更した場合、タッチスタートを処理する必要はありません。

2
Mahes

私は、jQueryを使用して魅力のように機能するこのエレガントなソリューションに出会いました。私の問題は、スクロール中にリストアイテムがタッチスタートイベントを呼び出せないことでした。これはスワイプでも機能するはずです。

  1. クラス「listObject」を使用してスクロールまたはスワイプする各アイテムにタッチスタートをバインドします

    $('.listObject').live('touchstart', touchScroll);
    
  2. 次に、各アイテムに、呼び出される関数を定義するデータオブジェクト属性を割り当てます

    <button class='listObject' data-object=alert('You are alerted !')>Alert Me</button>
    

次の機能は、タップとスクロールまたはスワイプを効果的に区別します。

function touchScroll(e){

    var objTarget = $(event.target);

    if(objTarget.attr('data-object')){
        var fn = objTarget.attr('data-object'); //function to call if tapped    
    }   

    if(!touchEnabled){// default if not touch device
        eval(fn);
        console.log("clicked", 1);
        return;
    }

    $(e.target).on('touchend', function(e){
        eval(fn); //trigger the function
        console.log("touchEnd")      
        $(e.target).off('touchend');
    });

    $(e.target).on('touchmove', function(e){
        $(e.target).off('touchend');
        console.log("moved")
    }); 

}
1
alQemist

複数の要素に対してこれを行い、マウスイベントとポインタイベントも必要な場合:

var elems = $('YOURMULTISELECTOR'); // selector for multiple elements
elems.unbind('mousdown pointerdown touchstart touchmove mouseup pointerup touchend');
var elem = null;
elems.on('mousdown pointerdown touchstart', function (e) {
    elem = yourSingleSelector(e);
}).on('touchmove', function (e) {
    elem = null;                
}).on('mouseup pointerup touchend', function (e) { 
    if (elem == yourSingleSelector(e)) {                    
        // do something
    }
});
0
A.J.Bauer

jQuery Mobileには .tap() イベントがあり、期待どおりの動作をしているようです:

JQuery Mobileタップイベントは、単一のターゲットオブジェクトで発生するすばやく完全なタッチイベントの後にトリガーされます。これは、タッチジェスチャのリリース状態でトリガーされる標準のクリックイベントに相当するジェスチャです。

これは必ずしも質問に答えるわけではないかもしれませんが、いくつかに代わる有用な選択肢かもしれません。

0
Wex

このコードを使用して、スワイプされていない場合にのみボタンが(タッチエンドで)トリガーされるようにします。

var startY;
var yDistance;

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

$('.button').on("touchstart", touchHandler, true);
$('.button').on("touchmove", touchHandler, true);

$('.button').on("touchstart", function(){
    startY = touch.clientY;
});

$('.button').on('touchend', function(){

    yDistance = startY - touch.clientY;

    if(Math.abs(yDist) < 30){

        //button response here, only if user is not swiping
        console.log("button pressed")
    }
});
0
LocDog

私はこれを少し異なる方法で行いました。それは間違いなく非常にエレガントではなく、確かにほとんどの状況に適していませんが、私にとってはうまくいきました。

JQueryのtoggleSlide()を使用して入力divを開いたり閉じたりして、タッチスタートでスライドを起動しました。問題は、ユーザーがスクロールしたいときに、タッチしたdivが開くことでした。これを防ぐために(またはユーザーが気付く前にそれを元に戻すために)最後にタッチされたdivを閉じるタッチスライドイベントをドキュメントに追加しました。

より詳細に、コードスニペットを次に示します。

var lastTouched;

document.addEventListener('touchmove',function(){
    lastTouched.hide();
});

$('#button').addEventListener('touchstart',function(){
    $('#slide').slideToggle();
    lastTouched = $('#slide');
});

グローバル変数は最後にタッチされたdivを保存し、ユーザーがスワイプすると、document.touchmoveイベントはそのdivを非表示にします。時々divのちらつきが発生することがありますが、それは私が必要とするものには機能し、私が思い付くほど簡単です。

0
Abraham Brookes