Programing

'touchstart'및 'click'이벤트를 바인딩하지만 둘 다에 응답하지 않는 방법은 무엇입니까?

lottogame 2020. 5. 13. 07:57
반응형

'touchstart'및 'click'이벤트를 바인딩하지만 둘 다에 응답하지 않는 방법은 무엇입니까?


다양한 장치에서 작동해야하는 모바일 웹 사이트에서 작업하고 있습니다. 지금 당장 나에게 두통을주는 것은 BlackBerry입니다.

키보드 클릭과 터치 이벤트를 모두 지원해야합니다.

이상적으로는 다음을 사용합니다.

$thing.click(function(){...})

그러나 우리가 겪고있는 문제는 이러한 블랙 베리 장치 중 일부는 터치 시간부터 클릭 트리거까지 매우 성가신 지연이 있다는 것입니다.

해결 방법은 touchstart를 대신 사용하는 것입니다.

$thing.bind('touchstart', function(event){...})

그러나 두 이벤트를 바인딩하고 어떻게 시작합니까? 키보드 장치에 대한 클릭 이벤트가 여전히 필요하지만 터치 장치를 사용하는 경우 클릭 이벤트 발생을 원하지 않습니다.

보너스 질문 : 어쨌든 터치 스타트 이벤트가없는 브라우저를 추가로 수용 할 수 있습니까? 이를 조사 할 때 BlackBerry OS5는 터치 스타트를 지원하지 않는 것 같으므로 해당 브라우저의 클릭 이벤트에 의존해야합니다.

추가:

아마도 더 포괄적 인 질문은 다음과 같습니다.

jQuery를 사용하면 동일한 바인딩으로 터치 상호 작용과 마우스 상호 작용을 모두 처리하는 것이 가능 / 권장됩니까?

이상적으로 대답은 '예'입니다. 그렇지 않은 경우 몇 가지 옵션이 있습니다.

1) WURFL을 사용하여 장치 정보를 얻으므로 자체 장치 행렬을 만들 수 있습니다. 기기에 따라 터치 스타트 또는 클릭을 사용합니다.

2) JS를 통해 브라우저에서 터치 지원을 감지하십시오 (더 많은 연구가 필요하지만 그렇게 할 수있는 것처럼 보입니다).

그러나 여전히 하나의 문제가 남아 있습니다. BOTH를 지원하는 장치는 어떻습니까? 당사가 지원하는 일부 전화 (즉, Nokias 및 BlackBerries)에는 터치 스크린 키보드 가 모두 있습니다. 그래서 그런 종류의 원조로 다시 원래의 질문으로 돌아갑니다 ... 어쨌든 두 가지를 동시에 허용 할 수있는 방법이 있습니까?


업데이트 : 마우스와 터치 중 하나를 선택하는 대신 "포인터"이벤트에 바인딩 할 수 있는 jQuery 포인터 이벤트 폴리 필 프로젝트를 확인하십시오 .


둘 다 바인딩하지만 플래그를 만들어서 함수가 100ms마다 한 번만 실행되도록하십시오.

var flag = false;
$thing.bind('touchstart click', function(){
  if (!flag) {
    flag = true;
    setTimeout(function(){ flag = false; }, 100);
    // do something
  }

  return false
});

이것은 내가 "만든"수정이며 GhostClick을 꺼내고 FastClick을 구현합니다. 직접 시도해보고 효과가 있는지 알려주십시오.

$(document).on('touchstart click', '.myBtn', function(event){
        if(event.handled === false) return
        event.stopPropagation();
        event.preventDefault();
        event.handled = true;

        // Do your magic here

});

다음과 같이 시도해보십시오.

var clickEventType=((document.ontouchstart!==null)?'click':'touchstart');
$("#mylink").bind(clickEventType, myClickHandler);

일반적으로 이것은 다음과 같이 작동합니다.

$('#buttonId').on('touchstart click', function(e){
    e.stopPropagation(); e.preventDefault();
    //your code here

});

이벤트 함수 return false;끝에 추가하면 on("click touchstart")이 문제를 해결할 수 있습니다.

$(this).on("click touchstart", function() {
  // Do things
  return false;
});

.on () 의 jQuery 문서에서

false이벤트 핸들러에서 복귀 하면 자동으로 event.stopPropagation()및을 호출 event.preventDefault()합니다. false핸들러 값을 약식으로 전달할 수도 있습니다 function(){ return false; }.


