วิธีผูกเหตุการณ์ 'touchstart' และ 'คลิก' แต่ไม่ตอบสนองต่อทั้งสองอย่าง


200

ฉันทำงานบนเว็บไซต์มือถือที่ต้องทำงานกับอุปกรณ์ที่หลากหลาย คนที่ทำให้ฉันปวดหัวในขณะนี้คือ BlackBerry

เราจำเป็นต้องสนับสนุนการคลิกทั้งคีย์บอร์ดและกิจกรรมการสัมผัส

เป็นการดีที่ฉันจะใช้:

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

แต่ปัญหาที่เราพบคืออุปกรณ์ BlackBerry บางตัวมีความล่าช้าที่น่ารำคาญมากตั้งแต่ช่วงเวลาที่กดปุ่มเพื่อให้เกิดการคลิก

การรักษาคือการใช้ touchstart แทน:

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

แต่ฉันจะไปผูกเหตุการณ์ทั้งสองอย่างไร แต่เพียงหนึ่งเหตุการณ์? ฉันยังต้องการกิจกรรมการคลิกสำหรับอุปกรณ์แป้นพิมพ์ แต่แน่นอนว่าไม่ต้องการให้เหตุการณ์การคลิกเริ่มทำงานหากฉันใช้อุปกรณ์ระบบสัมผัส

คำถามโบนัส: มีการทำเช่นนี้และรองรับเบราว์เซอร์เพิ่มเติมที่ไม่มีแม้แต่เหตุการณ์เริ่มต้นหรือไม่? ในการค้นคว้านี้ดูเหมือนว่า BlackBerry OS5 ไม่รองรับระบบเริ่มต้นดังนั้นจะต้องพึ่งพากิจกรรมการคลิกสำหรับเบราว์เซอร์นั้น

ภาคผนวก:

บางทีคำถามที่ครอบคลุมมากขึ้นคือ:

ด้วย jQuery เป็นไปได้ / แนะนำให้จัดการกับทั้งการโต้ตอบแบบสัมผัสและการโต้ตอบกับเมาส์ด้วยการโยงเดียวกันหรือไม่?

เป็นการดีที่คำตอบคือใช่ ถ้าไม่ฉันมีตัวเลือก:

1) เราใช้ WURFL เพื่อรับข้อมูลอุปกรณ์ดังนั้นสามารถสร้างเมทริกซ์ของอุปกรณ์ของเราเอง เราจะใช้ touchstart หรือคลิกทั้งนี้ขึ้นอยู่กับอุปกรณ์

2) ตรวจหาการรองรับการสัมผัสในเบราว์เซอร์ผ่าน JS (ฉันต้องทำวิจัยเพิ่มเติมเกี่ยวกับเรื่องนี้ แต่ดูเหมือนว่าเป็นไปได้)

อย่างไรก็ตามยังคงมีปัญหาหนึ่ง: สิ่งที่เกี่ยวกับอุปกรณ์ที่รองรับทั้งสอง โทรศัพท์บางรุ่นที่เรารองรับ (เช่น Nokias และ BlackBerries) มีทั้งหน้าจอสัมผัสและคีย์บอร์ด ดังนั้นการพาฉันกลับไปที่คำถามเดิม ... มีวิธีใดบ้างที่จะอนุญาตทั้งสองอย่างพร้อมกัน?


2
คุณควรผูกมัดเพื่อเริ่มต้นแตะและเขียนตรรกะการคลิกของคุณเองควบคู่ไปกับตรรกะการสัมผัสของคุณ การโทรกลับภายในคลิกไม่มีความรู้เรื่องการสัมผัส
Justin808

ฉันไม่แน่ใจว่าฉันจะตามจัสติน ฉันจะยังคงมีทั้งเหตุการณ์เริ่มต้นและการคลิกที่ถูกผูกไว้กับมันหรือไม่?
ดา

@DA - ไม่คุณจะไม่ผูกมัดกับการคลิกกลับ () ทั้งหมด ฉันจะพยายามเขียนคำตอบในบางรหัส sudo ผมมีอุปกรณ์สัมผัสที่มีประโยชน์ในการเขียนโค้ดจริง :)
Justin808

อ่า แต่เพื่อชี้แจงฉันยังต้องคลิกเหตุการณ์เนื่องจากจะมีคนเข้าถึงเว็บไซต์นี้ด้วยอุปกรณ์ที่ไม่ได้สัมผัส
ดา

2
การใช้.bind('touchstart mouseup')จะแก้ปัญหาได้ (ตามความเห็นด้านล่าง)
oriadam

คำตอบ:


136

อัปเดต : ลองดูโครงการ jQuery Pointer Events Polyfillที่ให้คุณเชื่อมโยงกับ "pointer" ของเหตุการณ์แทนการเลือกระหว่างเมาส์และสัมผัส


