การป้องกันเหตุการณ์คลิกด้วยการลากและวาง jQuery


86

ฉันมีองค์ประกอบในเพจที่ลากได้ด้วย jQuery องค์ประกอบเหล่านี้มีเหตุการณ์การคลิกที่นำทางไปยังหน้าอื่นหรือไม่ (เช่นลิงก์ธรรมดา)

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

ฉันมีปัญหากับองค์ประกอบที่เรียงลำดับได้ แต่คิดว่าเป็นการดีที่จะมีวิธีแก้ปัญหาสำหรับการลากและวางทั่วไป

ฉันได้แก้ปัญหาให้ตัวเองแล้ว หลังจากนั้นฉันพบว่ามีวิธีแก้ปัญหาเดียวกันสำหรับ Scriptaculousแต่อาจมีใครบางคนมีวิธีที่ดีกว่าในการบรรลุเป้าหมายนั้น


คล้าย / ซ้ำกัน: stackoverflow.com/questions/3486760/…
Ryan

คำตอบ:


89

วิธีแก้ปัญหาที่ใช้งานได้ดีสำหรับฉันและไม่ต้องการการหมดเวลา: (ใช่ฉันเป็นคนอวดดีนิดหน่อย ;-)

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

$('your selector').draggable({
    start: function(event, ui) {
        $(this).addClass('noclick');
    }
});

$('your selector').click(function(event) {
    if ($(this).hasClass('noclick')) {
        $(this).removeClass('noclick');
    }
    else {
        // actual click event code
    }
});

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

2
สิ่งนี้นำฉันไปสู่การแก้ปัญหาของฉันฉันเพิ่งใช้ฟังก์ชันข้อมูล jQuery แทนคลาส ขอบคุณ.
William

3
แต่เบราว์เซอร์จะไม่ทริกเกอร์เหตุการณ์การคลิกทุกครั้งที่คุณวางองค์ประกอบ
puchu

6
หากต้องการบันทึกการตั้งค่าคลาสหรือข้อมูลในตัวจัดการเหตุการณ์การคลิกคุณยังสามารถใช้: if (! $ (this) .is ('. ui-draggable-drag')) {... click action}
ChrisV

1
อ๊ะคุณไม่ต้องการstop: function(event, ui) { $(this).removeClass('noclick'); }ในตัวจัดการลากได้หรือไม่? มิฉะนั้นทุกครั้งที่ลากแล้วปล่อยที่อื่นจะไม่ขัดขวางการคลิกครั้งต่อไปที่ "ตัวเลือกของคุณ"
Bob Stein

41

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

วิธีการจัดเรียง:

...
.sortable({
...
        start: function(event, ui) {
            ui.item.bind("click.prevent",
                function(event) { event.preventDefault(); });
        },
        stop: function(event, ui) {
            setTimeout(function(){ui.item.unbind("click.prevent");}, 300);
        }
...
})

วิธีการลากได้:

...
.draggable({
...
        start: function(event, ui) {
            ui.helper.bind("click.prevent",
                function(event) { event.preventDefault(); });
        },
        stop: function(event, ui) {
            setTimeout(function(){ui.helper.unbind("click.prevent");}, 300);
        }
...
})

2
ดูเหมือนว่าสิ่งนี้ไม่สามารถป้องกันเหตุการณ์การคลิกสำหรับฉันฉันใช้ jquery 1.9.1 และ jquery.ui 1.10.3
aaron

1
แปลกที่คุณตอบคำถามของตัวเองแล้วมันก็ไม่ได้ผล Sasha lol

@aaron คำตอบด้านล่างใช้งานได้: http://stackoverflow.com/a/4572831/119765
lol

อีกคำตอบง่ายๆ - เพียงแค่วาง th jQuery .click () handler หลัง. draggable () stackoverflow.com/questions/18032136/…
JxAxMxIxN

1
หากคุณส่งตัวช่วยแอตทริบิวต์ที่มีค่า "โคลน" จะหลีกเลี่ยงการเรียกเหตุการณ์ไปยังรายการที่ลาก - เรียงลำดับ .. {helper: 'clone', start, stop ... etc},
Luchux

12

ฉันมีปัญหาเดียวกันและลองใช้หลายวิธี แต่ก็ไม่มีอะไรได้ผลสำหรับฉัน

แนวทางแก้ไข 1

$('.item').click(function(e)
{            
    if ( $(this).is('.ui-draggable-dragging') ) return false;
});  

