Linuchs: stopPropagation nicht verstanden?

problematische Seite

Moin,

wenn ich auf ein Info-Symbol klicke, soll das Klick-Event die Info-Anzeige per Ajax vom Server holen und anzeigen, aber im DOM der Seite nicht weitergemeldet werden. Deshalb versehe ich diese (i) Symbole mit der Bitte stopPropagation.

Auf der Seite http://remso.eu/ habe ich das rot umrandete Werbe-div in den <a> tag eingeschlossen. Egal wohin man klickt, der Link führt zu den Seiten des Gasthauses.

Mit einer Ausnahme: Das Hilfe-Symbol (i) unten links innerhalb des Werbe-div soll bei Klick nur einen erklärenden Text einblenden, aber nicht zum Gasthaus verlinken.

Der Link wird trotz stopPropagation ausgeführt. Warum?

<img id="werbung" class="help" src="img/icon_info.png" alt="?" title="Info" />
window.addEventListener('DOMContentLoaded', function ( ) {
//alert( "hier ajax_getHelptext.js" );
  // Textfeld einrichten
  var erster  = document.getElementsByTagName( "body" )[0].firstChild;
  var newDiv  = document.createElement("div");
  newDiv.id   = "helptext";
  document.body.insertBefore( newDiv, erster ); 

/* *************************************************
   *
   * HELP-Icons/Buchstaben anschliessen
   *
   ************************************************* */
  obj_help = document.getElementsByClassName( "help" );
  for ( i=0; i<obj_help.length; i++ ) {
    if ( obj_help[i].id ) {
      obj_help[i].addEventListener('click', function (event) {
        getHelptextXY( bia_domain, bia_owner, bia_kw, bia_ll, this.id, bia_lg, event.clientX, event.clientY );
        event.stopPropagation();  // Klick-Event verbirgt sonst den helptext wieder
      });
    }
  }
});