ผูกกับทั้งคู่ แต่สร้างแฟล็กเพื่อให้ฟังก์ชันใช้ไฟเพียงครั้งเดียวต่อ 100ms หรือมากกว่านั้น

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

  return false
});

7
อืม ... มันรู้สึกแฮ็ค แต่มันก็ใช้ได้ จับได้ว่าในอุปกรณ์เหล่านี้บางอย่างมันเป็นความล่าช้าที่เห็นได้ชัด ... อาจจะเกือบหนึ่งวินาที ... ซึ่งน่ารำคาญสำหรับคนอื่น ๆ ในอุปกรณ์ที่เร็วกว่า
ดา

15
แทนที่จะใช้clickเปลี่ยนเป็นmousedown... บางทีการหน่วงเวลานานคือความแตกต่างระหว่างmousedownและmouseupเป็นวิธีการclickพิจารณา
Mottie

7
นี่คือทางออกที่ฉันไปด้วย ขอบคุณ! สิ่งที่ฉันทำคือผมธงรายการบนโดยใช้ระดับของtouchend touchedสิ่งนี้จะยิงก่อนที่เหตุการณ์การคลิกจะถูกเรียก ฉันเพิ่มเหตุการณ์การคลิกที่ตรวจสอบการมีอยู่ของคลาสนั้นก่อน หากมีเราจะถือว่าเหตุการณ์การสัมผัสเกิดขึ้นและเราจะไม่ทำอะไรเลยกับการคลิกนอกจากลบชื่อชั้นเรียนเพื่อ 'รีเซ็ต' สำหรับการโต้ตอบครั้งต่อไป หากคลาสไม่ได้อยู่ที่นั่นเราจะถือว่าพวกเขาใช้แป้นพิมพ์เพื่อคลิกที่รายการและเรียกใช้ฟังก์ชันจากที่นั่น
ดา

7
คุณควรทราบว่าส่วนใหญ่เบราว์เซอร์โทรศัพท์มี 300ms ล่าช้าในเหตุการณ์คลิกดังนั้นคุณจึงควรเพิ่ม deleay อย่างน้อย 300, ดูบทความนี้เกี่ยวกับปัญหา 300ms ล่าช้า นี่เป็นพื้นฐานในการเปิดใช้งานฟังก์ชั่น "ดับเบิลคลิกเพื่อซูม" ซึ่งเป็นคุณสมบัติที่สำคัญมากในช่วงต้น iPhone เมื่อเว็บไซต์ส่วนใหญ่ได้รับการออกแบบให้ดูบนหน้าจอเดสก์ท็อปขนาดใหญ่
ความกลัว

12
คำถามนี้ได้รับการดูเกือบ 100,000 ครั้ง ณ จุดนี้ ดูเหมือนว่าจะเป็นกรณี / ปัญหาการใช้งานทั่วไปที่พิเศษ อย่างไรก็ตามคำตอบนี้และคำถามอื่น ๆ ที่คล้ายกันนี้ดูเหมือนว่าจะเป็นเรื่องแฮ็ค โซลูชันของ Google ในขณะที่คิดอย่างละเอียดมากกว่าส่วนใหญ่ดูเหมือนจะเป็นเพียงแฮ็คที่แข็งแกร่งกว่า สำหรับบางสิ่งที่ง่ายเหมือนตัวจัดการคลิกดูเหมือนว่าโซลูชันควรเป็นแบบง่าย ไม่มีใครพบวิธีแก้ปัญหาที่ไม่แฮ็กสำหรับปัญหานี้?
jinglesthula

73

นี่คือการแก้ไขที่ฉัน "สร้าง" และนำ 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

});

2
helgatheviking: jsbin.com/ijizat/25 ใน JavaScript มีฟังก์ชั่นที่ชื่อว่า TouchClick ที่รวมเอาไว้ด้านบน
Matt Parkins

5
ฉันชอบวิธีนี้มากสำหรับคำตอบทั้งหมดสำหรับคำถามนี้เพราะ a) ไม่ได้ทดสอบความสามารถของอุปกรณ์และ b) ไม่ได้ใช้ setTimeout จากประสบการณ์ของฉันการแก้ปัญหาเช่นนี้ที่ใช้ setTimeout อาจใช้งานได้ในกรณีส่วนใหญ่ แต่ช่วงเวลาของเหตุการณ์ค่อนข้างเป็นไปโดยพลการและอุปกรณ์เฉพาะ
itsmequinn

7
สิ่งนี้จะไม่ทำงานเพราะการส่ง 'เหตุการณ์' ในฟังก์ชันที่ไม่ระบุชื่อมันจะกลายเป็นวัตถุในตัวเครื่องภายในฟังก์ชันนั้นดังนั้นevent.handledจะเท่ากับundefinedทุกครั้งที่มีการเรียกใช้เหตุการณ์ นี่เป็นวิธีการแก้ปัญหา: ตั้ง 'จัดการ' jQuery.data()ธงในองค์ประกอบของเป้าหมายของตัวเองโดยใช้
thdoan