ไม่ทำอะไรให้ฉัน รายการจะถูกคลิกหลังจากการลากเสร็จสิ้น

โซลูชันที่ 2 (โดย Tom de Boer)

$('.item').draggable(
{   
    stop: function(event, ui) 
    {
         $( event.originalEvent.target).one('click', function(e){ e.stopImmediatePropagation(); } );
    }
});

สิ่งนี้ใช้งานได้ดี แต่ล้มเหลวในกรณีเดียว - เมื่อฉันเปิดเต็มหน้าจอคลิก:

var body = $('body')[0];     
req = body.requestFullScreen || body.webkitRequestFullScreen || body.mozRequestFullScreen;
req.call(body); 

โซลูชันที่ 3 (โดย Sasha Yanovets)

 $('.item').draggable({
        start: function(event, ui) {
            ui.helper.bind("click.prevent",
                function(event) { event.preventDefault(); });
        },
        stop: function(event, ui) {
            setTimeout(function(){ui.helper.unbind("click.prevent");}, 300);
        }
})

สิ่งนี้ไม่ได้ผลสำหรับฉัน

โซลูชันที่ 4- วิธีเดียวที่ใช้งานได้ดี

$('.item').draggable(
{   
});
$('.item').click(function(e)
{  
});

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


1
สิ่งเดียวที่ใช้ได้กับฉัน (ใน IE11 / Edge) คือโซลูชัน 4 ขอบคุณ!
Simon

1
# 4 เป็นวิธีแก้ปัญหาที่ถูกต้องและง่ายที่สุดสำหรับฉันโดยไม่ต้องใช้คลาสหรือตัวแปร
electrotype

11

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


9

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

var el, dragged

el = $( '#some_element' );

el.on( 'mousedown', onMouseDown );
el.on( 'mouseup', onMouseUp );
el.draggable( { start: onStartDrag } );

onMouseDown = function( ) {
  dragged = false;
}

onMouseUp = function( ) {
  if( !dragged ) {
    console.log('no drag, normal click')
  }
}

onStartDrag = function( ) {
  dragged = true;
}

ร็อคโซลิด ..


นี่เป็นทางออกที่ดีที่สุดสำหรับฉัน ฉันมีปัญหาในการรับเหตุการณ์การคลิกเพื่อทำงานบน jquery UI ที่เรียงลำดับได้ด้วยในเว็บเบราว์เซอร์มือถือโดยใช้ jquery.ui.touch.punch.js แต่สิ่งนี้แก้ไขได้ค่อนข้างสวยงาม
Godsmith

3

เวอร์ชันของ lex82 แต่สำหรับ. shortable ()

 start: function(event, ui){
 ui.item.find('.ui-widget-header').addClass('noclick');
 },

และคุณอาจต้องการเพียง:

 start: function(event, ui){
 ui.item.addClass('noclick');
 },

และนี่คือสิ่งที่ฉันใช้สำหรับการสลับ:

$("#datasign-widgets .ui-widget-header").click(function(){
if ($(this).hasClass('noclick')) {
$(this).removeClass('noclick');

}
else {
$(this).next().slideToggle();
$(this).find('.ui-icon').toggleClass("ui-icon-minusthick").toggleClass("ui-icon-plusthick");
}
});

2
ดูเหมือนว่า jquery UI จะต่อท้ายคลาส 'ui-sortable-helper' กับรายการที่กำลังจัดเรียง ดังนั้นคุณสามารถละทิ้งคลาส 'noclick' และทำถ้า ($ (this) .hasClass ('ui-sortable-helper')) รวบรัดกว่านั้น
Matt De Leon

3

ทางเลือกที่เป็นไปได้สำหรับคำตอบของ Sasha โดยไม่ป้องกันค่าเริ่มต้น:

