Mousemoveevent für Drag & Drop

Noeden

Erfahrenes Mitglied
Hallo,
ich versuche jetzt seit 3 Stunden es mir schön zu machen, aber es will nicht. Also ich möchte Drag&Drop selber programmieren. jQuery benutze ich aber. Ich bin jetzt an dem Punkt, wo mein helper der Maus folgen soll. Im Internet gibt es ja auch Milliarden Sachen zu, aber bei mir will es dennoch nicht. Wie weise ich meiner Funktion zum Positionieren das onmousemove-Event zu? Ist es richtig, dass die Funktion immer dabei ausgeführt wird und als erstes geprüft wird, ob sie überhaupt benötigt wird? mein Ansatz:

Code:
function posDraggerAktual(e){
	if(draggingstatus){
		posDragger (e);
	}
}
document.onmousemove = posDraggerAktual();

posDragger positioniert den Helper. Funktioniert auch ganz gut, halt nur nicht in Verbindung mit dem onmousemove-Event.

Code:
$("*").onmousemove(function(){});

ist denke ich auch eine Murkslösung oder?

Danke
 
Ich weiß nicht was genau du willst, aber hier mal ein Beispiel um ein Element zu draggen. Anstatt mit einer Variable ständig zu prüfen, ob wir gerade draggen, entferne ich einfach den Eventhandler wenn er nicht benötigt wird.

HTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
	<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
	
	<title>Drag</title>
	
	<style type="text/css">
		#drag {
			width:100px;
			height:100px;
			background:red;
			cursor:move;
		}
	</style>
</head>

<body>
	<div id="drag"></div>
	
	<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js"></script>
	<script type="text/javascript">
	/* <![CDATA[ */
	
	$(function() {
		var
			elem = $('#drag'),
			doc = $(document),
			xOff = 0,
			yOff = 0;
		
		//Wird aufgerufen, wenn die Maus gedrückt wird
		function onDragStart(e) {
			//Die drei Zeilen sind nur für schön. Damit bleibt der Mauszeiger relativ zum Objekt und das Objekt springt zu Beginn nicht.
			var off = elem.offset();
			xOff = e.pageX - off.left;
			yOff = e.pageY - off.top;
			
			//Hier setzen wir erst den Listener für das mousemove-Event. Damit er nicht unnötig Events produziert, obwohl wir gar nicht draggen.
			doc.mousemove(onDrag);
		}
		
		//Wird bei jeder Mausbewegung während des drags aufgerufen
		function onDrag(e) {
			elem.css({
				position: 'absolute',
				left: e.pageX - xOff,
				top: e.pageY - yOff
			});
		}
		
		//Browser standard verhindern
		elem
			.bind('dragstart', function() {return false;})
			.mousedown(onDragStart);
		
		//Wenn wir irgendwo die Maus wieder loslassen, entfernen wir den Listener, da er nicht gebraucht wird.
		doc.mouseup(function() {
			doc.unbind('mousemove', onDrag);
		});
	});
	/* ]]> */
	</script>
</body>

</html>
 
Hey cool, danke!
Also das mit der Positionierung etc. habe ich schon. Das Interessante ist wohl das Ende.

Erstmal aber eine andere Frage: Warum sind hier Kommata:
Code:
var
			elem = $('#drag'),
			doc = $(document),
			xOff = 0,
			yOff = 0;

und hier nicht:

Code:
elem
			.bind('dragstart', function() {return false;})
			.mousedown(onDragStart);

Und warum schreibst Du das so? Ist es nur schöner oder auch besser?

Dann bei dem zweiten Code-Schnipsel, die Funktion die nur false zurückgibt ist dazu da, dass der Browser nicht auf die Idee kommt etwas anderes zu machen. Was ist aber dieses 'dragstart'-Event? Erfindest Du das gerade um es anderweitig zu benutzen? Danach wird die Funktion onDragStart bei mousedown ausgeführt.

Für den Rest danke ich Dir sehr und Du hast mir mega weitergeholfen. DANKE