3
คุณมีevent.stopPropagation();และevent.preventDefault();จากความเข้าใจของฉัน (และการทดสอบ) เหตุการณ์สามารถถูกไล่ออกได้เพียงครั้งเดียวด้วยสิ่งนี้ ดังนั้นทำไมแม้กระทั่งตรวจสอบif(event.handled !== true)? สำหรับฉันมันไม่จำเป็นหรือฉันจะพลาดบางสิ่งบางอย่าง?
nbar

2
ไม่ควรevent.handledมีการตรวจสอบค่าtrue? เท่าที่ฉันสามารถบอกได้ว่ามันไม่เคยเกิดfalseขึ้น มันเริ่มต้นจากการเป็นแล้วคุณต้องการที่จะบอกได้ว่าจะได้รับการตั้งค่าให้undefined true
ACJ

49

คุณสามารถลองสิ่งนี้:

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

5
ฉันคิดว่าปัญหาของมันคือการทดสอบความสามารถของอุปกรณ์มากกว่าสิ่งที่ผู้ใช้กำลังทำอยู่ใช่ไหม ความท้าทายคือเราจำเป็นต้องสนับสนุนอุปกรณ์สัมผัสที่มีคีย์บอร์ด (เพื่อให้สามารถใช้ทั้งคู่ได้)
DA

8
นี่ไม่ใช่ความคิดที่ดี - จะแบ่งเหตุการณ์สำหรับผู้ใช้ที่มีหน้าจอสัมผัสและเมาส์เมื่อใช้เมาส์ (แล็ปท็อปที่มีหน้าจอสัมผัสกลายเป็นเรื่องธรรมดา)
Kristian J.

1
บทความที่ยอดเยี่ยมเกี่ยวข้องกับเรื่องนี้: hacks.mozilla.org/2013/04/…
ลุคเมเลีย

Windows 8 จะโยงเหตุการณ์ 'touchstart' เสมอหากใช้เพราะ 'ontouchstart' จะส่งคืน 'true' สำหรับระบบปฏิบัติการนี้ ดังนั้นอาจไม่ใช่คำตอบที่ดีที่สุดยกเว้นว่ารหัสของคุณจะทำงานบนอุปกรณ์หน้าจอสัมผัสจริงเท่านั้น แต่ถ้าเป็นเช่นนั้นคุณไม่จำเป็นต้องตรวจสอบเลย
Neil Monroe

ในแล็ปท็อปที่มีหน้าจอสัมผัสผู้ใช้จะไม่สามารถคลิกสิ่งใดโดยใช้รหัสนี้ใน chrome (อย่างน้อย chrome 46) IE 11 ดูเหมือนว่าจะทำงานได้
David Sherret

42

โดยปกติแล้วจะใช้งานได้เช่นกัน:

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

});

8
@ Jonathan ผิดถ้าคุณใช้ jQuery เวอร์ชันที่ใหม่กว่า Live เลิกใช้แล้วในรุ่น 1.7
Mike Barwick

ฉันลอง (และฉันอ่านที่อื่น - ไม่ใช่ความคิดของฉัน) ด้วย e.stopPropagation (); e.preventDefault (); และมันใช้งานได้ (สำหรับฉัน) เกือบจะ แต่ฉันค้นพบในภายหลังว่าในความเป็นจริงมันทำงานได้ตามที่คาดการณ์ไว้ 20 ครั้ง แต่ไม่ใช่ 1 ครั้งดังนั้นจึงไม่ใช่กระสุน ดูที่นี่: stackoverflow.com/questions/25671053/…ชื่นชมความคิดเห็นของคุณเกี่ยวกับปัญหานั้น!
Garavani

@ MikeBarwick คุณช่วยอธิบายความคิดเห็นของคุณหรือให้ลิงค์ที่อธิบายได้หรือไม่? ขอบคุณล่วงหน้า.
Billal Begueradj

22

เพียงเพิ่มreturn false;ในตอนท้ายของon("click touchstart")ฟังก์ชั่นเหตุการณ์สามารถแก้ปัญหานี้ได้

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

จากเอกสาร jQuery ใน. on ()

กลับfalseมาจากการจัดการเหตุการณ์จะเรียกโดยอัตโนมัติและevent.stopPropagation() คุ้มค่านอกจากนี้ยังสามารถผ่านไปจัดการตามที่จดชวเลขเป็นevent.preventDefault()falsefunction(){ return false; }


2
ใช้งานได้ดีไฟเพียงครั้งเดียวบนอุปกรณ์ที่มีทั้งคู่ - นี่เป็นวิธีแก้ปัญหาแฮ็กน้อยที่สุดที่สะอาดที่สุดสำหรับฉัน
Adam Cooper

ทำงานได้ดีที่สุดสำหรับฉันและทำให้เข้าใจได้ว่าทำไม
Amir5000

ข้อเสียของสิ่งนี้คืออะไร? ทำไมการแก้ปัญหาแบบง่าย ๆ จึงไม่เป็นที่รู้จักอย่างกว้างขวาง
ESR