var tmp_handler;
.sortable({
        start : function(event,ui){
            tmp_handler = ui.item.data("events").click[0].handler;
            ui.item.off();
        },
        stop : function(event,ui){
            setTimeout(function(){ui.item.on("click", tmp_handler)}, 300);
        },

3

ใน jQuery UI องค์ประกอบที่ลากจะได้รับคลาส "ui-draggable-drag"
เราจึงสามารถใช้คลาสนี้เพื่อพิจารณาว่าจะคลิกหรือไม่เพียงแค่เลื่อนเหตุการณ์ออกไป
คุณไม่จำเป็นต้องใช้ฟังก์ชันเรียกกลับ "เริ่ม" หรือ "หยุด" เพียงทำดังนี้

$('#foo').on('mouseup', function () {
    if (! $(this).hasClass('ui-draggable-dragging')) {
        // your click function
    }
});

สิ่งนี้ถูกทริกเกอร์จาก "mouseup" แทนที่จะเป็น "mousedown" หรือ "click" ดังนั้นจึงมีความล่าช้าเล็กน้อยอาจไม่สมบูรณ์แบบ - แต่ง่ายกว่าวิธีแก้ปัญหาอื่น ๆ ที่แนะนำที่นี่


1

หลังจากอ่านเรื่องนี้และไม่กี่หัวข้อนี่เป็นวิธีแก้ปัญหาที่ฉันใช้

var dragging = false;
$("#sortable").mouseover(function() {
    $(this).parent().sortable({
        start: function(event, ui) {
            dragging = true;
        },
        stop: function(event, ui) {
            // Update Code here
        }
    })
});
$("#sortable").click(function(mouseEvent){
    if (!dragging) {
        alert($(this).attr("id"));
    } else {
        dragging = false;
    }
});

1

ในกรณีของฉันมันใช้งานได้ดังนี้:

$('#draggable').draggable({
  start: function(event, ui) {
    $(event.toElement).one('click', function(e) { e.stopPropagation(); });
  }
});

0

คุณได้ลองปิดการใช้งานลิงก์โดยใช้ event.preventDefault () หรือไม่ ในเหตุการณ์เริ่มต้นและเปิดใช้งานอีกครั้งในเหตุการณ์ลากหยุดหรือเหตุการณ์ปล่อยโดยใช้การเลิกผูก?


0

เพียงแค่ริ้วรอยเล็กน้อยเพื่อเพิ่มคำตอบที่ให้ไว้ข้างต้น ฉันต้องสร้าง div ที่มีองค์ประกอบ SalesForce ที่ลากได้ แต่องค์ประกอบ SalesForce มีการกระทำแบบ onclick ที่กำหนดไว้ใน html ผ่าน VisualForce gobbledigook

เห็นได้ชัดว่าสิ่งนี้ละเมิดกฎ "กำหนดการคลิกหลังจากการดำเนินการลาก" ดังนั้นในการแก้ปัญหาฉันจึงนิยามการกระทำขององค์ประกอบ SalesForce ใหม่ให้เรียกใช้ "onDblClick" และใช้โค้ดนี้สำหรับคอนเทนเนอร์ div:

$(this).draggable({
        zIndex: 999,
        revert: true,
        revertDuration: 0,
        start: function(event, ui) {
                   $(this).addClass('noclick');
                }
});

$(this).click(function(){
    if( $(this).hasClass('noclick'))
    {
        $(this).removeClass('noclick');
    }
    else
    {
        $(this).children(":first").trigger('dblclick');
    }
});

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


0

ฉันลองสิ่งนี้:

var dragging = true; 

$(this).click(function(){
  if(!dragging){
    do str...
  }
});

$(this).draggable({
  start: function(event, ui) {
      dragging = true;
  },

  stop: function(event, ui) {
      setTimeout(function(){dragging = false;}, 300);
  }

});

0

สำหรับฉันช่วยส่งตัวช่วยในวัตถุตัวเลือกเป็น:

.sortable({
   helper : 'clone', 
   start:function(), 
   stop:function(),
   .....
});

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


0

การจัดงาน onmousedown และ onmouseup ทำงานในโครงการเล็ก ๆ ของฉัน

var mousePos = [0,0];
function startClick()
{
    mousePos = [event.clientX,event.clientY];
}
        
function endClick()
{
    if ( event.clientX != mousePos[0] && event.clientY != mousePos[1] )
    {
        alert( "DRAG CLICK" );
    }
    else
    {
        alert( "CLICK" );
    }
}
<img src=".." onmousedown="startClick();" onmouseup="endClick();" />

ใช่ฉันรู้. ไม่ใช่วิธีที่สะอาดที่สุด แต่คุณเข้าใจ


0

วิธีแก้ปัญหาที่ง่ายและมีประสิทธิภาพที่สุด? เพียงแค่สร้างองค์ประกอบโปร่งใสบนลากได้ของคุณ

.click-passthrough {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  background: transparent;
}

element.draggable({        
        start: function () {

        },
        drag: function(event, ui) {
            // important! if create the 'cover' in start, then you will not see click events at all
                  if (!element.find('.click-passthrough').length) {
                      element.append("<div class='click-passthrough'></div>");
                  }
        },
        stop: function() {
          // remove the cover
          element.find('.click-passthrough').remove();
        }
    });
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.