Felix Riesterer: jQuery - mousemove feuert nach DOM-Manipulation nicht mehr

Liebe jQuery-Kundige,

um meine Aufklapp-Navi im Browser cache-bar zu machen, habe ich diese in ein JSON-Objekt geschmissen, das von einem JavaScript per JSON-Ajax nachgeladen wird. Ja, die Seite ist auch ohne JavaScript oder Maus voll benutzbar - man braucht eben mehr Klicks/Taps und Seitenaufrufe bzw. benutzt die Suchfunktion.

[Live-Beispiel]

Bei verschachtelten Menüs klappt das Aufklappen desselben Unterpunktes nicht mehrfach nacheinander, sondern nur dann, wenn inzwischen ein anderer Unterpunkt aufgeklappt wurde.

Debug-Ausgaben haben ergeben, dass wohl das "mousemove"-Event nach dem Entfernen des notwendigen <ul> für das Untermenü den Link als solchen nicht mehr "findet". Warum das so ist, verstehe ich nicht.

Die Navigation sieht im Wesentlichen (mit JavaScript-Manipulation) so aus:

<nav>  
    <div id="hover-navigation"></div>  
    <article>  
        <h2>Navigation</h2>  
        <ul><li><a href="...">...</li>...</ul>  
    </article>  
    <!-- kann fehlen: -->  
    <article>  
        <h2>Unterseiten</h2>  
        <ul><li><a href="...">...</li>...</ul>  
    </article>  
</nav>

Für die in der fest auf der Seite vorhandenen Navi-Links verwende ich diese Prüfung:
el.is("body > nav > article a[href]")

Für Links innerhalb des Hover-Menüs verwende ich diesen:
el.is("#hover-navigation a[href]")

Wenn nun also ein Aufklapp-Menü innerhalb des Hover-Menüs erscheint, dann ändert sich das DOM beispielsweise so:

<div id="hover-navigation">  
    <ul>  
        <li>  
            <a href="was/austausch">Austausch</a>  
            <!-- Untermenü -->  
            <ul><li><a href="...">...</a></li>...</ul>  
        </li>  
    </ul>  
</div>

Nach Entfernen des verschachtelten <ul> bewirkt ein Hovern über dem "Austausch"-Link kein Feuern des mousemove-Events. Der Selektor "#hover-navigation a[href]" müsste aber doch nach wie vor greifen, da sich sonst am DOM nichts verändert hat, oder verstehe ich das falsch?

Weiß jemand Rat?

Liebe Grüße,

Felix Riesterer.