나는 비슷한 것을해야했습니다. 여기 나를 위해 일한 것의 단순화 된 버전이 있습니다. 터치 이벤트가 감지되면 클릭 바인딩을 제거하십시오.

$thing.on('touchstart click', function(event){
  if (event.type == "touchstart")
    $(this).off('click');

  //your code here
});

필자의 경우 클릭 이벤트가 <a>요소 에 바인딩되어 있으므로 클릭 바인딩을 제거하고 클릭 이벤트를 리 바인드하여 <a>요소 의 기본 동작을 방해했습니다 .

$thing.on('touchstart click', function(event){
  if (event.type == "touchstart")
    $(this).off('click').on('click', function(e){ e.preventDefault(); });

  //your code here
});

나는 다음과 같은 방법으로 성공했다.

쉬워요...

$(this).on('touchstart click', function(e){
  e.preventDefault();
  //do your stuff here
});

일반적으로 기본 터치와 비 터치 (클릭) API를 혼합하고 싶지 않습니다. 터치의 세계로 이동하면 터치 관련 기능 만 쉽게 처리 할 수 ​​있습니다. 아래는 원하는 것을 수행하는 의사 코드입니다.

touchmove 이벤트에 연결하고 위치를 추적하면 doTouchLogic 기능에 더 많은 항목을 추가하여 제스처 및 기타 사항을 감지 할 수 있습니다.

var touchStartTime;
var touchStartLocation;
var touchEndTime;
var touchEndLocation;

$thing.bind('touchstart'), function() {
     var d = new Date();
     touchStartTime = d.getTime();
     touchStartLocation = mouse.location(x,y);
});

$thing.bind('touchend'), function() {
     var d = new Date();
     touchEndTime= d.getTime();
     touchEndLocation= mouse.location(x,y);
     doTouchLogic();
});

function doTouchLogic() {
     var distance = touchEndLocation - touchStartLocation;
     var duration = touchEndTime - touchStartTime;

     if (duration <= 100ms && distance <= 10px) {
          // Person tapped their finger (do click/tap stuff here)
     }
     if (duration > 100ms && distance <= 10px) {
          // Person pressed their finger (not a quick tap)
     }
     if (duration <= 100ms && distance > 10px) {
          // Person flicked their finger
     }
     if (duration > 100ms && distance > 10px) {
          // Person dragged their finger
     }
}

모범 사례는 이제 사용하는 것입니다.

$('#object').on('touchend mouseup', function () { });

터치

터치 표면에서 터치 포인트가 제거되면 터치 엔드 이벤트가 시작됩니다.

터치 엔드 이벤트는 마우스 이벤트를 트리거하지 않습니다 .


마우스

마우스 포인터가 요소 위에 있고 마우스 단추를 놓으면 mouseup 이벤트가 요소로 전송됩니다. 모든 HTML 요소가이 이벤트를 수신 할 수 있습니다.

mouseup 이벤트는 터치 이벤트를 트리거하지 않습니다 .