Gruß, Linuchs

  1. problematische Seite

    Meinten Sie vielleicht e.preventDefault() für das abgefangene Klickevent.

    1. problematische Seite

      hallo

      Meinten Sie vielleicht e.preventDefault() für das abgefangene Klickevent.

      das müsste er aber auf dem a-Element registriern, was er wohl nicht will.

      Die Antwort heisst: Verschachtle keine interaktiven Elemente.

      1. problematische Seite

        Die Antwort heisst: Verschachtle keine interaktiven Elemente.

        Mein erster Versuch war, diese Info hinter dem <a> tag per margin-top:-2em in den Werbe-div zu schmuggeln, doch er verschwand in der dritten Dimension (z-index) unter dem div und ward nicht mehr gesehen.

        Einer Information die kein position:absolute hat, kann man ja auch keinen wirksamen z-index mitgeben.

        1. problematische Seite

          hallo

          Einer Information die kein position:absolute hat, kann man ja auch keinen wirksamen z-index mitgeben.

          doch!

          1. problematische Seite

            doch!

            danke, es funktioniert mit position:relative;margin-top:-3em;z-index:2;

            Gruß, Linuchs

  2. problematische Seite

    Hallo Linuchs

    Ich habe noch nicht so ganz durchschaut was du hier zusammenbaust, aber so richtig gut schaut es nicht aus.

    <img id="werbung" class="help" src="img/icon_info.png" alt="?" title="Info" />
    

    Meinst du nicht, dass es einen aussagekräftigeren Alternativtext gibt als bloß ein Fragezeichen? Das wäre aus Benutzersicht wichtiger als ein title–Attribut, das die Mehrzahl der Benutzer vermutlich ohnehin nie zu Gesicht bekommen wird.

      var erster  = document.getElementsByTagName( "body" )[0].firstChild;
    

    var erster? Für diese Wahl des Bezeichners gibt es wohl nicht die Goldmedaille. Mal angenommen, die Variable wäre nicht überflüssig, dann würde ich ihr einen Namen geben, unter dem man sich etwas vorstellen kann. – Vielleicht firstChild?

    // Für ältere Browser über Elementausdrücke
    
    var firstChild = document.body.firstChild;
    
    
    // In modernen Browsern auch mittels Destrukturierung
    
    const {firstChild} = document.body;
    

    Du musst übrigens nicht mit getElementsByTagName eine HTMLCollection der body–Elemente erzeugen. Ich meine, ich will nicht hoffen, dass in deinem Dokument mehr als ein body–Element vorkommt. Das eine kannst du dann direkt über document.body referenzieren.

      document.body.insertBefore( newDiv, erster );
    

    Na sowas, hier ist dir das mit document.body ja wieder eingefallen. 😉 Da du das erste Kindelement von body nur an dieser Stelle brauchst, hättest du es auch direkt in den Ausdruck einsetzen können. Wäre so vielleicht ein wenig besser lesbar.

    // Fügt das div vor dem ersten Kindelement von body ein
    
    document.body.prepend(helptext);
    

    Wenn du Internet Explorer und andere antiquierte Browser nicht mehr unterstützen willst, könntest du statt insertBefore an dieser Stelle übrigens auch die Methode prepend verwenden, um den Hilfetext als erstes Kind des body–Elements hinzuzufügen. Eine Referenz auf den ersten Kindknoten ist bei Verwendung dieser Methode nicht nötig.

    Ein Polyfill für ältere Browser ist übrigens schnell gebaut:

    if (!Element.prototype.prepend) {
    
        Element.prototype.prepend = function prepend() {
            var fragment = document.createDocumentFragment();
    
            Array.prototype.forEach.call(arguments, function(value) {
                fragment.appendChild(
                    value instanceof Node
                        ? value
                        : document.createTextNode(value)
                );
            });
    
            this.insertBefore(fragment, this.firstChild);
        };
    
    }
    

    Ist aber ungetestet.

      obj_help = document.getElementsByClassName( "help" );
    

    Hier legst du eine globale Variable an. Sofern du nicht in der äußeren lexikalischen Umgebung deiner Funktion eine lokale Variable gleichen Namens deklariert hast, an die du zuweist. Globale Variablen möchtest du sicherlich vermeiden, also verwende wenn nötig var, oder besser gleich const. – Eine erneute Zuweisung wird hier offenbar nicht vorgenommen.

    Buchstaben sind übrigens keine Mangelware. Du möchtest wohl wenigstens noch ein 'e', ein 'c' und ein 't' für den Bezeichner kaufen. Immerhin postest du den Code hier, möchtest also, dass auch andere ihn gut lesen können.

    Es ist oft auch eine gute Idee, Namen von Variablen, die für Sammlungen – also eine Vielzahl von Objekten stehen, im Plural zu notieren. Zum Beispiel hättest du im vorliegenden Fall helpObjects oder helpers schreiben können.

    In JavaScript ist es übrigens Konvention, bei Bezeichnern camelCase und nicht snake_case zu verwenden, so wie du es weiter oben auch getan hast. Unabhängig davon welche Variante du wählst, solltest du aber mindestens innerhalb eines Skripts bei dieser Variante bleiben. Beide Stile zu mischen fördert die Lesbarkeit nicht.

    <img id="werbung" class="help" src="img/icon_info.png" alt="?" title="Info" />
    

    Ich füge an dieser Stelle nochmal das Markup vom Anfang ein, für den Fall, dass irgendjemand die Zeile bereits vergessen hat.

      obj_help = document.getElementsByClassName( "help" );
    

    Wir halten fest: Was in der Variable obj_help gespeichert wird, ist also eine HTMLCollection, die mindestens ein img–Element enthält. ✔️

      for ( i=0; i<obj_help.length; i++ ) {
    

    Und wieder keine Variablendeklaration. Laufindizes als globale Variablen sind sehr nützlich wenn es darum geht, schwer auffindbare Programmfehler zu konstruieren.

    for (let i = 0, length = helpers.length; i < length; i++) {
    

    Wenn du für die Deklaration das Schlüsselwort let verwendest, dann ist der Index nur innerhalb des Anweisungsblocks der Schleife sichtbar. Wechselwirkungen mit gleichnamigen Variablen anderswo im Programm sind auf diese Weise ausgeschlossen. Verwendest du var, ist die Sichtbarkeit immerhin auf den lokalen Scope der Funktion begrenzt.

        if ( obj_help[i].id ) {
    

    Du machst eine Entscheidung davon abhängig, ob irgendwelche Elemente eine id besitzen? Ganz allgemein und nicht etwa abhängig vom Wert des id–Attributs? Oder von anderen Merkmalen? – Die Logik scheint mir nur schwer nachvollziehbar …

          obj_help[i].addEventListener('click', function (event) {
    

    Jetzt ist es passiert! Du hast einem img–Element, das (ohne usemap–Attribut) kein interaktiver Inhalt ist, einen click–Handler zugewiesen. Damit ist dein Hilfetext für Seitenbesucher die auf Tastaturbedienbarkeit angewiesen sind nicht erreichbar. Das ist ziemlicher Mist!

    <button type="button" class="help">
        <img id="werbung" src="img/icon_info.png" alt="Hilfe"/>
    </button>
    

    Für Aktionen auf derselben Seite, wie das Anzeigen eines Hilfetexts, sind buttons da. Die können ohne weiteres Zutun auch per Tastatur (und gegebenenfalls durch andere Eingabegeräte) ausgewählt und aktiviert werden; Ein zugehöriges Bild kann als Elementinhalt notiert oder über CSS eingebunden werden.

    Viele Grüße,

    Orlok