11

ฉันต้องทำสิ่งที่คล้ายกัน นี่คือเวอร์ชั่นที่เรียบง่ายของสิ่งที่ได้ผลสำหรับฉัน หากตรวจพบเหตุการณ์การสัมผัสให้ลบการเชื่อมการคลิก

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

ฉันหวังว่ามันจะทำงานได้ดี แต่มันก็ไม่ได้ $ (สิ่งนี้) ไม่ได้ชี้ไปที่สิ่งที่คุณคิด
Dave Lowerre

8

ฉันประสบความสำเร็จด้วยวิธีต่อไปนี้

ง่าย Peasy ...

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

4
จะไม่ยิงสองครั้งในอุปกรณ์ที่ลงทะเบียนทั้งการคลิกและทัชสตาร์?
ดา

8
ขอโทษฉันรู้คุณ ใช่คุณถูกต้องการสัมผัสจะสร้างเหตุการณ์เริ่มต้นและคลิกเหตุการณ์เช่นกัน สิ่งนี้เรียกว่าการคลิกแบบโกสต์ Google ได้นำวิธีการแก้ปัญหาสำหรับที่ อาจไม่ตรงไปข้างหน้า แต่ทำงานได้อย่างสมบูรณ์แบบ นี่คือลิงค์. code.google.com/mobile/articles/fast_buttons.html#ghost
Hasanavi

2
อาจไม่สำคัญ แต่คุณพลาดeพารามิเตอร์ในfunction()
ProblemsOfSumit

@Hasanavi นี่เป็นการแก้ไขที่ยอดเยี่ยม แต่ฉันพบผลลัพธ์ที่ดีกว่าเมื่อใช้. on
Neil

1
ขอบคุณ @ Neil ใช่ใช้. on เป็นความคิดที่ดีกว่าเนื่องจากอาจถูกลบในรุ่นอนาคต ฉันเปลี่ยนตัวอย่างจากผูกเป็นเปิด
Hasanavi

5

ฉันเชื่อว่าวิธีปฏิบัติที่ดีที่สุดคือตอนนี้ที่จะใช้:

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

touchend

เหตุการณ์ touchend จะถูกยิงเมื่อจุดสัมผัสถูกลบออกจากพื้นผิวสัมผัส

เหตุการณ์ touchend จะไม่เรียกใช้เหตุการณ์เมาส์ใด ๆ


mouseup

เหตุการณ์การเลื่อนเมาส์ถูกส่งไปยังองค์ประกอบเมื่อตัวชี้เมาส์อยู่เหนือองค์ประกอบและปุ่มเมาส์จะถูกปล่อยออกมา องค์ประกอบ HTML ใด ๆ สามารถรับเหตุการณ์นี้ได้

เหตุการณ์การคลิกเมาส์จะไม่เรียกใช้กิจกรรมการสัมผัสใด ๆ

ตัวอย่าง

$('#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")เข้ากันได้กับทั้งเมาส์และสัมผัสมากขึ้นโดยการขจัดความล่าช้าที่เกิดขึ้นจากการแตะที่กิจกรรมตามคำขอการคลิก

สิ่งนี้นำไปสู่ข้อสรุปว่าการคืนค่ากลับไปใช้เพียงแค่เหตุการณ์คลิกจะเป็นวิธีที่ง่ายที่สุดในการก้าวไปข้างหน้า

ฉันยังไม่ได้ทำการทดสอบข้ามเบราว์เซอร์เพื่อดูว่าสิ่งนี้ใช้ได้จริงหรือไม่


นี่ดูเหมือนจะเป็นสัญญากับฉันดังนั้นฉันจึงยุ่งไปกับมัน แต่ในที่สุดฉันก็พบว่าmouseupมีผลลัพธ์ที่ไม่สามารถคาดเดาได้โดยเฉพาะอย่างยิ่งเมื่อใช้แทร็คแพดแทนเมาส์แบบดั้งเดิม
skybondsor

4

โดยทั่วไปคุณไม่ต้องการผสมผสานการสัมผัสเริ่มต้นและการไม่แตะ (คลิก) 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
     }
}

ฉันคิดว่านั่นเป็นประเด็นสำคัญของคำถาม: มันสมเหตุสมผลหรือไม่ที่จะลองและสนับสนุนทั้งสองรุ่นด้วยรหัสฐานเดียว ฉันจะอัปเดตคำถามของฉันด้วยสถานการณ์เพิ่มเติม
ดา

1
"โดยทั่วไปคุณไม่ต้องการผสมผสานการสัมผัสเริ่มต้นและการไม่แตะต้อง" หลังจากทำสิ่งนี้ฉันยอมรับ ปัญหาคือพวกเขายังคงผลิตอุปกรณ์สัมผัสด้วยแป้นพิมพ์ซึ่งเป็นเรื่องที่ปวดหัวสำหรับนักพัฒนาของเรา
ดา

