Verhindern eines Klickereignisses durch Ziehen und Ablegen von jQuery


85

Ich habe Elemente auf der Seite, die mit jQuery gezogen werden können. Haben diese Elemente ein Klickereignis, das zu einer anderen Seite navigiert (z. B. normale Links)?

Was ist der beste Weg, um zu verhindern, dass beim Löschen eines solchen Elements ein Klick ausgelöst wird, während das Klicken nicht per Drag & Drop ausgeführt wird?

Ich habe dieses Problem mit sortierbaren Elementen, denke aber, dass es gut ist, eine Lösung für allgemeines Drag & Drop zu haben.

Ich habe das Problem für mich gelöst. Danach stellte ich fest, dass es für Scriptaculous dieselbe Lösung gibt , aber vielleicht hat jemand einen besseren Weg, dies zu erreichen.


Antworten:


88

Eine Lösung, die für mich gut funktioniert hat und keine Auszeit erfordert: (Ja, ich bin ein bisschen pedantisch ;-)

Ich füge dem Element eine Markierungsklasse hinzu, wenn das Ziehen beginnt, z. B. 'noclick'. Wenn das Element gelöscht wird, wird das Klickereignis ausgelöst - genauer gesagt, wenn das Ziehen endet, muss es nicht auf ein gültiges Ziel abgelegt werden. Im Klick-Handler entferne ich die Markierungsklasse, falls vorhanden, andernfalls wird der Klick normal behandelt.

$('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
Nützlich! Ich habe nicht auf einmal für mich gearbeitet, wahrscheinlich weil ich in früheren Versuchen das Gleiche durcheinander gebracht habe und die Klassen auf einem anderen Element gelandet sind als dem, auf das geklickt wurde. Aber trotzdem habe ich eine globale Variable verwendet (was extrem ist einfache Seite war ok), und das hat auch ganz gut funktioniert.
MSpreij

2
Dies führte mich zu meiner Lösung. Ich habe nur die jQuery-Datenfunktion anstelle einer Klasse verwendet. Vielen Dank.
William

3
Aber der Browser löst nicht jedes Mal ein
Klickereignis aus,

6
Um das Festlegen einer Klasse oder von Daten zu speichern, können Sie im Click-Event-Handler auch
Folgendes

1
Hoppla, willst du nicht stop: function(event, ui) { $(this).removeClass('noclick'); }in den ziehbaren Handler? Andernfalls wird nicht jedes Ziehen und Ablegen irgendwo anders den nächsten Klick auf "Ihre Auswahl" vereiteln.
Bob Stein

41

Die Lösung besteht darin, einen Klick-Handler hinzuzufügen, der verhindert, dass sich der Klick beim Start des Ziehens ausbreitet. Entfernen Sie dann diesen Handler, nachdem das Ablegen ausgeführt wurde. Die letzte Aktion sollte etwas verzögert werden, damit die Klickverhinderung funktioniert.

Lösung zum Sortieren:

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

Lösung für Draggable:

...
.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
scheint könnte dies das Click - Ereignis für mich nicht verhindern, im mit jquery 1.9.1 und 1.10.3 jquery.ui
aaron

1
seltsam, dass du deine eigene Frage beantwortet hast und es nicht einmal funktioniert, Sasha lol.

@ Aaron diese Antwort unten funktioniert: http://stackoverflow.com/a/4572831/119765
lol

Eine weitere einfache Antwort - platzieren Sie einfach den jQuery .click () Handler nach .draggable () stackoverflow.com/questions/18032136/…
JxAxMxIxN

1
Wenn Sie den Attribut-Helfer mit dem Wert 'Klon' übergeben, wird vermieden, dass das Ereignis für das gezogene sortierte Element ausgelöst wird. {Helfer: 'Klon', Start, Stopp ... usw.},
Luchux

12

Ich hatte das gleiche Problem und versuchte mehrere Ansätze und keiner funktionierte für mich.

Lösung 1

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

tut nichts für mich Das Element wird angeklickt, nachdem das Ziehen abgeschlossen ist.

Lösung 2 (von Tom de Boer)

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

Dies funktioniert einwandfrei, schlägt jedoch in einem Fall fehl - als ich auf Vollbild ging, klicken Sie auf:

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

Lösung 3 (von 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);
        }
})

Das funktioniert bei mir nicht.

Lösung 4 - die einzige, die gut funktioniert hat

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