PS: Kann das sein, dass ich mit der Methode Probleme habe, wenn ich über ein iframe dragge? Wird in einem iframe neu positioniert? Danke


PPS: Also bei mir Ruckelt es. Und solange ich die Maus gedrückt halte, verfolgt der Klon nicht die Maus. Hat dein Event dragstart damit etwas am Hut? ;)

Danke!

Code:
	//Bewegbarmachen der Elemente
	jQuery.fn.dragme = function(){
						return this.each(function(){
							$(this).addClass("dragmeable");
							if($(this).children().is(".dat-icon")){
								$(this).children(".dat-icon").addClass("draghandler");
							}else{
								$(this).addClass("draghandler");
							}
						});
					}



	function posDragger (e) {
			var dragelement = $(".dragging");
			var draghandler = dragelement.children(".draghandler");
			var handlerpos = draghandler.offset();
			var dragelementpos = dragelement.offset();
			var handlerposLeft = handlerpos.left - dragelementpos.left;
			var handlerposTop = handlerpos.top - dragelementpos.top;
			var dragelempaddingleft = dragelement.css("padding-left").slice(0, dragelement.css("padding-left").length-2);
			var dragelempaddingtop = dragelement.css("padding-top").slice(0, dragelement.css("padding-top").length-2);
			var left = e.pageX - handlerposLeft - dragelempaddingleft - draghandler.width()/2;
			var top = e.pageY - handlerposTop - dragelempaddingtop - draghandler.height()/2;
			dragelement.css({
					position: 'absolute',
					left: left, 
					top: top
					});
	}

	$(".draghandler").live('mousedown', function(e){
		dragelement= $(this).parent().clone().css("z-index", "1000").removeClass("dragmeable").addClass("dragging");
		dragelement.appendTo('body');
		posDragger(e);
		$(".dragfield").bind('mouseup', function(){
			//dragelement.remove();
			//$(".dragfield").unbind('mousemove', posDragger);
		});
		$(".dragfield").mousemove (posDragger);
	});

Wenn Du auch noch andere Tipps zu meinem Hunzcode hast, wäre das sehr nett. Der ist heute enstanden und nicht nur, dass ich ein Anfänger bin, ich bin heute auch noch besonders verstreut :D danke
 
Zuletzt bearbeitet:
Warum sind hier Kommata:

Das ist einfach eine Kurzschreibweise (die Bytes spart). Du könntest auch jedes mal "var" benutzen, aber man kann auch einfach mehrere Variablen mit Komma getrennt deklarieren. Die Zeilenumbrüche sind für die Lesbarkeit.

Javascript:
var elem = $('#drag');
var doc = $(document);
var xOff = 0;
var yOff = 0;


Auch das ist einfach eine Kurzschreibweise (die Bytes spart). jQuery erlaubt chaining, also das Aneinander-hängen mehrere Methoden für das selbe Objekt direkt aneinander. Die Zeilenumbrüche sind wieder nur für die Lesbarkeit.

Javascript:
//Alle in eine Zeile. Wird immer schwerer zu lesen
elem.bind('dragstart', function() {return false;}).mousedown(onDragStart);

//Zwei getrennte Aufrufe -> mehr Bytes weil "elem" doppelt kommt.
elem.bind('dragstart', function() {return false;});
elem.mousedown(onDragStart);


Dann bei dem zweiten Code-Schnipsel, die Funktion die nur false zurückgibt ist dazu da, dass der Browser nicht auf die Idee kommt etwas anderes zu machen. Was ist aber dieses 'dragstart'-Event? Erfindest Du das gerade um es anderweitig zu benutzen? Danach wird die Funktion onDragStart bei mousedown ausgeführt.