ฉันยอมรับว่ามันจะง่ายกว่าที่จะทำตอนเริ่มต้นการตัดสินใจขั้นสุดท้ายของ js หรือไม่แตะต้อง ช่างเป็นโลกที่ง่ายขนาดไหน! แต่สิ่งนี้เกี่ยวกับอุปกรณ์ที่ถูกสาปนั้นคลิกและสัมผัสอย่างสมเหตุสมผล? มันมีค่าที่จะปวดหัวเหล่านั้นเพราะพวกเขา? นี่คืออนาคตที่ฉันถามตัวเองหรือ
Garavani

ฉันไม่ชอบความคิดในการมีฟังก์ชั่นที่หลากหลาย บริษัท ของฉันกำลังทำงานในโครงการที่ iPad ไม่ได้รับการจัดการเป็นอุปกรณ์มือถือเป็นพิเศษและคุณยังต้องใช้กิจกรรม touchend แต่ touchend ไม่ทำงานบนเดสก์ท็อปเวอร์ชันปกติ ดังนั้นการแก้ปัญหาของ @mottie จึงเป็นสิ่งที่ดีที่สุดสำหรับสถานการณ์นั้น
Pete


3

ดี ... สิ่งเหล่านี้ซับซ้อนมาก

หากคุณมีความทันสมัยมันเป็นเกมง่ายๆ

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

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

2
คุณช่วยอธิบายสิ่งนั้นได้ไหม? ฉันเชื่อว่ามันตรวจสอบเพื่อดูว่าอุปกรณ์รองรับการสัมผัสและถ้าไม่ใช้คลิก จับคือ (หรืออย่างน้อยก็เมื่อฉันเขียนคำถาม) คืออุปกรณ์บางอย่างรองรับทั้ง และในอุปกรณ์เหล่านั้นฉันยังต้องจัดการกับทั้งสองเหตุการณ์เนื่องจากทริกเกอร์อาจมาจากการแตะหรือคลิก
ดา

2
ฉันขอแนะนำให้อธิบายเพิ่มเติมเล็กน้อย
Aaron Hall

2

การดำเนินการอื่นเพื่อการบำรุงรักษาที่ดีขึ้น อย่างไรก็ตามเทคนิคนี้จะทำ event.stopPropagation () การคลิกไม่ได้ถูกตรวจจับบนองค์ประกอบอื่นใดที่คลิกมาเป็นเวลา 100 มิลลิวินาที

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()) {
      ...
   }
}

2

นี่เป็นสิ่งที่ฉันได้ทำเพื่อการคลิกเร็วที่สุด / ตอบสนองมากที่สุดบนเดสก์ท็อป / แตะบนโซลูชันมือถือที่ฉันนึกถึง:

ฉันแทนที่onฟังก์ชั่นของ jQuery ด้วยสิ่งที่ถูกแก้ไขซึ่งเมื่อใดก็ตามที่เบราว์เซอร์รองรับกิจกรรมการสัมผัสจะแทนที่เหตุการณ์การคลิกทั้งหมดของฉันด้วย touchstart

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


1
การจับด้วยสิ่งนี้จะเป็นอุปกรณ์ที่รองรับทั้งการสัมผัสและคีย์บอร์ดที่คุณต้องการเพื่อให้แน่ใจว่าคุณรองรับทั้งการโต้ตอบ
ดา

สังเกตดี @DA ฉันได้เพิ่มการตรวจสอบเพื่อแทนที่เหตุการณ์การคลิกเท่านั้นหากคุณไม่ได้ใช้กิจกรรมการสัมผัสใด ๆ ร่วมกับมัน Stil จะไม่เป็นทางออกสำหรับทุกโครงการ แต่ฉันแน่ใจว่าจะเหมาะสำหรับหลาย ๆ โครงการ
Gerson Goulart

2

ฉันเพิ่งคิดความคิดที่จะจดจำว่าontouchstartเคยถูกเรียก ในกรณีนี้เราอยู่ในอุปกรณ์ที่รองรับและต้องการเพิกเฉยต่อonclickเหตุการณ์ เนื่องจากontouchstartควรเรียกใช้มาก่อน onclickฉันจึงใช้สิ่งนี้:

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


1
เนื่องจากผู้ใช้อาจใช้การสัมผัสและเมาส์ระหว่างการเยี่ยมชมหน้าเดียวกันปัญหาของข้อมูลโค้ดนี้คือการลงทะเบียนการตั้งค่าสถานะ touchAvailable หนึ่งครั้งแทนที่จะเป็นต่อเหตุการณ์ ปลั๊กอิน jQuery จากstackoverflow.com/a/26145343/328272ทำเช่นเดียวกับรหัสของคุณ แต่สามารถบอกได้ว่าเหตุการณ์ของเมาส์ไม่ว่าจะถูกทริกเกอร์ด้วยการสัมผัสหรือเมาส์ตามเหตุการณ์ ไม่เพียง แต่เช่น เมื่อคลิก แต่ยังรวมถึง mouseleave ซึ่งอาจถูกเรียกใช้เป็นวินาที (หรือนาที) หลังจาก mouseenter (เหมาะสำหรับเมนู flyout ที่ควรขยายเมื่อวางเมาส์เหนือด้วยท่าทางเมาส์หรือคลิกเมื่อใช้การสัมผัส)
lmeurs

