JavaScript Inkonsistentes Verhalten der duch onResize-Funktion hinzugefügten Event-Handler

safeload

Ensign
Registriert
Juni 2010
Beiträge
244
Hallo, ich hab bei meinem CSS-DropDown Menü über einen if-Block festgelegt, daß wenn es sich um ein Touch-Device handelt oder eine bestimmte Fenstergröße (Breite 900px) unterschritten wird , dem Menü Click-Event-Handler zum togglen von CSS-Klassen hinzugefügt werden.

Die aktuelle Fenstergröße wird dabei durch die Abfrage der CSS-Eigenschaft: 'Display: flex' ermittelt, welche von den Media Queries bei einer Breite < 900px gesetzt wird.

Dies funktioniert gut bei onLoad.
Sobald dies aber über die Resize-Funktion getriggert wird, funktionieren die Click-Events (bei einer Breite < 900 px) unzuverlässig.

Dies kommt wohl dadurch, daß onResize das Dokument mit den togglenden Click-Event-Handlern befeuert wird, die in den ausgeführten Funktionen enthalten sind.


Kann mir vielleicht jemand sagen, wie man diese aus der Resize-Funktion herausnehmen kann ohne die Funktionalität zu zerstören?

jQuery
Code:
// JavaScript Document

$(document).ready(function () { // Anweisungen nach dem Ladeabschluss des DOM ausführen
	'use strict';

	//################################### MOBILE MENÜ EIN-/AUSBLENDEN ###################################

	// Funktion definieren zum Ausblenden der DropDown Menüs
	function resetDropDownMenu() {
		$('nav ul li ul').removeClass('display-flex'); // DropDown Menüs aller Ebenen ausblenden
		$('nav ul li i.exit-block').removeClass('exit-block'); // Exit-Icons aller Ebenen ausblenden
		$('nav ul li a').removeClass('touch-background-hl'); // Click Backgrounds aller Ebenen ausblenden
	}

	$('#mobile_menu_button').click(function () {
		$('#navigation').toggleClass('display-flex');
		$(resetDropDownMenu); // Alle aktivierten Elemente des DropDown Menüs zurücksetzen

	});

	//################################# MENÜ- UND QUITICON AUSTAUSCHEN ##################################

	var MENU_OPEN = false;
	$('.menu_icon').stop().click(function () {
		if (MENU_OPEN === false) {
			MENU_OPEN = true;
			$('.menu_icon').addClass('is-active'); // Animation Menü- zu Quiticon auslösen
		} else {
			MENU_OPEN = false;
			$('.menu_icon').removeClass('is-active'); // Animation Quit- zu Menüicon auslösen
		}
	});

	//############################## DROPDOWN MOBILE MENÜ EIN-/AUSBLENDEN ###############################

	function isTouchDevice() { // Abfrage ob es sich um Touch-Device handelt
		return typeof window.ontouchstart !== 'undefined';
	}

$(window).resize(function () {


		function isSmallWindowDevice() { // Abfrage ob es sich um ein Device mit Anzeige des Mobile Menüs handelt
			return $('#mobile-menu-header').css('display') === 'flex'; // Abfrage ob Media Query für mobile Geräte aktiv ist
		} /*alert(isSmallWindowDevice());*/

		if (isTouchDevice() || isSmallWindowDevice()) { // Anfang der Anweisungen für Touch- oder smallWindow-Devices

			// Ebene1 + 2
			$('nav ul li a').click(function () {
				$(this).siblings('ul').toggleClass('display-flex'); // DropDown-Menü(ul), adressiert als Geschwister-Element des geclickten Links, aus- und einklappen
				$(this).children('i.exit-none').toggleClass('exit-block'); // Exit-Icon(i), adressiert als Kind-Element des geclickten Links, ein- und ausblenden
			});
			
			// Ebene1 + 2
			if ($('nav ul').css('display') !== 'flex') { // wenn Ebene ausgeblendet ist
				$('nav ul li a').click(function () { // hinzufügen des click events wichtig für (this) Lokalisation
					$(this).siblings('ul').find('li ul').removeClass('display-flex'); // Drop-Down Ebene2(ul), adressiert als Geschwister-Element des geclickten Links, ausblenden
					$(this).siblings('ul').find('li i.exit-block').removeClass('exit-block'); // Drop-Down Ebene1 Exit-Icon(i) ein- und ausblenden, adressiert als Geschwister-Element des geclickten Ebene1 Links
					$(this).siblings('ul').find('li a').removeClass('touch-background-hl touch-background-hl1 touch-background-h2'); // Drop-Down Ebene1 Background-Color zurücksetzen, adressiert über den vom Geschwister-Element des geclickten Ebene1 Links
				});
			}

			//######################## MOBILE MENÜ LINKS BACKGROUND-COLOR TOGGLE ON CLICK #######################

			// Alle Ebenen
			$('nav ul li a').click(function () {
				$(this).toggleClass('touch-background-hl'); // onClick Background-Color für alle Links austauschen
			});

			//Alle Ebenen
			$('nav ul li').removeClass("no-touch"); // Hover Klasse für noTouch-Devices & Desktop Menü deaktivieren

			
		} // Ende der Anweisungen für Touch- oder smallWindow-Devices

		
		//######################### HOVER NUR FÜR NON-TOUCH_DEVICES AKTIVIETREN #########################

		//Alle Ebenen
		else {
			$('nav ul li').addClass("no-touch"); // Hover Klasse für noTouch-Devices & Desktop Menü aktivieren 
			$(resetDropDownMenu); // Alle durch click aktivierten Elemente des DropDown-Menüs zurücksetzen

		} // Ende der Anweisungen für noTouch-Devices und largeWindow-Devices

	
	}).resize();



});
 