Ja, das ist es - die richtige Reihenfolge macht den Trick - zuerst müssen Sie draggable () binden und dann click (). Selbst wenn ich den Vollbild-Umschaltcode in click () eingefügt habe, wurde er beim Ziehen nicht in den Vollbildmodus verschoben. Perfekt für mich!


1
Nur eine, die für mich (in IE11 / Edge) funktioniert hat, war Lösung 4. Danke!
Simon

1
# 4 ist die richtige und einfachste Lösung für mich, ohne Klassen oder Variablen zu verwenden.
Elektrotyp

11

Ich möchte hinzufügen, dass das Klickereignis anscheinend nur funktioniert, wenn das Klickereignis NACH dem ziehbaren oder sortierbaren Ereignis definiert ist. Wenn der Klick zuerst hinzugefügt wird, wird er beim Ziehen aktiviert.


9

Ich mag es nicht wirklich, Timer zu verwenden oder zu verhindern. Also habe ich Folgendes getan:

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

Rocksolid ..


Dies war die beste Lösung für mich. Ich hatte Probleme, Klickereignisse dazu zu bringen, auf einer jquery-Benutzeroberfläche zu arbeiten, mit der in einem mobilen Webbrowser mit jquery.ui.touch.punch.js sortiert werden kann, aber dies löste es ziemlich elegant.
Godsmith

3

lex82's version aber für .sortable ()

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

und Sie brauchen möglicherweise nur:

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

und hier ist, was ich für den Umschalter verwende:

$("#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
Es scheint, dass die jquery-Benutzeroberfläche die Klasse 'ui-sortable-helper' an das zu sortierende Element anfügt. Sie können also die 'noclick'-Klasse weglassen und einfach if ($ (this) .hasClass (' ui-sortable-helper ')) ausführen. Auf diese Weise knapper
Matt De Leon

3

Eine mögliche Alternative für Sashas Antwort, ohne die Standardeinstellung zu verhindern:

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

In der jQuery-Benutzeroberfläche erhalten Elemente, die gezogen werden, die Klasse "ui-draggable-dragging".
Wir können diese Klasse daher verwenden, um zu bestimmen, ob geklickt werden soll oder nicht. Verzögern Sie einfach das Ereignis.
Sie müssen die Rückruffunktionen "Start" oder "Stop" nicht verwenden. Führen Sie einfach Folgendes aus:

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

Dies wird eher durch "Mouseup" als durch "Mousedown" oder "Click" ausgelöst - es gibt also eine leichte Verzögerung, die möglicherweise nicht perfekt ist -, aber es ist einfacher als andere hier vorgeschlagene Lösungen.


1

Nachdem ich dies und einige Themen durchgelesen hatte, war dies die Lösung, mit der ich mich entschieden habe.

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

In meinem Fall hat es so funktioniert:

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

0

Haben Sie versucht, den Link mit event.preventDefault () zu deaktivieren? im Startereignis und erneutes Aktivieren im Drag-Stop-Ereignis oder Drop-Ereignis mit Unbind?


0

Nur eine kleine Falte, um die oben gegebenen Antworten zu ergänzen. Ich musste ein div, das ein SalesForce-Element enthält, ziehbar machen, aber das SalesForce-Element verfügt über eine On-Click-Aktion, die im HTML-Code durch ein VisualForce-Gobbledigook definiert ist.

Offensichtlich verstößt dies gegen die Regel "Klickaktion nach der Ziehaktion definieren". Als Problemumgehung habe ich die Aktion des SalesForce-Elements neu definiert, die "onDblClick" ausgelöst werden soll, und diesen Code für das Container-Div verwendet:

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

Das Klickereignis der Eltern verbirgt im Wesentlichen die Notwendigkeit, auf das untergeordnete Element zu doppelklicken, wodurch die Benutzererfahrung erhalten bleibt.


0

Ich habe es so versucht:

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

Für mich half es, den Helfer im Optionsobjekt zu übergeben als:

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

Das Ziehen des gezogenen dom-Elements scheint das Sprudeln des Ereignisses verhindert zu haben. Ich konnte es mit keinem Ereignis vermeiden. Propagation, Sprudeln usw. Dies war die einzige funktionierende Lösung für mich.


0

Die Onmousedown- und Onmouseup-Events haben in einem meiner kleineren Projekte funktioniert.

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();" />

Ja, ich weiß. Nicht der sauberste Weg, aber Sie bekommen die Idee.


0

die einfachste und robusteste Lösung? Erstellen Sie einfach ein transparentes Element über Ihrem Draggable.

.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();
        }
    });
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.