2

คุณสามารถลองสิ่งนี้:

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

อุปกรณ์ที่รองรับการสัมผัสและเมาส์
Walle Cyril

2

ในกรณีของฉันนี้ทำงานได้อย่างสมบูรณ์:

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

ปัญหาหลักคือเมื่อแทนที่การคลิกเมาส์ฉันลองด้วยการคลิกบนอุปกรณ์สัมผัสที่เปิดการคลิกและการสัมผัสในเวลาเดียวกันถ้าฉันใช้การคลิกปิดการทำงานบางอย่างไม่สามารถใช้งานได้บนอุปกรณ์มือถือ ปัญหาเกี่ยวกับการคลิกคือเหตุการณ์ทั่วโลกที่ทำให้เกิดเหตุการณ์ที่เหลือรวมถึง touchend


1

สิ่งนี้ใช้งานได้สำหรับฉันมือถือฟังทั้งสองอย่างเพื่อป้องกันเหตุการณ์หนึ่งซึ่งเป็นเหตุการณ์การสัมผัส เดสก์ท็อปเท่านั้นฟังเมาส์

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

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

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

1

สำหรับฉันคำตอบที่ดีที่สุดที่ 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;
});

0

ฉันกำลังทำงานกับแอพพลิเคชั่นบนเว็บสำหรับ Android / iPad และดูเหมือนว่าหากใช้ "touchmove" ก็เพียงพอที่จะ "ย้ายส่วนประกอบ" (ไม่จำเป็นต้องเริ่มต้นระบบสัมผัส) โดยการปิดการใช้งาน touchstart คุณสามารถใช้. คลิก (); จาก jQuery มันใช้งานได้จริงเพราะไม่แตะต้องจนเกินไป

ในที่สุดคุณสามารถ binb .live ("touchstart", function (e) {e.stopPropagation ();}); เพื่อขอให้เหตุการณ์ touchstart หยุดการแพร่กระจายห้องนั่งเล่นให้คลิก () เพื่อรับการกระตุ้น

มันใช้งานได้สำหรับฉัน


คุณจะปิดการใช้งานtouchstartอย่างไร
punkrockbuddyholly

ฉันเชื่อว่าคุณ "สามารถ" ทำสิ่งที่ต้องการ: jQuery ("# ​​my_element"). on ('touchstart', function (e) {e.preventDefault ()});
nembleton

0

มีหลายสิ่งที่ต้องพิจารณาเมื่อพยายามแก้ไขปัญหานี้ โซลูชันส่วนใหญ่หยุดการเลื่อนหรือไม่จัดการเหตุการณ์การคลิกที่ถูกต้อง

สำหรับวิธีการแก้ปัญหาอย่างเต็มรูปแบบโปรดดูที่https://developers.google.com/mobile/articles/fast_buttons

หมายเหตุ: คุณไม่สามารถจัดการเหตุการณ์คลิกผีบนพื้นฐานต่อองค์ประกอบ การคลิกที่ล่าช้าจะทำงานตามตำแหน่งหน้าจอดังนั้นหากเหตุการณ์การสัมผัสของคุณแก้ไขหน้าในบางกรณีเหตุการณ์การคลิกจะถูกส่งไปยังหน้าเวอร์ชั่นใหม่


0

มันอาจจะมีประสิทธิภาพในการกำหนดให้กับเหตุการณ์ที่เกิดขึ้น'touchstart mousedown'หรือเพื่อหลีกเลี่ยงการที่ไม่พึงประสงค์ผลข้างเคียงของการใช้'touchend mouseup'click


0

การใช้ประโยชน์จากความจริงที่ว่าการคลิกจะติดตามเหตุการณ์แบบสัมผัสเสมอนี่คือสิ่งที่ฉันทำเพื่อกำจัด "การคลิกแบบผี" โดยไม่ต้องใช้การหมดเวลาหรือการตั้งค่าสถานะทั่วโลก

$('#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เหตุการณ์เกิดเพลิงไหม้ที่องค์ประกอบให้ตั้งค่าสถานะจากนั้นจึงลบ (และละเว้น) เมื่อคลิกมา


0

ทำไมไม่ใช้ 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
};

1
แนะนำให้ใช้การตรวจจับคุณสมบัติมากกว่าการดมกลิ่นตัวแทนผู้ใช้
jinglesthula

ตัวจับคืออุปกรณ์ที่รองรับการสัมผัสและคลิก
ดา

0

หากคุณใช้ 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));