Zuletzt bearbeitet:
so wie ich das sehe setzt du die click() handler in der funktion, die vom event handler resize() aufgerufen wird. wenn du das fenster resized dann setzt du die click() handler ständig erneut. dadurch werden sie nicht ersetzt sondern hinzugefügt. jede callback function jedes handlers wird dann bei click ausgeführt.
du kannst entweder:
- einen namespace für click handler verwenden und diese handler bei ausführung der cb von resize() mit .off() entfernen (siehe jquery doc)
- die click-handler unabhängig der fensterbreite setzen und in den cb functions der click() handler die fensterbreite überprüfen (so würde ich es machen)
 
90% der Kommentare kannst du dir sparen. Das macht deinen ganzen Code total aufgebläht und unübersichtlich.
Natürlich sind Kommentare wichtig aber du musst nich jede Zeile Code 5mal dokumentieren sondern da wo Erklärungsbedarf herrscht.

function isSmallWindowDevice() { // Abfrage ob es sich um ein Device mit Anzeige des Mobile Menüs handelt

bestes Beispiel hier - wozu der Kommentar? Der Funktionsname sagt doch schon alles
 
Zuletzt bearbeitet:
floq0r schrieb:
so wie ich das sehe setzt du die click() handler in der funktion, die vom event handler resize() aufgerufen wird. wenn du das fenster resized dann setzt du die click() handler ständig erneut. dadurch werden sie nicht ersetzt sondern hinzugefügt. jede callback function jedes handlers wird dann bei click ausgeführt.
du kannst entweder:
- einen namespace für click handler verwenden und diese handler bei ausführung der cb von resize() mit .off() entfernen (siehe jquery doc)
- die click-handler unabhängig der fensterbreite setzen und in den cb functions der click() handler die fensterbreite überprüfen (so würde ich es machen)

Vielen Dank, Option 2 hat super funktioniert, das war genau der Anstoss den ich gebraucht habe!
Ergänzung ()

cppnap schrieb:
90% der Kommentare kannst du dir sparen. Das macht deinen ganzen Code total aufgebläht und unübersichtlich.
Natürlich sind Kommentare wichtig aber du musst nich jede Zeile Code 5mal dokumentieren sondern da wo Erklärungsbedarf herrscht.



bestes Beispiel hier - wozu der Kommentar? Der Funktionsname sagt doch schon alles

Du hast recht, die Kommentare sind ja auschließlich für mich (das war mein 1. jQuery "Projekt") und ich habe alles ausformuliert um es mir besser einzuprägen und es später nachvollziehen zu können. Das Ihr das nicht braucht war mir klar und ich hätte es der Übersicht halber wohl besser entfernen sollen .(wozu ich einfach zu faul war ... :))
Ergänzung ()