"dragstart" ist ein natives Event in modernen Browsern (https://developer.mozilla.org/en/dragdrop/drag_and_drop). Wenn du das nicht abfängst, dann passiert es, dass du manchmal das DIV nicht draggen kannst, sondern der Browser ein natives dragging macht. Da hat man dann so eine leicht transparente Kopie von dem Element und kann es im Fall von z.B. Bildern in die Adressleiste ziehen und kommt zur Adresse des Bildes.


PS: Kann das sein, dass ich mit der Methode Probleme habe, wenn ich über ein iframe dragge? Wird in einem iframe neu positioniert?

Wenn du über einem IFrame bist, hast du Probleme mit dem mousemove-Event, weil du ja gar nicht mehr in dem selben "document" bist! Sollte funktionieren, wenn du stattdessen "window" nimmst.


PPS: Also bei mir Ruckelt es. Und solange ich die Maus gedrückt halte, verfolgt der Klon nicht die Maus. Hat dein Event dragstart damit etwas am Hut? ;)

Ich kann in Firefox, Chromium und Opera das rote DIV wie ein verrückter durch die Gegend ziehen ohne Probleme. Eventuell machst du bei mousemove zu viele Dinge. Das Event wird SEHR häufig gefeuert. Ich hatte da schon mal Performance Probleme, weil ich jedes mal "position()" von einem Element aufgerufen hatte. Nach dem ich mir dann den Quelltext von position (und vorallem offset) angeguckt habe, war mir auch klar wieso. Musste das dann ändern und die Probleme waren weg.


Jetzt zu deinem Plugin:
Was sofort auffällt: Du rufst sogar zwei mal "offset" auf :-D
Du kannst bestimmt viele der Berechnung in die mousedown Funktion stecken. Und "width()" wird sich wohl auch nicht ändern während des draggings.
Und dadurch, dass du "unbind" auskommentiert hast, bekommst du pro mousedown einen weiteren Event handler dazu. Also wird die posDragger Funktion noch häufiger ausgeführt als sie es ohnehin schon würde.

Ich hab mal die eine Funktion etwas verkürzt. Bei den anderen musst du selbst mal gucken. Vielleicht könntest du mal ein Beispiel samt HTML und CSS geben, damit ich mir mal angucken kann, wie es überhaupt aussehen soll.

Javascript:
jQuery.fn.dragme = function(){
	if( !this.children('.dat-icon').addClass('draghandler').size() ) {
		this.addClass('draghandler');
	}
	
	return this.addClass('dragmeable');
}


Das dürfte der längste Post sein, den ich bisher geschrieben habe :-D
 
:-D Danke für deine Mühen :)
Ich bin leider krank, deshalb bin ich nu bissl langsam. Das Remove hatte ich extra auskommentiert. Zum testen. Ich glaube, dass das dragevent vom Firefox dazwischengefudelt hat (denn ich sah eine solche automatische kopie)
Und dann soll ich die Werte für die Position des Handlers (mein knopf an dem ich das Element durch die Gegend zerre) bzw. für die Verschiebung rel. zur Maus in globalen Variablen speichern und dann immer wieder verwenden. Und beim mousedown dann gegebenenfalls verändern.

Ich leg mich noch etwas hin und überarbeite dann nochmal meinen Code und poste ihn hier. Wäre super nett, wenn Du dann nochmal drüberschauen könntest ;)


Bis dann
Noeden
 
Zuletzt bearbeitet:
Also erstmal habe ich noch eine Frage:

Wann this und wann $(this). Folgende Erklärung fand ich im Internet

3. Finally, when you turn this into $(this),
you are creating a JQuery object out of, well, this.
Turning a DOM element (the A in this case) into a JQuery object
allows you to use all the JQuery functions on it. We do not really
need to do that here, but just as a demonstration, we use $(this)
to fade the menu item out and in. We need to turn the A into a
JQuery object so that we can call the JQuery fadeOut
and fadeIn functions.

Wann muss ich aus this ein jQuery-Objekt machen? Oder muss ich das hier in keinem Falle, da jQuery-Funktionen nur auf jQuery-Objekte angewendet werden, "this" also eh schon eins ist und dann spare ich mir die 3 Zeichen?
(Anmerkung: Habe erstmal überall this anstatt von $(this) eingefügt, da wo er gemeckert hat, hab ich es wieder geändert, habs also doch noch nicht gerafft)

