Pit: data-attribute nach Browserupdate nicht mehr definiert

Hallo,

ich habe eine Tabelle, in der ich Zellen jeweils den code

<button id='...' data-xyz='' data-zyx=''> mitgebe, um später in JS/Jquery hierauf zugreifen zu können.

var xyz = $(this).attr('data-xyz');

Das funktionierte lange Zeit sehr gut, aber auf einigen Browsern funktioniert das inzwischen nicht mehr, da xyz bei einem console.log als undefined ausgegeben wird.

Hat sich da browserseitig irgendwas verändert, bzw. wie fixe ich das im Code?

Ich vermute, dass das this nicht mehr den Button als Objekt referenziert sondern etwas anderes…

Pit

  1. Wolltest Du eigentlich vielleicht etwas wie

    <button id='...' data-v1='Eins' data-v2='Zwei' onclick="alert( this.dataset.v2 )">Hallo</button>
    

    notieren? Dann sähe das Ergebnis so aus:

    1. Wolltest Du eigentlich vielleicht etwas wie

      Nein...eigentlich wollte ich das schon so, wie ich geschrieben habe. Gebe ich mir this z.b. im FF65 aus, erhalte ich ein "object HTMLButtonElement" (genau das wollte ich auch), gebe ich es im FF66 aus, erhalte ich ein "object HTMLImageElement" (an dieses sind aber keine data-attribute angehängt).

      Pit

      1. Tach!

        Gebe ich mir this z.b. im FF65 aus, erhalte ich ein "object HTMLButtonElement" (genau das wollte ich auch), gebe ich es im FF66 aus, erhalte ich ein "object HTMLImageElement" (an dieses sind aber keine data-attribute angehängt).

        Daraus kann man schließen, dass in Inneren des Buttons ein Image ist, und vermuten, dass der Eventhandler für dieses Image angeschlagen hat statt für den Button.

        dedlfix.

        1. Hallo dedlfix,

          genau so ist es. Ich hatte gerade einen entsprechenden Fiddle gebastelt, aber den brauchts ja nun nicht mehr. Es iost genau, wie Du sagst... was mich wundert ist aber, dass das FF von einer Version auf die andere anders interpretiert.

          Pit

          Daraus kann man schließen, dass in Inneren des Buttons ein Image ist, und vermuten, dass der Eventhandler für dieses Image angeschlagen hat statt für den Button.

          dedlfix.

        2. Hallo dedlfix,

          dann hätte der Firefox im jahrealten Streit nachgegeben, um ein einheitliches Verhalten zu bekommen? Mozilla sagte immer, das wäre kein Bug, sondern korrekt. Chrome sagte das Gegenteil.

          Gerade auf den 66er upgedated und schnell gefiddelt - nö. Nicht gefixt. Bzw. anders gefixt. In jQuery zeigt this immer auf den Button. Aber e.target zeigt im FF66 auf das img, im FF60 dagegen nicht. Ausprobiert mit jQuery 1.9.1, 2.2.4, 3.1.1 und 3.3.1 (geht in JSFiddle ja schnell).

          <button><img src="foo" alt="Für Gunnar">Clickme</button>

          Rolf

          --
          sumpsi - posui - clusi
      2. <html>
        <button id='btn1' data-v1='Eins' data-v2='Zwei'><img id='img1' src='hallo.gif' style='width:3em;height:3rem' /></button>
        
        <button id='btn2' data-v1='Eins' data-v2='Zwei'><img id='img2' src='hallo.gif' style='width:3em;height:3rem'></button>
        
        <script>
        document.getElementById('btn1').addEventListener( 'click', function(){ console.log( this.dataset.v1 ) }, false );	
        document.getElementById('btn2').addEventListener( 'click', function(){ console.log( this.dataset.v2 ) }, false );	
        </script>
        </html>
        

        Geht im Firefox 65 und in Chromium 73.

        1. Hallo ursus,

          wie gesagt, im FF66 ist ein breaking change bezüglich e.target. Aber bezüglich jQuery this konnte ich auch keinen feststellen. Das kann auch nicht sein, wenn man sich den jQuery Source anschaut, da wird per apply dafür gesorgt das this immer das Element ist auf dem registriert wurde.

          Pit??? War das wirklich das von jQuery gesetzt this, von dem wir hier reden?

          Rolf

          --
          sumpsi - posui - clusi
          1. Hallo Rolf,

            Pit??? War das wirklich das von jQuery gesetzt this, von dem wir hier reden?

            $('.test')
                .on('click', 'img, a, button', function(event) {
                  event.preventDefault();
                    alert (this);
            });
            

            ergibt im FF65 [object HTMLButtonElement] und im 66 [object HTMLImageElement]

            Pit

            1. Hallo Pit,

              schönes JavaScript, aber ohne das zugehörige HTML darf man nur raten. Ich rate mal, dass dein HTML so aussieht:

              <div class="test">
                 <button>
                    <img src="foo">Bar
                 </button>
              </div>  
              

              Und dann musst du genauer hingucken. Es gibt 2 Alerts, einen für das img und einen für den button.

              Ein click event auf ein img sollst Du nur dann registrieren, wenn es ein usemap Attribut hat, nur solche Images sind interaktiv (ob sie leicht zugänglich sind, ist eine andere Frage). Aber interaktive Element ein einem Button sind vom Inhaltsmodell her verboten.

              Ein img in einem button sollte niemals eigenständig auf Klicks reagieren.

              Rolf

              --
              sumpsi - posui - clusi
              1. Hallo Rolf,

                <div class="test">
                   <button>
                      <img src="foo">Bar
                   </button>
                </div>  
                

                Ja, so in etwa sieht es aus.

                Und dann musst du genauer hingucken. Es gibt 2 Alerts, einen für das img und einen für den button.

                Ein click event auf ein img sollst Du nur dann registrieren, wenn es ein usemap Attribut hat, nur solche Images sind interaktiv (ob sie leicht zugänglich sind, ist eine andere Frage). Aber interaktive Element ein einem Button sind vom Inhaltsmodell her verboten.

                Oh, wußt ich nicht.

                Ein img in einem button sollte niemals eigenständig auf Klicks reagieren.

                Ok, dann ist der Fall klar.

                $('.test')
                    .on('click', 'a, button', function(event) {
                      event.preventDefault();
                        alert (this);
                });
                
                

                ergibt [object HTMLButtonElement] und damit läuft dann auch der Rest wieder einwandfrei.

                Vielen Dank für Deine Hilfe und guts Nächtle 😀

                Pit

  2. Tach!

    Hat sich da browserseitig irgendwas verändert, bzw. wie fixe ich das im Code?

    Nichts das ich wüsste.

    Ich vermute, dass das this nicht mehr den Button als Objekt referenziert sondern etwas anderes…

    Überprüf deine Vermutung! Du kannst das this auch über console.log() anzeigen lassen. Mehr kann man erst sagen, wenn du mehr Code zeigst. So ist das nicht nachvollziehbar, worauf das this zeigen könnte.

    dedlfix.

  3. Hallo Pit,

    zunächst mal ist es so, dass .attr("data-xyz") nicht der ideale Weg ist, um auf den Wert von data-Attributen zuzugreifen.

    Wenn Du nur mit jQuery hantierst, kannst Du die .data Methode verwenden, also z.B. let xyz = $("#...").data("xyz");. Damit kannst Du auch etwas ändern: $("#...").data("xyz", 4711); speichert 4711 für xyz, aber nicht im data-Attribut, sondern jQuery-intern. Auslesen mit .data("xyz") liefert den neuen Wert, Auslesen mit .attr("data-xyz") liefert den alten Wert. Das kann gelegentlich nützlich sein.

    Eine Alternative ist das dataset Attribut im DOM. Wenn Du das DOM Element zum Button hast, kannst Du mit btn.dataset.xyz auf den Wert von data-xyz zugreifen und den Attributinhalt durch Zuweisung auch ändern.

    Wie kommst Du an das DOM Element heran: $("#...")[0], oder document.getElementById("...") liefert es Dir. Mit dem this in einem click-Handler musst Du dagegen aufpassen. this enthält das DOM Element, auf dem der click-Handler registriert wurde. Wenn das der Button ist, passt es. Hat das Event aber gebubbelt, enthält this das DOM Element des Containerelements, auf dem der click-Handler registriert wurde.

    Es ist sicherer, statt this das Event-Objekt zu verwenden, das dem click-Handler mitgegeben wird, und darin das target-Attribut zu verwenden. Das funktioniert gut, solange im Button nur Text steht.

    FALLS der Button HTML enthält, verhalten sich Chrome und Firefox unterschiedlich. Der eine liefert dann den Button, der andere das Kind-Element. D.h. du musst dann mit .closest('button') sicherstellen, dass Du wirklich auf dem Button landest.

    Folgendes HTML-Snippet ist valide:

    <div id="outer">
       <button id="btnFoo">
          <span id="inner">Don't</span> Click Me
       </button>
    </div>
    

    Darauf registrieren wir nun Event Handler:

    $("outer").click(function(e) {
       console.log(this);
       console.log(e.target);   
    });
    $("btnFoo").click(function(e) {
       console.log(this);
       console.log(e.target);
    });
    

    1. Man klickt auf "Click me"

    Der btnFoo-Handler loggt den Button für this und für e.target. Der outer-Handler loggt das div für this und den button für e.target

    2. Man klickt auf "Don't"

    Ich habe gerade keinen Firefox parat, aber soweit ich weiß, ergibt sich kein Unterschied.

    In Chrome ist es aber so, dass ein Klick auf "Don't" den span für e.target loggt. D.h. man sollte es unterlassen, in Buttons HTML-Innereien einzubauen, oder man muss mit $(e.target).closest("button") ein jQuery-Set mit dem Button beschaffen. In allen Browsern außer Internet Explorer gibt es diese Methode auch nativ im DOM: e.target.closest("button") liefert das DOM Element zum Button.

    Rolf

    --
    sumpsi - posui - clusi
    1. Hallo Rolf,

      danke für die lange Erklärung.

      Ich bau daraus heute abend oder morgen vormittag mal ein paar Tests zusammen, jedenfalls ist meine bisherige Version offensichtlich wirklich nicht "wasserdicht". Ist mir zuletzt schonmal auf dem Handy aufgefallen, da dachte ich aber, es läge an zu trockenen Fingern oder daran, dass ich das kleine image im Button-Element nicht genau getroffen hätte.

      Hast Du eine Ahnung, warum der FF65/66 hier unterschiedliche Ergebnisse liefert?

      Pit

      1. Hallo Pit,

        ja, siehe oben in der Disku mit dedlfix.

        Hier steht es auch:

        Fixed: <button> element is no longer special cased in event dispatch, per latest specifications

        Offenbar hat endlich jemand ein Einsehen gehabt und diesen Streit per Spec geschlichtet. FF verhält sich jetzt wie Chrome.

        Ich habe das im Wiki mal schnell vermerkt, das kann sich als breaking change herausstellen, wenn jemand FF-only programmiert hat.

        Rolf

        --
        sumpsi - posui - clusi