--
"Wäre die EU ein Staat, der die Aufnahme in die EU beantragen würde, müsste der Antrag zurückgewiesen werden - aus Mangel an demokratischer Substanz." (Martin Schulz, Präsident des EU-Parlamentes)
  1. Hi Felix,

    wenn ich dich richtig verstehe, willst du nach dem $( document ).ready() nachträglich HTML elemente binden ?

    Da gibts für unterschiedlich JQuery Versionen unterschiedlich Möglichkeiten - müsste ich selber wieder kucken - wurde hier schon oft erklärt (zB mit 'on' letzte Version).
    Oder du nutzt natives JS ;-)

    Viele Grüße aus LA

    --
    ralphi
    1. Lieber ralphi,

      wenn ich dich richtig verstehe, willst du nach dem $( document ).ready() nachträglich HTML elemente binden ?

      "binden"? Im Sinne von "an Events binden"? Ja, das gesamte document z.B.!

      (zB mit 'on' letzte Version).

      Öhm, wie meinst Du das? Ich verwende $(document).on("mousemove", function (e) { ...});

      Oder du nutzt natives JS ;-)

      Na klar! jQuery ist natives JS. Steht doch alles im Quelltext der verlinkten Seite!

      Liebe Grüße,

      Felix Riesterer.

      --
      "Wäre die EU ein Staat, der die Aufnahme in die EU beantragen würde, müsste der Antrag zurückgewiesen werden - aus Mangel an demokratischer Substanz." (Martin Schulz, Präsident des EU-Parlamentes)
      1. Hi Felix,

        "binden"? Im Sinne von "an Events binden"? Ja, das gesamte document z.B.!

        Ich kenn mich zu wenig aus :-| Bei mir futzt, wenn ich nur nachgeladene Elemente mit $('#element').on("mousemove", function () anspreche.

        Oder du nutzt natives JS ;-)

        Na klar! jQuery ist natives JS. Steht doch alles im Quelltext der verlinkten Seite!

        Mit nativ meine ich document.getElementById("element"), also ohne schon beim laden der Seite zu binden. Also nix $( document ).ready() notwendig ;-)

        Viele Grüße aus LA

        --
        ralphi
        1. Lieber ralphi,

          Ich kenn mich zu wenig aus :-|

          trotzdem danke für Dein Bemühen.

          Bei mir futzt, wenn ich nur nachgeladene Elemente mit $('#element').on("mousemove", function () anspreche.

          Dass der Eventhandler grundsätzlich greift, ist ja bewiesen. Da liegt nicht das Problem.

          Mit nativ meine ich document.getElementById("element"), also ohne schon beim laden der Seite zu binden. Also nix $( document ).ready() notwendig ;-)

          In meiner Überschrift steht doch "jQuery"... Du meintest wohl eher "low-level", obwohl das gerade in diesem Fall so Überhaupt keine Rolle spielt (Event wird eben mit einem jQuery-Objekt eingebunden!)...

          Aber trotzdem vielen Dank für Dein Bemühen! Muss wohl heute abend mehr testen.

          Liebe Grüße,

          Felix Riesterer.

          --
          "Wäre die EU ein Staat, der die Aufnahme in die EU beantragen würde, müsste der Antrag zurückgewiesen werden - aus Mangel an demokratischer Substanz." (Martin Schulz, Präsident des EU-Parlamentes)
  2. Hallo Ingrid,

    [Live-Beispiel]

    damit ein entferntes Untermenü nicht dazu führt, dass das <li>-Element, in dem es enthalten war, vom "mouseover"-Event ignoriert wird, muss eine zusätzliche DOM-Manipulation her (schließlich kommt es wieder, wenn zwischendurch ein anderes Untermenü zu sehen war).

    Der eklige Hack besteht nun darin, das innerHTML des "leeren" <li>-Elements, in welchem nun nur noch das <a>-Element ohne <ul> liegt, mit seinem aktuellen Inhalt zu "überschreiben":

    ul.fadeOut(400, function () {  
    	var p = $(this).parent(),  
    		html = "";  
      
    	this.remove();  
      
    	html = p.html();  
      
    	if (html) {  
    		p.html(html);  
    	}  
    });
    

    Ist das nun ein Bug in jQuery, den es irgendwo zu submitten gäbe? Das Verhalten war im Chromium-Browser identisch nachvollziehbar...

    Liebe Grüße,

    Felix Riesterer.

    --
    "Wäre die EU ein Staat, der die Aufnahme in die EU beantragen würde, müsste der Antrag zurückgewiesen werden - aus Mangel an demokratischer Substanz." (Martin Schulz, Präsident des EU-Parlamentes)
    1. Liebe Ingrid,

      sicherlich muss sogar Gunnar daran scheitern, dass dieses Verhalten der Aufklapp-Navi rein mit CSS-Mitteln (insbesondere "transitions") erreicht werden könnte. Eine wesentliche Lücke im transitions-Konzept von CSS ist, dass man den Wert mancher Eigenschaften nicht zeitverzögert ändern ("not animatable") kann:

      nav li ul {  
          display: none;  
      }  
        
      nav li:hover > ul {  
          display: block;  
          transition: 1s display; /* unmöglich, weil nicht "animatable" */  
      }
      

      Liebe Grüße,

      Felix Riesterer.

      --
      "Wäre die EU ein Staat, der die Aufnahme in die EU beantragen würde, müsste der Antrag zurückgewiesen werden - aus Mangel an demokratischer Substanz." (Martin Schulz, Präsident des EU-Parlamentes)
      1. Om nah hoo pez nyeetz, Felix Riesterer!

        nav li ul {

        display: none;
        }

        nav li:hover > ul {
            display: block;
            transition: 1s display; /* unmöglich, weil nicht "animatable" */
        }

          
        ~~~css
        nav li ul {  
            display: none;  
            opacity: 0;  
        }  
        nav li:hover > ul {  
            display: block;  
            opacity: 1;  
            transition: opacity ease 1s; /* möglich, weil animatable */  
        }
        

        Matthias

        --
        Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Plane und Planeten.

        1. Lieber Matthias Apsel,

          hast Du das auch getestet? Die Animation für das Ein- oder Ausblenden (mit opacity) ist nicht mein Problem. Mein Problem ist, dass die display-Eigenschaft nicht verzögert geändert werden kann. Wenn nämlich ein Zitterfinger von einem <li> abgleitet, verschwindet sofort (eben ohne Verzögerung) die verschachtelte Liste, ohne dem Zitterfinger die Gelegenheit zu geben, doch noch rechtzeitig wieder darauf zu zeigen. Und genau das eben geht mit CSS-transitions eben nicht!

          Du darfst das gerne ausprobieren. Dein Code kann das zeitverzögerte Ausblenden nicht leisten. Und wird es ohne JavaScript auch nicht, es sei denn, das Konzept hinter den CSS-Transitions wird dahingehend erweitert, dass die "not animatable" Eigenschaften zumindest nach einer Zeitverzögerung mit der Standardfunktion für transition-timing-function (linear) einfach von a nach b umgeschaltet werden.

          Was ich noch nicht probiert habe (mit animiertem CSS habe ich kaum Erfahrung) sind die Animationen. Kann man da einen keyframe so definieren, dass er einen bestimmten display-Wert festlegt?

          Liebe Grüße,

          Felix Riesterer.

          --
          "Wäre die EU ein Staat, der die Aufnahme in die EU beantragen würde, müsste der Antrag zurückgewiesen werden - aus Mangel an demokratischer Substanz." (Martin Schulz, Präsident des EU-Parlamentes)
          1. Om nah hoo pez nyeetz, Felix Riesterer!

            hast Du das auch getestet?

            Natürlich nicht ;-)

            Die Animation für das Ein- oder Ausblenden (mit opacity) ist nicht mein Problem. Mein Problem ist, dass die display-Eigenschaft nicht verzögert geändert werden kann. Wenn nämlich ein Zitterfinger von einem <li> abgleitet, verschwindet sofort (eben ohne Verzögerung) die verschachtelte Liste, ohne dem Zitterfinger die Gelegenheit zu geben, doch noch rechtzeitig wieder darauf zu zeigen.

            Noch ein Grund mehr, (Navigations-)Inhalte eben nicht mit display: none zu verstecken (und auch nicht mit left -9999px) sondern andere Varianten zu verwenden. Und jetzt verstehe ich auch dein Problem ;-)

            Ebenfalls ungetestet schlage ich vor:

            nav li ul {  
              opacity: 0;  
              z-index: -10;  
              transition: opacity ease 1s;  
            }  
            /* Die Untermenüs liegen transparent im Hintergrund */  
            /* Texte können markiert, Links geklickt werden */  
            nav:hover li ul {  
              z-index: auto;  
            }  
            /* Mauszeiger über der Navigation -> Untermenüs transparent aber im Vordergrund */  
            nav li:hover > ul {  
              opacity: 1;  
            }  
            /* Deckkraft wird für das konkrete Untermenü animiert */
            

            Matthias

            --
            Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Kolk und Kolkrabe.

            1. Lieber Matthias Apsel,

              hast Du das auch getestet?

              Natürlich nicht ;-)

              *g*

              Noch ein Grund mehr, (Navigations-)Inhalte eben nicht mit display: none zu verstecken (und auch nicht mit left -9999px) sondern andere Varianten zu verwenden.

              Ich habe mit einigen Varianten herumexperimentiert. Z-Index ist nicht die Lösung, jedenfalls nicht alleine. Es gelingt mir nicht, die gerade unsichtbaren Listen nicht-hoverbar zu bekommen, denn das Hovern über ihnen macht sie ungewollt sichtbar.

              Vielleicht muss ich zusätzliche Animationen zu height und width machen, die nach dem opacity-Ändern greifen...

              Und jetzt verstehe ich auch dein Problem ;-)

              Hmm. Mal sehen:

              nav li ul {

              opacity: 0;
                z-index: -10;
                transition: opacity ease 1s;
              }

                
              Das Ein- und Ausblenden erfolgt im Zeitraum einer Sekunde, beginnend sofort. Um Zitterfinger zu unterstützen, möchte ich aber, dass das Einblenden (relativ) sofort erfolgt, das Ausblenden aber erst nach drei Sekunden oder später! Das ist mit dem Transitions-Konzept machbar, wie ich herausgefunden habe.  
                
              Wollte ich, dass das Ausblenden für Zitterfinger erst nach drei Sekunden erfolgte, müsste ich das Ein- und Ausblenden generell auf drei Sekunden einstellen. Dafür aber kann ich der :hover-Klasse ein zusätzliches transition-delay von 0s geben, was das Einblenden unmittelbar und das Ausblenden zeitverzögert macht. Fast fertig!  
                
              Jetzt entsteht ein unschöner Nebeneffekt: Hovere ich über einen neuen Link, zu dem es ein anderes oder kein "Untermenü" gibt, so verschwindet das nun "nicht mehr gültige" vorherige Menü erst nach seiner verlängerten "Zitterfingerunterstützung". Das will ich natürlich nicht! Aber woher sollte CSS auch wissen, dass ich nur beim endgültigen Verlassen von #hover-navigation die Verlängerung beim Ausblenden haben will...?  
                
              Abhilfe schafft der Selektor `#navigation-hover:hover li ul`{:.language-css}, der alle Unterlisten selektiert, auch die gerade gehoverte. Hier kann ich nun eine sofort wirksame Animation definieren, damit die nicht mehr gültigen Unterlisten sofort verschwinden. Für die jetzt einzublendende Unterliste gilt der Selektor mit der höheren Spezifität: `#navigation-hover li:hover ul`{:.language-css}. Damit klappt nun wirklich fast alles:  
                
              ~~~css
              /* <div id=hover-navigation"> enthält die Hover-Listen */  
              #hover-navigation {  
              	margin: 0;  
              	padding: 0;  
              	position: absolute;  
              	width: 190px;  
              	z-index: 1;  
              }  
                
              #hover-navigation ul {  
              	background: white;  
              	position: absolute;  
              	width: 100%;  
              }  
                
              /* unsichtbare Unterlisten */  
              #hover-navigation li ul {  
              	margin: calc(-1.5em - 10px) 0 0 190px;  
              	opacity: 0;  
              	transition:  
              		/* zeitverzögert ausblenden */  
              		opacity 0.5s ease 3s;  
              	z-index: 0;  
              }  
                
              /* Unterliste wenn woanders gehovert wird */  
              #hover-navigation:hover li ul {  
              	transition:  
              		/* sofort ausblenden */  
              		opacity 1s ease 0s;  
              }  
                
              /* Unterliste beim Hovern */  
              #hover-navigation li:hover > ul {  
              	opacity: 1;  
              	transition:  
              		/* sofort einblenden */  
              		opacity 1s ease 0s;  
              	width: 100%;  
              	z-index: 2;  
              }
              

              Einzig das Aufklappen der unsichtbaren Unterlisten stört, die unbeabsichtigt gehovert werden. Das sieht man auch an den Scrollbars, die nur dann erscheinen, wenn man gerade überhaupt die Navi hovert. Brauche ich denn noch weitere Animationen? Etwa zu width und height? Oder womit werden die unsichtbaren Unterlisten nicht mehr hoverbar? Also doch ein margin-left:-9999px?

              Liebe Grüße,

              Felix Riesterer.

              --
              "Wäre die EU ein Staat, der die Aufnahme in die EU beantragen würde, müsste der Antrag zurückgewiesen werden - aus Mangel an demokratischer Substanz." (Martin Schulz, Präsident des EU-Parlamentes)
              1. Liebe Ingrid,

                aktuell zu testen (eventuell Browser zum neuen Laden zwingen um Cache zu überschreiben):
                http://peutinger-gymnasium.de

                Liebe Grüße,

                Felix Riesterer.

                --
                "Wäre die EU ein Staat, der die Aufnahme in die EU beantragen würde, müsste der Antrag zurückgewiesen werden - aus Mangel an demokratischer Substanz." (Martin Schulz, Präsident des EU-Parlamentes)
              2. Om nah hoo pez nyeetz, Felix Riesterer!

                Abhilfe schafft der Selektor #navigation-hover:hover li ul, der alle Unterlisten selektiert, auch die gerade gehoverte. Hier kann ich nun eine sofort wirksame Animation definieren, damit die nicht mehr gültigen Unterlisten sofort verschwinden. Für die jetzt einzublendende Unterliste gilt der Selektor mit der höheren Spezifität: #navigation-hover li:hover ul.

                <klugscheiß>
                  Die beiden Selektoren enthalten je 1 ID, 2 Elemente, 1 Pseudoklasse …
                </klugscheiß>

                Einzig das Aufklappen der unsichtbaren Unterlisten stört, die unbeabsichtigt gehovert werden. Das sieht man auch an den Scrollbars, die nur dann erscheinen, wenn man gerade überhaupt die Navi hovert. Brauche ich denn noch weitere Animationen? Etwa zu width und height? Oder womit werden die unsichtbaren Unterlisten nicht mehr hoverbar? Also doch ein margin-left:-9999px?

                Kann man sich das irgendwo anschauen?

                Matthias

                --
                Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Halle und Halleffekt.

                1. Lieber Matthias Apsel,

                  Für die jetzt einzublendende Unterliste gilt der Selektor mit der höheren Spezifität: #navigation-hover li:hover ul.

                  <klugscheiß>
                    Die beiden Selektoren enthalten je 1 ID, 2 Elemente, 1 Pseudoklasse …
                  </klugscheiß>

                  stimmt. Deshalb steht er auch zuerst notiert, damit der Selektor gleicher Spezifität (ja, mein Denkfehler) überschrieben wird.

                  Kann man sich das irgendwo anschauen?

                  Nö, natürlich nicht. Doch, warte, da war ja noch ein follow-up Post von mir...

                  Liebe Grüße,

                  Felix Riesterer.

                  --
                  "Wäre die EU ein Staat, der die Aufnahme in die EU beantragen würde, müsste der Antrag zurückgewiesen werden - aus Mangel an demokratischer Substanz." (Martin Schulz, Präsident des EU-Parlamentes)
              3. Om nah hoo pez nyeetz, Felix Riesterer!

                Einzig das Aufklappen der unsichtbaren Unterlisten stört, die unbeabsichtigt gehovert werden. Das sieht man auch an den Scrollbars, die nur dann erscheinen, wenn man gerade überhaupt die Navi hovert. Brauche ich denn noch weitere Animationen? Etwa zu width und height? Oder womit werden die unsichtbaren Unterlisten nicht mehr hoverbar? Also doch ein margin-left:-9999px?

                Transitionen von 0 auf auto sind allein mit CSS nicht möglich. (Archiv 2013) Wenn dich die unterschiedlich langen Scrollbalken stören, bzw. auf Seiten mit wenig Inhalt erscheinende Scrollbalken wirklich stören, die es imho mit margin-left: -9999px auch geben würde, weil die Box ja trotzdem da ist, könntest du etwa line-height animieren.

                Matthias

                --
                Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Wind und Windows.

                1. Lieber Matthias Apsel,

                  Wenn dich die unterschiedlich langen Scrollbalken stören, bzw. auf Seiten mit wenig Inhalt erscheinende Scrollbalken wirklich stören, die es imho mit margin-left: -9999px auch geben würde, weil die Box ja trotzdem da ist, könntest du etwa line-height animieren.

                  so, spätestens hier verlässt mich mein akademisches Interesse. Vielen Dank für Deine Mühen, aber jetzt sehe ich keinen Sinn mehr, in einer rein CSS-animierten Hover-Navi, da diese Ergänzung zur Navigation ohnehin nur mit aktiviertem JavaScript zur Verfügung steht und folgerichtig auch JavaScript die Animation übernehmen kann.

                  Sehr wahrscheinlich benutzt jQuery CSS-Animationen intern, wenn der Browser welche anbietet, aber dass ich mich hier mit CSS verrenken sollte, obwohl ich ja ohnehin schon JavaScript nutze, sehe ich nicht mehr ein. Das Experiment hat mich eingehender mit CSS-Animationen vertraut gemacht, insbesondere mit dem Konzept der "transitions", weshalb ich es nicht als verlorene Zeit ansehe, sondern ganz im Gegenteil als Lehrstück. Für das Web-Projekt kehre ich aber nun wieder zur JavaScript-basierten Lösung zurück.

                  Dir noch einmal vielen Dank für Deine Anregungen!

                  Liebe Grüße,

                  Felix Riesterer.

                  --
                  "Wäre die EU ein Staat, der die Aufnahme in die EU beantragen würde, müsste der Antrag zurückgewiesen werden - aus Mangel an demokratischer Substanz." (Martin Schulz, Präsident des EU-Parlamentes)
          2. @@Felix Riesterer:

            nuqneH

            Ich mach jetzt mal den at (Kellerkind, wo steckt der eigentlich?) …

            sicherlich muss sogar Gunnar daran scheitern

            … gerade erst auf diesen Thread gestoßen.

            Mein Problem ist, dass die display-Eigenschaft nicht verzögert geändert werden kann.

            display kann mit transition gar nicht geändert werden. Stufenlos (d.h. feinstufig ;-)) ändern kann man nur numerische Werte, nicht Keywords. Was sollte denn auch zwischen 'none' und 'block' liegen?

            Verzögert? Aber ja! transition-delay existiert; auch in der Shorthand-Eigenschaft.

            Qapla'

            --
            „Talente finden Lösungen, Genies entdecken Probleme.“ (Hans Krailsheimer)