Ja ich bin ein wenig Begriffsstutzig :D Hier mein neuer Code, wie Du sehen wirst, habe ich die Ausrichtung des helpers von Dir geklaut, habe mir nämlich überlegt, dass es schöner, und programmiertechnisch einfacher ist, den Helper relativ zur Maus zu positionieren, als dass der beim mousedown mit dem draghandler zur Maus springt.

Code:
$(document).ready(function(){
//Deklaration der Variablen
var 
		drag = $(".draghandler"),
		drag_helper = '',
		drag_x = '',
		drag_y = '',
		drag_field = $(window);

//Bewegbarmachen der Elemente
jQuery.fn.dragme = function(){
	return this.each(function() {
		if( !$(this).children(".dat-icon").addClass('draghandler').size() ){
			$(this).addClass("draghandler");
		}
		return $(this).addClass("dragmeable");
	});
}

//Beim Verschieben Positionierung
function onDrag (e){
	drag_helper.css({left: e.pageX - drag_x, top: e.pageY - drag_y});
};

drag
	.live('dragstart', function() {return false;})
	.live('mousedown', function(e){
		drag_helper = $(this).parent().clone().removeClass("dragmeable").addClass("dragging").appendTo('body');
		var drag_par = $(this).parent();
		var o = drag_par.offset();
		drag_x = e.pageX - o.left;
		drag_y = e.pageY - o.top;
		onDrag(e);
		drag_field.mousemove(onDrag);
		//iframes bedecken, wegen draglaggs
		$("iframe").each(function(){
			var 	ift = $(this),
				ifo = ift.offset();
				$('<div></div>').addClass("iframelayer").css({top: ifo.top+'px', left: ifo.left+'px', height: ift.height()+'px', width: ift.width()+'px'}).appendTo('body');
		});
		drag_field.mouseup(function(){
			drag_field.unbind('mousemove', onDrag);
			drag_helper.remove();
			$(".iframelayer").each(function(){$(this).remove();});
		});
	});

Außerdem habe ich noch layer über alle iframes gelegt, weil auch mit dem window der helper nicht der mausgefolgt ist, sobald diese den Helper durch zu schnelles fahren, verlassen hat. (Der Helper gehört ja zu dem Dokument). Wenn ich jetzt aber etwas in ein iframe packen möchte, muss ich das event von dem layer nehmen und auf das iframe übertragen wa? Ach ich glaub ich drücke mich schon wieder wirr aus ....

DANKE
 
Zu this vs $(this):

Das hängt ganz vom Zusammenhang ab. Wenn du ein jQuery Plugin schreibst (z.B. jQuery.fn.dragme), dann zeigt innerhalb der Funktion "this" auf das entsprechende jQuery Objekt auf welchem das Plugin aufgerufen wurde. Also gibt es keinen Grund es zusätzlich noch mal in die jQuery Funktion zu stecken.

In einem anderen Kontext, also zum Beispiel innerhalb eines "each" Blocks, zeigt "this" auf das DOM Element (siehe Dokumentation), welches aktuell dran ist. Dort brauchst du natürlich die jQuery Funktion, sofern du jQuery Methoden benutzen willst.



Wenn ich jetzt aber etwas in ein iframe packen möchte, muss ich das event von dem layer nehmen und auf das iframe übertragen wa?

Wie habe ich das zu verstehen? Willst du ein Objekt aus dem aktuellen Dokument in das IFrame packen, so dass es plötzlich in einem völlig anderen Dokument erscheint?
 
Ok,

bei dem this schaue ich also jedes mal in die Dokumentation, was benötigt wird und lerne es dadurch. Danke.

Und zu dem iframe: ja das ist ein WYSIWYG-Editor. Da wollte ich per Drag&drop Inhalte einfügen. Hmm, das wird wohl noch ein gefrickel. aber danke, Du hast mit sehr geholfen!
 
Zurück