$('#click').on('mouseup', function () { alert('Event detected'); });
$('#touch').on('touchend', function () { alert('Event detected'); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1 id="click">Click me</h1>
<h1 id="touch">Touch me</h1>


편집 (2017)

2017 년부터 Chrome을 시작 .on("click")으로 클릭 요청시 탭 이벤트로 인해 발생하는 지연을 제거하여 클릭 이벤트가 마우스와 터치 모두에 대해 더 호환되도록하기위한 브라우저를 만들었습니다 .

클릭 이벤트만으로 되 돌리는 것이 가장 간단한 해결책이라는 결론에 이르게됩니다.

이것이 실제적인지 확인하기 위해 아직 크로스 브라우저 테스트를 수행하지 않았습니다.


Google https://developers.google.com/mobile/articles/fast_buttons 에서 빠른 버튼 및 클릭 수 확인


글쎄 ...이 모든 것은 매우 복잡합니다.

당신이 modernizr을 가지고 있다면, 그것은 쉬운 일이 아닙니다.

ev = Modernizr.touch ? 'touchstart' : 'click';

$('#menu').on(ev, '[href="#open-menu"]', function(){
  //winning
});

더 나은 유지 보수를위한 또 다른 구현입니다. 그러나이 기술은 event.stopPropagation ()도 수행합니다. 100ms 동안 클릭 한 다른 요소에는 클릭이 걸리지 않습니다.

var clickObject = {
    flag: false,
    isAlreadyClicked: function () {
        var wasClicked = clickObject.flag;
        clickObject.flag = true;
        setTimeout(function () { clickObject.flag = false; }, 100);
        return wasClicked;
    }
};

$("#myButton").bind("click touchstart", function (event) {
   if (!clickObject.isAlreadyClicked()) {
      ...
   }
}

문서화 목적으로, 내가 생각할 수있는 모바일 솔루션의 데스크탑 / 탭에서 가장 빠르고 반응이 빠른 클릭을 위해 내가 한 일은 다음과 같습니다.

on브라우저가 터치 이벤트를 지원할 때마다 모든 클릭 이벤트를 터치 시작으로 바꾸는 jQuery의 기능을 수정 된 기능으로 대체했습니다.

$.fn.extend({ _on: (function(){ return $.fn.on; })() });
$.fn.extend({
    on: (function(){
        var isTouchSupported = 'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
        return function( types, selector, data, fn, one ) {
            if (typeof types == 'string' && isTouchSupported && !(types.match(/touch/gi))) types = types.replace(/click/gi, 'touchstart');
            return this._on( types, selector, data, fn);
        };
    }()),
});

다음과 같이 사용법이 이전과 정확히 동일합니다.

$('#my-button').on('click', function(){ /* ... */ });

그러나 가능하면 터치 스타트를 사용하고 그렇지 않으면 클릭하십시오. 어떤 종류의 지연도 필요하지 않습니다 : D


나는 ontouchstart방아쇠를 당겼다 면 암기 할 아이디어를 생각해 냈습니다 . 이 경우 우리는 그것을 지원하는 장치에 있으며 onclick이벤트 를 무시하고 싶습니다 . ontouchstart항상 전에 트리거 해야하기 때문에 onclick이것을 사용하고 있습니다.

<script> touchAvailable = false; </script>
<button ontouchstart="touchAvailable=true; myFunction();" onclick="if(!touchAvailable) myFunction();">Button</button>


다음과 같이 시도해보십시오.

var clickEvent = (('ontouchstart' in document.documentElement)?'touchstart':'click');
$("#mylink").on(clickEvent, myClickHandler);

제 경우에는 완벽하게 작동했습니다.

jQuery(document).on('mouseup keydown touchend', function (event) {
var eventType = event.type;
if (eventType == 'touchend') {
    jQuery(this).off('mouseup');
}
});

주요 문제는 마우스 클릭 대신 터치 장치에서 클릭과 터치 엔드를 동시에 트리거했을 때 클릭 오프를 사용하면 일부 기능이 모바일 장치에서 전혀 작동하지 않았다는 것입니다. 클릭 문제는 터치 엔드를 포함한 나머지 이벤트를 발생시키는 글로벌 이벤트입니다.


이것은 나를 위해 일했습니다. 모바일은 두 가지를 모두 듣고 있으므로 터치 이벤트 인 것을 피하십시오. 데스크탑은 마우스 만 청취합니다.

 $btnUp.bind('touchstart mousedown',function(e){
     e.preventDefault();

     if (e.type === 'touchstart') {
         return;
     }

     var val = _step( _options.arrowStep );
               _evt('Button', [val, true]);
  });

Mottie가 제공 한 최상의 답변이기 때문에 그의 코드를 더 재사용 할 수 있도록 노력하고 있습니다.

bindBtn ("#loginbutton",loginAction);

function bindBtn(element,action){

var flag = false;
$(element).bind('touchstart click', function(e) {
    e.preventDefault();
    if (!flag) {
        flag = true;
        setTimeout(function() {
            flag = false;
        }, 100);
        // do something
        action();
    }
    return false;
});

또한 Android / iPad 웹 앱에서 작업 중이며 "touchmove"만 사용하면 "구성 요소를 이동"하기에 충분합니다 (touchstart 필요 없음). touchstart를 비활성화하면 .click (); jQuery에서. touchstart로 과부하되지 않았기 때문에 실제로 작동합니다.

마지막으로 .live ( "touchstart", function (e) {e.stopPropagation ();}); 터치 스타트 이벤트가 전파를 멈추도록 요청하고 거실을 click ()하여 트리거합니다.

그것은 나를 위해 일했다.


이 문제를 해결하려고 할 때 고려해야 할 사항이 많이 있습니다. 대부분의 솔루션은 스크롤을 끊거나 고스트 클릭 이벤트를 올바르게 처리하지 않습니다.

전체 솔루션은 https://developers.google.com/mobile/articles/fast_buttons를 참조 하십시오.

주의 : 요소별로 고스트 클릭 이벤트를 처리 할 수 ​​없습니다. 지연된 클릭은 화면 위치에 따라 실행되므로 터치 이벤트가 어떤 방식 으로든 페이지를 수정하면 클릭 이벤트가 새 버전의 페이지로 전송됩니다.


이 이벤트에 할당하는 것이 효과적 일 수 'touchstart mousedown'또는 'touchend mouseup'사용의 바람직하지 않은 부작용을 피하기 위해 click.


클릭이 항상 터치 이벤트를 따른다 는 사실을 활용 하면 시간 초과 또는 전역 플래그를 사용하지 않고 "고스트 클릭"을 제거하기 위해 수행 한 작업이 있습니다.

$('#buttonId').on('touchstart click', function(event){
    if ($(this).data("already")) {
        $(this).data("already", false);
        return false;
    } else if (event.type == "touchstart") {
        $(this).data("already", true);
    }
    //your code here
});

기본적으로 ontouchstart이벤트가 요소에서 발생할 때마다 세트에 플래그를 지정한 다음 클릭이 발생할 때 제거하고 무시합니다.


jQuery Event API를 사용하지 않는 이유는 무엇입니까?

http://learn.jquery.com/events/event-extensions/

이 간단한 이벤트를 성공적으로 사용했습니다. 깨끗하고 네임 스페이스 가능하며 유연하게 개선 할 수 있습니다.

var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent);
var eventType = isMobile ? "touchstart" : "click";

jQuery.event.special.touchclick = {
  bindType: eventType,
  delegateType: eventType
};

jQuery를 사용하는 경우 다음이 꽤 잘 작동합니다.

var callback; // Initialize this to the function which needs to be called

$(target).on("click touchstart", selector, (function (func){
    var timer = 0;
    return function(e){
        if ($.now() - timer < 500) return false;
        timer = $.now();
        func(e);
    }
})(callback));

다른 솔루션도 좋지만 루프에서 여러 이벤트를 바인딩하고 적절한 클로저를 만들기 위해 자체 호출 기능이 필요했습니다. 또한 다음 클릭 / 터치 시작시 바인딩을 호출 할 수 있기를 원했기 때문에 바인딩을 비활성화하고 싶지 않았습니다.

비슷한 상황에있는 사람을 도울 수 있습니다!


간단한 기능을 사용하려면 터치를 인식하거나 클릭하십시오. 다음 코드를 사용합니다.

var element = $("#element");

element.click(function(e)
{
  if(e.target.ontouchstart !== undefined)
  {
    console.log( "touch" );
    return;
  }
  console.log( "no touch" );
});

터치 스타트 이벤트가 정의되어 있으면 "터치"를 반환하고 그렇지 않으면 "터치 없음"을 반환합니다. 내가 말했듯이 이것은 클릭 / 탭 이벤트에 대한 간단한 접근법입니다.


나는 이것을 시도하고 있으며 지금까지는 효과가있다 (그러나 나는 안드로이드 / Phonegap에서만주의해야한다)

  function filterEvent( ob, ev ) {
      if (ev.type == "touchstart") {
          ob.off('click').on('click', function(e){ e.preventDefault(); });
      }
  }
  $('#keypad').on('touchstart click', '.number, .dot', function(event) {
      filterEvent( $('#keypad'), event );
      console.log( event.type );  // debugging only
           ... finish handling touch events...
  }

I don't like the fact that I am re-binding handlers on every touch, but all things considered touches don't happen very often (in computer time!)

I have a TON of handlers like the one for '#keypad' so having a simple function that lets me deal with the problem without too much code is why I went this way.


Try to use Virtual Mouse (vmouse) Bindings from jQuery Mobile. It's virtual event especially for your case:

$thing.on('vclick', function(event){ ... });

http://api.jquerymobile.com/vclick/

Browser support list: http://jquerymobile.com/browser-support/1.4/


EDIT: My former answer (based on answers in this thread) was not the way to go for me. I wanted a sub-menu to expand on mouse enter or touch click and to collapse on mouse leave or another touch click. Since mouse events normally are being fired after touch events, it was kind of tricky to write event listeners that support both touchscreen and mouse input at the same time.

jQuery plugin: Touch Or Mouse

I ended up writing a jQuery plugin called "Touch Or Mouse" (897 bytes minified) that can detect whether an event was invoked by a touchscreen or mouse (without testing for touch support!). This enables the support of both touchscreen and mouse at the same time and completely separate their events.

This way the OP can use touchstart or touchend for quickly responding to touch clicks and click for clicks invoked only by a mouse.

Demonstration

First one has to make ie. the body element track touch events:

$(document.body).touchOrMouse('init');

Mouse events our bound to elements in the default way and by calling $body.touchOrMouse('get', e) we can find out whether the event was invoked by a touchscreen or mouse.

$('.link').click(function(e) {
  var touchOrMouse = $(document.body).touchOrMouse('get', e);

  if (touchOrMouse === 'touch') {
    // Handle touch click.
  }
  else if (touchOrMouse === 'mouse') {
    // Handle mouse click.
  }
}

See the plugin at work at http://jsfiddle.net/lmeurs/uo4069nh.

Explanation

  1. This plugin needs to be called on ie. the body element to track touchstart and touchend events, this way the touchend event does not have to be fired on the trigger element (ie. a link or button). Between these two touch events this plugin considers any mouse event to be invoked by touch.
  2. Mouse events are fired only after touchend, when a mouse event is being fired within the ghostEventDelay (option, 1000ms by default) after touchend, this plugin considers the mouse event to be invoked by touch.
  3. When clicking on an element using a touchscreen, the element gains the :active state. The mouseleave event is only fired after the element loses this state by ie. clicking on another element. Since this could be seconds (or minutes!) after the mouseenter event has been fired, this plugin keeps track of an element's last mouseenter event: if the last mouseenter event was invoked by touch, the following mouseleave event is also considered to be invoked by touch.

Here's a simple way to do it:

// A very simple fast click implementation
$thing.on('click touchstart', function(e) {
  if (!$(document).data('trigger')) $(document).data('trigger', e.type);
  if (e.type===$(document).data('trigger')) {
    // Do your stuff here
  }
});

You basically save the first event type that is triggered to the 'trigger' property in jQuery's data object that is attached to the root document, and only execute when the event type is equal to the value in 'trigger'. On touch devices, the event chain would likely be 'touchstart' followed by 'click'; however, the 'click' handler won't be executed because "click" doesn't match the initial event type saved in 'trigger' ("touchstart").

The assumption, and I do believe it's a safe one, is that your smartphone won't spontaneously change from a touch device to a mouse device or else the tap won't ever register because the 'trigger' event type is only saved once per page load and "click" would never match "touchstart".

Here's a codepen you can play around with (try tapping on the button on a touch device -- there should be no click delay): http://codepen.io/thdoan/pen/xVVrOZ

I also implemented this as a simple jQuery plugin that also supports jQuery's descendants filtering by passing a selector string:

// A very simple fast click plugin
// Syntax: .fastClick([selector,] handler)
$.fn.fastClick = function(arg1, arg2) {
  var selector, handler;
  switch (typeof arg1) {
    case 'function':
      selector = null;
      handler = arg1;
      break;
    case 'string':
      selector = arg1;
      if (typeof arg2==='function') handler = arg2;
      else return;
      break;
    default:
      return;
  }
  this.on('click touchstart', selector, function(e) {
    if (!$(document).data('trigger')) $(document).data('trigger', e.type);
    if (e.type===$(document).data('trigger')) handler.apply(this, arguments);
  });
};

Codepen: http://codepen.io/thdoan/pen/GZrBdo/


The best method I have found is to write the touch event and have that event call the normal click event programatically. This way you have all your normal click events and then you need to add just one event handler for all touch events. For every node you want to make touchable, just add the "touchable" class to it to invoke the touch handler. With Jquery it works like so with some logic to make sure its a real touch event and not a false positive.

$("body").on("touchstart", ".touchable", function() { //make touchable  items fire like a click event
var d1 = new Date();
var n1 = d1.getTime();
setTimeout(function() {
    $(".touchable").on("touchend", function(event) {
        var d2 = new Date();
        var n2 = d2.getTime();
        if (n2 - n1 <= 300) {
            $(event.target).trigger("click"); //dont do the action here just call real click handler
        }
    });
}, 50)}).on("click", "#myelement", function() {
//all the behavior i originally wanted
});

참고URL : https://stackoverflow.com/questions/7018919/how-to-bind-touchstart-and-click-events-but-not-respond-to-both

반응형