โซลูชันอื่น ๆ ก็ดี แต่ฉันผูกพันหลายเหตุการณ์ในลูปและต้องการฟังก์ชันการโทรด้วยตนเองเพื่อสร้างการปิดที่เหมาะสม นอกจากนี้ฉันไม่ต้องการปิดใช้งานการเชื่อมโยงเนื่องจากฉันต้องการให้สามารถเรียกใช้เมื่อคลิก / touchstart ครั้งต่อไป

อาจช่วยคนที่อยู่ในสถานการณ์ที่คล้ายคลึงกัน!


0

สำหรับคุณสมบัติที่เรียบง่ายเพียงจำสัมผัสหรือคลิกฉันใช้รหัสต่อไปนี้:

var element = $("#element");

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

สิ่งนี้จะคืนค่า "สัมผัส" หากกำหนดเหตุการณ์เริ่มต้นและ "ไม่แตะ" ถ้าไม่ทำ อย่างที่ฉันบอกไปว่านี่เป็นวิธีการง่ายๆสำหรับการคลิก / แตะเหตุการณ์เพียงแค่นั้น


0

ฉันพยายามและมันก็ใช้งานได้ (แต่ฉันเท่านั้นบน Android / Phonegap ดังนั้น caveat emptor)

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

ฉันไม่ชอบความจริงที่ว่าฉันเป็นตัวจัดการการผูกมัดใหม่ทุกครั้งที่สัมผัส แต่สิ่งที่ถือว่าเป็นสิ่งที่สัมผัสไม่ได้เกิดขึ้นบ่อยมาก (ในเวลาที่คอมพิวเตอร์!)

ฉันมี TON ของตัวจัดการเหมือนหนึ่งสำหรับ '#keypad' ดังนั้นการมีฟังก์ชั่นง่าย ๆ ที่ช่วยให้ฉันจัดการกับปัญหาที่ไม่มีรหัสมากเกินไปคือสาเหตุที่ฉันไปทางนี้



0

แก้ไข:คำตอบเดิมของฉัน (ตามคำตอบในหัวข้อนี้) ไม่ใช่วิธีที่จะไปหาฉัน ฉันต้องการเมนูย่อยเพื่อขยายเมื่อป้อนเมาส์หรือแตะที่สัมผัสและยุบลงเมื่อปล่อยเมาส์หรือคลิกอีกครั้ง เนื่องจากปกติเหตุการณ์เมาส์จะเริ่มทำงานหลังจากเหตุการณ์การสัมผัสจึงเป็นการยากที่จะเขียนตัวฟังเหตุการณ์ที่รองรับทั้งหน้าจอสัมผัสและการป้อนข้อมูลเมาส์ในเวลาเดียวกัน

ปลั๊กอิน jQuery: แตะหรือเมาส์

ฉันลงเอยด้วยการเขียนปลั๊กอิน jQuery ที่เรียกว่า " Touch Or Mouse " (897 bytes minified) ที่สามารถตรวจสอบว่ามีเหตุการณ์ที่ถูกเรียกใช้โดยหน้าจอสัมผัสหรือเมาส์ (โดยไม่ต้องทดสอบเพื่อรองรับการสัมผัส!) สิ่งนี้ทำให้การสนับสนุนทั้งหน้าจอสัมผัสและเมาส์ในเวลาเดียวกันและแยกกิจกรรมของพวกเขาอย่างสมบูรณ์

วิธีนี้ OP สามารถใช้touchstartหรือtouchendหาได้อย่างรวดเร็วตอบสนองต่อการสัมผัสและการคลิกclickสำหรับการคลิกเรียกเท่านั้นโดยเมาส์

สาธิต

ก่อนอื่นต้องทำคือ เหตุการณ์องค์ประกอบของแทร็กสัมผัสร่างกาย:

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

เหตุการณ์เมาส์ของเราถูกผูกไว้กับองค์ประกอบในวิธีการเริ่มต้นและโดยการโทร$body.touchOrMouse('get', e)เราสามารถค้นหาว่าเหตุการณ์ถูกเรียกโดยหน้าจอสัมผัสหรือเมาส์

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

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

ดูปลั๊กอินที่ทำงานที่http://jsfiddle.net/lmeurs/uo4069nh