Yuuri schrieb:
Code:
$(document).on( ... )
Ist das Stichwort.

Danke. Das ist aber ein Stichwort, das ich auf Grund meines beschränkten Syntax-Horizonts leider nicht so gut einordnen kann :)
Du meinst einfach
Code:
$(window).on( ... )
durch
Code:
$(document).on( ... )
ersetzen?
 
Zuletzt bearbeitet:
Nein. Wie floq0r bereits geschrieben hat, bindest du immer und immer wieder neue Handler dem Knoten hinzu, was vollkommen Banane ist. Das hast du im Pen (warum ist der Link jetzt raus?) wunderbar gesehen.

Klick -> Funktion -> wie gewünscht
Fenstergröße ändern
Klick -> Funktion -> Funktion -> "wie gewünscht"
Fenstergröße ändern
Klick -> Funktion -> Funktion -> Funktion -> "wie gewünscht"
...

Das Zeichen, dass Handler mehrfach gebunden worden sind. Wenn du die Developer Tools öffnest, auf den Button gehst und dir die Event Handler ansiehst, kannst du dann wunderbar beobachten, dass pro Ändern der Fenstergröße ein Event Handler dazu kommt.

Es ist doch so: Du willst, dass etwas bei irgendwas passiert. Also schreib es doch auch so. Mit
Code:
$(document).on( "click", ".button", function() {} );
sagst du bereits, dass beim "click" auf ".button" der Callback aufgerufen wird. Das bindest du einmal ans document (nicht auf irgend einen Button o.ä.). Danach wird mit jedem Klick auf ".button" der Callback ausgeführt, egal ob du diese nun alle entfernst oder neu hinzufügst.

Würdest du dies nicht ans document binden, würde beim Hinzufügen von neuen Buttons der Handler nicht aufgerufen werden, weil du den Callback direkt auf einen Button gelegt hast. Und neue Buttons haben eben noch nichts gebunden. Deshalb $(document).on(...), einmal den Callback definiert und sobald du dann auf einen Button klickst, wird der Callback aufgerufen.
 
Also ich hab´s jetzt so gelöst, daß ich onResize nur die Überprüfung der Fenstergröße durchführe und die if-Abfrage in die callback Funktion integriert habe. Danke nochmal an alle, die sich beteiligt haben.

Code:
$('nav ul li a').click(function () {
		if (isTouchDevice() || isSmallWindowDevice()) {
			$(this).siblings('ul').toggleClass('display-flex'); 
			$(this).children('i.exit-none').toggleClass('exit-block'); 
		} else {
			return false;
		}
	});
 
Zuletzt bearbeitet:
noch so zu info:

$(document).ready(... bitte durch $(function() {... ersetzen (jquery 3.x)

bzw. gleich "Anonymisieren":
(function($) { .... }(jQuery);


Wenn du dich damit beschäftigst:
Kannst du auch

$(element).on("click touchend", function() {... beschäftigen.

bzw. mit "namespace"

$(element).on("click.meinNamespace", function() {...

Einfach mal googeln, wenn fragen bestehen bleiben einfach hier posten... :)
 
Ich hab den shortcut, den ich tatsächlich zuerst eingesetzt hatte, wieder ersetzt, damit ich anhand der Syntax wieder direkt sehen konnte, was es bedeutet. Was spricht denn gegen den Einsatz von $(document).ready(... ?
Wie gesagt, dies ist mein erstes jQuery "Projekt" :) und deswegen schreibe ich alles aus und dokumentiere jede Banalität.

Danke für die Hinweise, das werd´ ich mir auf jeden Fall mal ansehen.
 
Zuletzt bearbeitet:
Zurück
Oben