j4nk3y: jQuery teilweise ausgeführt

Salve,

folgendes Problem welches ich mir nicht erklären kann. Ich versuche meine Javascript/jQuery durch Funktionen logisch zu trennen.
Dabei rufe ich eine Funktion auf und im ersten Schritt werden die benötigten Templates mittels .load(). aus einer externen PHP datei geladen und dann das DOM zusammengebastelt. in der loadIndex ist auch ein Login Formular, welches dann per .on('click') per $.ajax(), die eigegebenen Daten an die login.php schickt. Wenn nun der AJAX Request erfolgreich war und die richtigen Daten von der login.php zurück kommen, soll nun die nächste Funktion ausgeführt werden. Hier werden dann wieder neue Templates geladen. Soweit klappt das auch sehr gut. Danach kommt nun der logische Schritt das DOM neu zu erstellen. aber es scheitert schon an dem ersten Befehl den "Load Screen" zu verstecken.

Aber der Code spricht mehr als 1000 Worte:

$(function(){
	
	var $body = $('body');
	var $loader = $('#loader_wrapper'); // Load Screen Container
	
	loadIndex();
	
	function loadIndex(){
		
		$body.append('<div id="templates"></div>');
		
		$('#templates').load("../../templates/indexTemplates.php", function(){
			
			// Append Elements to the DOM
			
			$body.append('<main class="flex_row" id="main_index"></main>');
			var $main = $('main');
			
			// Append Navigation
			
			$main.append(Mustache.render(...));
			
			// Append login formular
			
			$main.append(Mustache.render(...));
			
			// Attach Eventlistener to Login Button
			
			$('#login').on('click', function(e) { 
				e.preventDefault();
				
				$('#login').hide();
				
				var pass_field_name = $('#pass_field_name').val();
				var data = {
					user: $('#login_user').val(),
					pass_field_name: pass_field_name
				}
				
				data[pass_field_name] = $('#' + pass_field_name).val();
				
				$.ajax({
					type: "POST",
					url: "../functions/server/php/login.php",
					data: data,
					success: function(text){
						var response = JSON.parse(text);
						if(typeof(response) === "object"){
							
							$loader.fadeIn( 1000 ).queue(function(){ loadLobby( response ); }); // Login erfolgreich -> loadLobby()
						}
						else{
							$('#login_error').append('<p class="label">' + response + '</p>');
							$('#login').show();
						}
					}
				});
			});
			
			// Hide loadscreen
			
			$loader.delay(2000).fadeOut( 1000 );
		});
	}
	
	function loadLobby( response ){
		
		$('#templates').load("../../templates/lobbyTemplates.php", function(){

			console.log(response); // funktioniert
			$loader.fadeOut( 1000 ); // funktioniert nicht
			
		});
		
	};

Also in kurz: Ich kann mir nicht erklären warum $loader.fadeOut( 1000 ); in der loadLobby() nicht funktioniert/ausgeführt wird.

Danke euch!
Gruß
Jo

  1. Hallo j4nk3y,

    Also in kurz: Ich kann mir nicht erklären warum $loader.fadeOut( 1000 ); in der loadLobby() nicht funktioniert/ausgeführt wird.

    Was sagt denn die Fehlerkonsole?

    Bis demnächst
    Matthias

    --
    Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
    1. Hey,

      Also in kurz: Ich kann mir nicht erklären warum $loader.fadeOut( 1000 ); in der loadLobby() nicht funktioniert/ausgeführt wird.

      Was sagt denn die Fehlerkonsole?

      Nichts.

      Ich hab mal schnell ein Fiddle gebastelt.

      Gruß
      Jo

  2. Tach!

    Aber der Code spricht mehr als 1000 Worte:

    Es fehlen aber die Worte, die nach der Debug-Session entstanden sind. Also die konkreten Erkenntnisse, was nun genau vorhanden ist und was nicht. "funktioniert" und "funktioniert nicht" ist nicht sinnvoll, um daraus Schritte abzuleiten. Also was sagt der Debugger? Ist in $loader das drin, was du vermutest? Und wenn nein, an welcher Stelle läuft Wunsch und Wirklichkeit auseinander?

    dedlfix.

    1. hey,

      Ist in $loader das drin, was du vermutest?

      Ja ist es.

      Und wenn nein, an welcher Stelle läuft Wunsch und Wirklichkeit auseinander?

      An keiner die ich finde, darum frage ich euch.

      Gruß
      Jo

  3. Hallo Jo,

    du darfst ein wrapped set nicht für längere Zeit speichern. In deinem Fall müsste es schon genügen, wenn Du in loadLobby statt $loader das Set neu holst mit $("loader_wrapper");

    Dazu kommt noch, dass Du bei Einsatz der .queue() Funktion die Animation unterbrichst. Laut Doku bekommt die Funktion, die du in die Queue stellst, einen next-Parameter mit. Das ist eine Funktionsreferenz, die Du zum Abschluss Deiner Queue-Funktion aufrufen musst, damit die Animationsqueue weiter abgearbeitet wird. Ob das für Dich tatsächlich ein Thema ist, weiß ich nicht, weil dein loadLobby() ja einen neuen Ajax-Call macht, der ohnehin eine neue Asynchronität ins Spiel bringt. Aber möglicherweise ist durch den fehlenden Aufruf von next() die Queue-Verarbeitung gestört.

    Also - schreib in deinem success-Handler vom login mal

    $('#loader_wrapper')
    	.fadeIn( 1000 )
    	.queue(function(next){
    	    loadLobby( response );
    	    next();
    	});
    

    und schmeiß die $loader-Variable weg. Schreibe überall, wo $loader steht, $('#loader_wrapper'). Es vergeht zu viel Zeit, als dass es gut sein könnte, DOM Referenzen in Variablen zu halten.

    Über die User-Experience deines fadeIn und fadeOut will ich lieber nicht reden. Du hast Delays und Fades, die den User warten lassen während dein Server längst fertig ist. Zumindest den .delay(2000) würde ich rauswerfen.

    Gruß
    Rolf

    1. Moin Rolf,

      du darfst ein wrapped set nicht für längere Zeit speichern. In deinem Fall müsste es schon genügen, wenn Du in loadLobby statt $loader das Set neu holst mit $("loader_wrapper");

      Hatte ich schon vorher ausprobiert, ändert nichts am Verhalten. Und naja, ich hab gelernt, dass wenn man mehrmals auf ein Element zugreift, dann sollte es am besten in einer variablen gespeichert werden, bevor man x-mal das DOM nach diesem Element durchsucht. Eure Meinungen?

      Also - schreib in deinem success-Handler vom login mal

      $('#loader_wrapper')
      	.fadeIn( 1000 )
      	.queue(function(next){
      	    loadLobby( response );
      	    next();
      	});
      

      Bringt leider auch nichts.

      Über die User-Experience deines fadeIn und fadeOut will ich lieber nicht reden. Du hast Delays und Fades, die den User warten lassen während dein Server längst fertig ist.

      Ja, der Server ist fertig aber WebGL beim Client noch nicht.

      Zumindest den .delay(2000) würde ich rauswerfen.

      Das kommt wahrscheinlich noch, mal sehn.

      Dazu kommt noch, dass Du bei Einsatz der .queue() Funktion die Animation unterbrichst. Aber möglicherweise ist durch den fehlenden Aufruf von next() die Queue-Verarbeitung gestört.

      Danke, das hat mich auf die richtige Fährte gebracht, $( this ).dequeue(); machts.

      Danke schön!

      Gruß
      Jo

      1. Tach!

        Und naja, ich hab gelernt, dass wenn man mehrmals auf ein Element zugreift, dann sollte es am besten in einer variablen gespeichert werden, bevor man x-mal das DOM nach diesem Element durchsucht. Eure Meinungen?

        Ja. Referenzen vergammeln nicht wie Lebensmittel. Sie bleiben stabil, bis sich die Umstände ändern. Und die ändern sich auch nicht aus heiterem Himmel. Wenn sich bei dir also nichts ändert, dann spricht nichts dagegen, nicht jedes Mal das DOM abzusuchen.

        dedlfix.

        1. Genau das ist aber der Punkt: Man muss bei jedem gespeicherten Wrapped Set aufpassen, dass die Umstände stabil bleiben. Es während eines Funktionsdurchlaufes zu speichern, das ist ok (es sei denn, man verändert mutwillig den gewrappten Teil des DOM). Wird es aber, so wie von j4nkey, in eine Closure gepackt, die den aktuellen JavaScript Aufrufzyklus überlebt, dann halte ich für ein Antipattern. Es kann bei guter Planung funktionieren, ist aber zerbrechlicher Code.

          Und j4nk3y, wenn man schon optimiert: $(this).dequeue() ist die Lösung vor jQuery 1.4. Seitdem gibt es den next() Callback an die gequeuete Funktion.

          Gruß
          Rolf

          1. Tach!

            Genau das ist aber der Punkt: Man muss bei jedem gespeicherten Wrapped Set aufpassen, dass die Umstände stabil bleiben. Es während eines Funktionsdurchlaufes zu speichern, das ist ok (es sei denn, man verändert mutwillig den gewrappten Teil des DOM). Wird es aber, so wie von j4nkey, in eine Closure gepackt, die den aktuellen JavaScript Aufrufzyklus überlebt, dann halte ich für ein Antipattern. Es kann bei guter Planung funktionieren, ist aber zerbrechlicher Code.

            Javascript-Aufrufzyklus? Klär mich bitte auf!

            Das Wrapped Set zeigt in dem Fall auf genau ein Element im DOM. Das ist doch die ganze Zeit über immer dasselbe Element. Das DOM wird doch nicht ständig neu aufgebaut, inklusive dass alle Referenzen neu vergeben würden. Also, wo siehst du da ein Problem, das ich nicht sehe?

            dedlfix.

            1. Wenn sich im Browser etwas tut, das mit JavaScript behandelt werden soll (sprich: irgendein Event oder ein Timer, der zuschlägt), werden die dafür registrierten JS Handler aufgerufen. Das nennt sich auch ein Makrotask. Wenn dieser eine Task beendet ist, folgen ggf. noch die daraus generierten Mikrotasks, dann kommen eventuell Redraw- und Layout und danach der nächste Makrotask.

              Mit einem Aufrufzyklus meinte ich einen Makrotask. DOM-spezifische Daten von einem Makrotask in den nächsten mitzunehmen setzt voraus, dass man GANZ genau weiß was man tut, denn zwischen zwei Makrotasks kann alles mögliche passieren. In einer fetten SPA kann auch innerhalb eines Makrotasks einiges los sein, das man innerhalb einer kleinen, unschuldigen JS Funktion nicht überschaut.

              Definition Makro/Mikrotasks z.B. hier.

              Gruß
              Rolf

              1. Tach!

                Wenn sich im Browser etwas tut, das mit JavaScript behandelt werden soll (sprich: irgendein Event oder ein Timer, der zuschlägt), werden die dafür registrierten JS Handler aufgerufen. Das nennt sich auch ein Makrotask. Wenn dieser eine Task beendet ist, folgen ggf. noch die daraus generierten Mikrotasks, dann kommen eventuell Redraw- und Layout und danach der nächste Makrotask.

                Ach die Event Loop.

                Mit einem Aufrufzyklus meinte ich einen Makrotask. DOM-spezifische Daten von einem Makrotask in den nächsten mitzunehmen setzt voraus, dass man GANZ genau weiß was man tut, denn zwischen zwei Makrotasks kann alles mögliche passieren.

                Alles setzt voraus, dass man ganz genau weiß, was man tut. In dem Fall scheint es mir, dass du im Verdacht hast, dass sich an entscheidender Stelle das DOM ändert, aber dem nicht so ist. Auch Event Loop und Micro- und Macrotasks bauen das DOM nicht einfach mal so komplett neu auf, so dass Referenzen verlorengehen, von Dingen, die man nicht angefasst hat. Im vorliegenden Fall wäre das Problem nur, wenn über die Animation oder andere Wege der Knoten entfernt und neu erstellt worden wäre. Das wäre zu untersuchen und nicht einfach nur eine vage anzunehmen, dass sowas passieren könnte.

                In einer fetten SPA kann auch innerhalb eines Makrotasks einiges los sein, das man innerhalb einer kleinen, unschuldigen JS Funktion nicht überschaut.

                Wenn in einer fetten SPA sich ständig das DOM ändert, vor allem in Teilen, die gar nicht geändert werden, dann macht wohl eher die SPA oder dessen Autor(en) was falsch. Oder irgendwer hat den Überblick verloren.

                dedlfix.

          2. Hey,

            Und j4nk3y, wenn man schon optimiert: $(this).dequeue() ist die Lösung vor jQuery 1.4. Seitdem gibt es den next() Callback an die gequeuete Funktion.

            Funktioniert jedoch nicht mit jQuery 3.1.1 .

            Gruß
            Jo

        2. Hey,

          Ja. Referenzen vergammeln nicht wie Lebensmittel. Sie bleiben stabil, bis sich die Umstände ändern. Und die ändern sich auch nicht aus heiterem Himmel. Wenn sich bei dir also nichts ändert, dann spricht nichts dagegen, nicht jedes Mal das DOM abzusuchen.

          Genau, danke.

          Gruß
          Jo