คำอธิบาย

  1. ปลั๊กอินนี้จะต้องมีการเรียกเช่น องค์ประกอบของร่างกายที่จะติดตามtouchstartและtouchendกิจกรรมวิธีนี้touchendเหตุการณ์ไม่จำเป็นต้องถูกไล่ออกจากองค์ประกอบทริกเกอร์ (เช่นลิงก์หรือปุ่ม) ระหว่างเหตุการณ์การแตะสองครั้งปลั๊กอินนี้จะพิจารณาเหตุการณ์เมาส์ใด ๆ ที่จะเรียกใช้โดยการสัมผัส
  2. เหตุการณ์เมาส์จะเริ่มทำงานหลังจากtouchendเมื่อเหตุการณ์เมาส์ถูกเริ่มทำงานภายในghostEventDelay(ตัวเลือก 1,000 มิลลิวินาทีตามค่าเริ่มต้น) หลังจากtouchendนั้นปลั๊กอินนี้จะพิจารณากิจกรรมเมาส์ที่จะเรียกใช้โดยการสัมผัส
  3. เมื่อคลิกที่องค์ประกอบโดยใช้หน้าจอสัมผัสองค์ประกอบจะได้รับ:activeสถานะ mouseleaveเหตุการณ์ถูกยิงเฉพาะหลังจากที่สูญเสียองค์ประกอบของรัฐเช่นนี้โดย คลิกที่องค์ประกอบอื่น ตั้งแต่นี้อาจเป็นวินาที (หรือนาที!) หลังจากmouseenterเหตุการณ์ถูกไล่ออกปลั๊กอินนี้จะติดตามmouseenterเหตุการณ์สุดท้ายขององค์ประกอบ: หากmouseenterเหตุการณ์ล่าสุดถูกเรียกโดยการสัมผัสmouseleaveเหตุการณ์ต่อไปนี้จะถูกพิจารณาด้วยการสัมผัส

0

นี่เป็นวิธีง่ายๆในการทำ:

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

โดยทั่วไปคุณบันทึกประเภทเหตุการณ์แรกที่ถูกเรียกไปยังคุณสมบัติ 'ทริกเกอร์' ในวัตถุข้อมูลของ jQuery ที่แนบมากับเอกสารรูทและดำเนินการเฉพาะเมื่อประเภทเหตุการณ์เท่ากับค่าใน 'ทริกเกอร์' บนอุปกรณ์แบบสัมผัสเชนเหตุการณ์น่าจะเป็น 'touchstart' ตามด้วย 'คลิก'; อย่างไรก็ตามตัวจัดการ 'คลิก' จะไม่ทำงานเนื่องจาก "คลิก" ไม่ตรงกับประเภทเหตุการณ์เริ่มต้นที่บันทึกไว้ใน 'ทริกเกอร์' ("touchstart")

สมมติฐานและฉันเชื่อว่าเป็นอุปกรณ์ที่ปลอดภัยนั่นคือสมาร์ทโฟนของคุณจะไม่เปลี่ยนจากอุปกรณ์สัมผัสเป็นอุปกรณ์เมาส์อย่างเป็นธรรมชาติมิฉะนั้นการแตะจะไม่เคยลงทะเบียนเพราะเหตุการณ์ประเภท 'ทริกเกอร์' จะบันทึกเพียงครั้งเดียวต่อ การโหลดหน้าเว็บและ "คลิก" จะไม่ตรงกับ "touchstart"

ต่อไปนี้เป็น codepen ที่คุณสามารถเล่นได้ด้วย (ลองแตะที่ปุ่มบนอุปกรณ์ระบบสัมผัส - ไม่ควรมีความล่าช้าในการคลิก): http://codepen.io/thdoan/pen/xVVrOZ

ฉันยังใช้สิ่งนี้เป็นปลั๊กอิน jQuery ง่าย ๆ ที่สนับสนุนการกรองลูกหลานของ jQuery โดยผ่านสตริงตัวเลือก:

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


ทำไมคุณถึงใช้คุณสมบัติข้อมูลบนเอกสารแทนที่จะใช้เพียงตัวแปร?
Raphael Schweikert

@RaphaelSchweikert อาจเป็นเพราะนิสัย ไม่มีทางที่ถูกหรือผิด แต่ฉันชอบที่จะใช้$.data()เพื่อจัดเก็บข้อมูลที่สามารถเข้าถึงได้โดยฟังก์ชั่นหลายอย่าง แต่ไม่ได้อยู่ในขอบเขตทั่วโลก ข้อดีคือเพราะฉันเก็บไว้ในองค์ประกอบมันทำให้ฉันมีบริบทมากขึ้น
thdoan

0

วิธีที่ดีที่สุดที่ฉันพบคือการเขียนเหตุการณ์การสัมผัสและให้เหตุการณ์นั้นเรียกเหตุการณ์การคลิกปกติโดยทางโปรแกรม วิธีนี้ทำให้คุณมีเหตุการณ์การคลิกปกติทั้งหมดของคุณและจากนั้นคุณต้องเพิ่มตัวจัดการเหตุการณ์เพียงหนึ่งตัวสำหรับกิจกรรมการสัมผัสทั้งหมด สำหรับทุกโหนดที่คุณต้องการให้แตะได้เพียงเพิ่มคลาส "แตะ" เพื่อเรียกใช้ตัวจัดการการสัมผัส ด้วย Jquery มันใช้งานได้ดีกับตรรกะบางอย่างเพื่อให้แน่ใจว่าเป็นเหตุการณ์ที่สัมผัสได้จริงและไม่ใช่สิ่งที่ผิดพลาด

$("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
});
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.