Deus Figendi: +CSS mehrere Umschalt-Klassen verwalten

Guten Morgen,
entsprechend diverser Empfehlungen aus dem self-Raum, dass "man das so macht" habe ich mir angewöhnt in meinem JavaScript üblicherweise wenn ich den Zustand (=Styles) von Elementen ändern möchte einfach die Klasse zu ändern und im zentralen Stylesheet eben die Unterschiede zu notieren.

Das klappt auch in aller Regel wunderbar, meisten geht es ums ein- und ausblenden.
Jetzt bin ich aber in der Situation, dass ich zwei Kriterien für Zustände habe, nämlich einmal eine Animation und Sichtbarkeit. Hier der relevante Code:

  
  
my_pelement = document.createElement("p");  
my_pelement.appendChild(document.createTextNode("foobar"));  
my_pelement.id = "pelement";  
  
 var style_content = "         \  
                               \  
 #pelement {                   \  
   font-size:200%;             \  
   width:600px;                \  
   text-align:right;           \  
                               \  
   -webkit-transition: all 0.8s ease-in-out; \  
   -moz-transition: all 0.8s ease-in-out;    \  
   -o-transition: all 0.8s ease-in-out;      \  
   transition: all 0.8s ease-in-out;         \  
 }                             \  
                               \  
 #pelement.supersize {         \  
   font-size:500%;             \  
   color:#F00;                 \  
 }                             \  
                               \  
 #pelement.pelement_visible {  \  
   display:block;              \  
 }                             \  
                               \  
 #pelement.pelement_invisible {\  
   display:none;               \  
 }                             \  
 ";  
  
 my_style_element = document.createElement("style");  
 my_style_element.type = "text/css";  
 my_style_element.appendChild(document.createTextNode(style_content));  
 document.getElementsByTagName("head")[0].appendChild(my_style_element);  
  
//und an anderer Stelle in einer Funktion...  
  
function eine_Funktion() {  
 if (!kl_ongoing || etwas_anderes_was_hier_keine_Rolle_spielt) {  
  kl_ongoing = true;  
  my_pelement.className = "supersize";  
  mklcnto = window.setTimeout("my_pelement.className = ''",1000);  
  klof = window.setTimeout("kl_ongoing = false",1200);  
 }  
}  

Soweit funktioniert das (meistens), wird die Funktion aufgerufen ändert sich die Klasse und das font-size ändert sich als Animation. Nachdem die abgelaufen ist (0.8s) wird die Klasse gelöscht (1s) und es wird "zurück animiert".
Das ganze soll nicht mehrfach laufen, wenn die Animation im Gange ist wird idR keine neue angefangen und falls doch eine neue angefangen wird, werden die Timeouts überschrieben(!), damit nicht zurück-animiert wird, bevor fertig hin-animiert wurde.
Soweit klappt das auch, zumindest im für mich benötigten Rahmen ^^ (manchmal zickt es doch noch rum).

Jetzt möchte ich aber das Element auch ausblenden können, daher...
some_checkbox.addEventListener("change", function() { my_pelement.className="pelement_"+this.checked?"visible":"invisible"; }, true);
Und da dürfte das Problem erkennbar werden, zwei verschiedene Funktionen (eine anonym) verändern die Klasse des gleichen Elements, ich habe also insgesamt vier Zustände.

Bekanntermaßen können Elemente ja durchaus mehrere Klassen haben, aber soweit mir bekannt werden sie immer als ein gemeinsamer String verwaltet.
Ich hoffe jetzt einfach mal, dass es eine mir unbekannte Methode gibt die Klassen auch als Array zu verwalten, dann wäre das Problem ja schon gelöst:

.classNameArray[0] = "supersize"; //oder "";  
.classNameArray[1] = "visible";    //oder "invisible";

Gibt es für dieses Problem eine fertige oder übliche Lösung oder sollte ich visible/invisible dann einfach doch über die .style.display (block/none) lösen? Wäre wohl dreckig, aber simpel.

--
sh:( fo:| ch:? rl:( br:& n4:& ie:{ mo:} va:) de:µ_de:] zu:) fl:( ss:| ls:[ js:(
  1. Du könntest auch einfach die String-Methoden "split()" und "join()" verwenden.

    Also eine Klasse hinzufügen wäre dann z.B.:

    var aClasses = eMyElement.className.split(" ");  
    aClasses.push("EineWeitereKlasse");  
    eMyElement.className = aClasses.join(" ");
    

    Das könnte man sogar richtig fein verpacken:

    HTMLElement.prototype.addClass = function(sClassName){  
     var aClasses = this.className.split(" ");  
     aClasses.push("EineWeitereKlasse");  
     this.className = aClasses.join(" ");  
    }
    

    Nur das mit dem Löschen der Klassen ist etwas komplizierter. Aber mit aClasses.splice(iIndexDerZuLöschendenKlasse, 1); geht ja auch das grundsätzlich.

    1. Ups... "join()" ist natürlich keine String-, sondern eine Array-Methode :)

      1. Ja, naja wenn ich das in eine Funktion oder Methode auslagere wird das Löschen auch wieder unkompliziert, zumal ich die Klassen ja auch benennen kann wie ich will und dann z.B. alphabetisch arbeiten...
        size_super, size_big, visible_yes, visible_no
        dann kann ich alphabetisch sortieren und hätte size immer in [0] und visible immer in [1] folglich klappt löschen, überschreiben etc. ganz prima, aber ich hatte eben auf eine ausdrücklich simple Lösung gehofft und dass es vielleicht schon eine Eigenschaft gibt, die ClassNameArray heißt :D (bzw. das tut).

        Aber gut, dann bau ich halt ne Funktion ^^

        --
        sh:( fo:| ch:? rl:( br:& n4:& ie:{ mo:} va:) de:µ_de:] zu:) fl:( ss:| ls:[ js:(
        1. Also da das ganze Projekt nicht wirklich OOP aufgebaut ich hab ich es diesmal auch nicht gemacht, sondern diese Funktion geschrieben:

          function set_className (this_element,this_index,this_value) {  
           var old_value = false;  
           if (!this_element.classNameArray) {  
            this_element.classNameArray = new Array();  
           }  
           if (this_element.classNameArray[this_index]) {  
            var old_value = this_element.classNameArray[this_index];  
           }  
           if (typeof(this_value) != "undefined") {  
            this_element.classNameArray[this_index] = this_value;  
            this_element.className = this_element.classNameArray.join(" ");  
           }  
            
           return old_value;  
          }
          

          Sollte indexierte Klassen entgegennehmen und zurückgeben und eben beliebig setzen lassen. Funktioniert nicht so ganz wie ich gemerkt habe, ich weiß noch nicht warum.
          Aber während ich untersuchte warum es nicht geht entdeckte ich im DOM des Elements eine Eigenschaft namens "classList", die eben die Klassen als Array enthält. Diese Array-Elemente zu beschreiben führte noch nicht zum gewünschten Resultat, aber die Eigenschaft bringt noch einen Satz Methoden mit nämlich
          add()
          contains()
          item()
          remove()
          toString()
          toggle()
          was die machen, wie die Funktionieren weiß ich noch nicht, aber ich schätze, dass ich damit weiter komme. Wenn jemand die Refferenz zu diesen Methoden hat immer mal her damit, ansonsten schau ich mal was Suchmaschinen so dazu sagen...

          --
          sh:( fo:| ch:? rl:( br:& n4:& ie:{ mo:} va:) de:µ_de:] zu:) fl:( ss:| ls:[ js:(
          1. Klappt (im Fox4) ganz ausgezeichnet.
            Fürs Archiv:

              
              
            my_pelement = document.createElement("p");  
            my_pelement.appendChild(document.createTextNode("foobar"));  
            my_pelement.id = "pelement";  
            my_pelement.classList.add("pelement_invisible");  
              
             var style_content = "         \  
                                           \  
             #pelement {                   \  
               font-size:200%;             \  
               width:600px;                \  
               text-align:right;           \  
                                           \  
               -webkit-transition: all 0.8s ease-in-out; \  
               -moz-transition: all 0.8s ease-in-out;    \  
               -o-transition: all 0.8s ease-in-out;      \  
               transition: all 0.8s ease-in-out;         \  
             }                             \  
                                           \  
             #pelement.supersize {         \  
               font-size:500%;             \  
               color:#F00;                 \  
             }                             \  
                                           \  
             #pelement.pelement_visible {  \  
               display:block;              \  
             }                             \  
                                           \  
             #pelement.pelement_invisible {\  
               display:none;               \  
             }                             \  
             ";  
              
             my_style_element = document.createElement("style");  
             my_style_element.type = "text/css";  
             my_style_element.appendChild(document.createTextNode(style_content));  
             document.getElementsByTagName("head")[0].appendChild(my_style_element);  
              
            //und an anderer Stelle in einer Funktion...  
              
              
             if (!kl_ongoing || etwas_anderes_was_hier_keine_Rolle_spielt) {  
              kl_ongoing = true;  
              my_pelement.classList.add("supersize");  
              mklcnto = window.setTimeout("my_pelement.classList.remove('supersize')",1000);  
              klof = window.setTimeout("kl_ongoing = false",1200);  
             }  
              
            some_checkbox.addEventListener("change", function() { my_pelement.classList.toggle("pelement_invisible"); }, true);  
              
            
            

            Ich hoffe ich hab nichts relevantes vergessen :)

            Lt. MDC funktioniert das in Gecko und Webkit und ist bereits WHATWG-spezifiziert.
            Wenns in Presto auch noch läuft bin ich zufrieden :) (hab ich noch nicht ausprobiert).

            --
            sh:( fo:| ch:? rl:( br:& n4:& ie:{ mo:} va:) de:µ_de:] zu:) fl:( ss:| ls:[ js:(
    2. @@Miikku:

      nuqneH

      Also eine Klasse hinzufügen wäre dann z.B.:

      var aClasses = eMyElement.className.split(" ");

      aClasses.push("EineWeitereKlasse");
      eMyElement.className = aClasses.join(" ");

        
      Wozu?? Das kann man doch einfacher haben:  
      `eMyElement.className += " EineWeitereKlasse";`{:.language-javascript}  
      bzw.  
      `eMyElement.className += " " + "EineWeitereKlasse";`{:.language-javascript}  
        
      Problematisch dabei: Wenn eMyElement schpn der Klasse EineWeitereKlasse angehört, wird sie ein zweites Mal ins @class-Attribut geschrieben.  
        
      Man müsste also, wenn man mit Array arbeitet, dieses nach EineWeitereKlasse durchsuchen.  
        
      Oder gleich den Wert von @class mit Stringfunktion nach EineWeitereKlasse durchsuchen.  
        
      Qapla'
      
      -- 
      Gut sein ist edel. Andere lehren, gut zu sein, ist noch edler. Und einfacher.  
      (Mark Twain)
      
      1. Hi,

        Oder gleich den Wert von @class mit Stringfunktion nach EineWeitereKlasse durchsuchen.

        Dabei aber aufpassen, daß man nicht EineWeitereKlasseZwei oder KEineWeitereKlasse erwischt, sprich: mit Wortgrenzen suchen!

        cu,
        Andreas

        --
        Warum nennt sich Andreas hier MudGuard?
        O o ostern ...
        Fachfragen per Mail sind frech, werden ignoriert. Das Forum existiert.
        1. @@MudGuard:

          nuqneH

          Oder gleich den Wert von @class mit Stringfunktion nach EineWeitereKlasse durchsuchen.

          Dabei aber aufpassen, daß man nicht EineWeitereKlasseZwei oder KEineWeitereKlasse erwischt

          Ja.

          sprich: mit Wortgrenzen suchen!

          Wenn du damit '\b' meinst: Nein, sondern (?:^|\s) und (?:$|\s).

          Qapla'
          --
          Gut sein ist edel. Andere lehren, gut zu sein, ist noch edler. Und einfacher.
          (Mark Twain)

      2. @@Gunnar Bittersmann:

        nuqneH

        Problematisch dabei: Wenn eMyElement schpn der Klasse EineWeitereKlasse angehört, wird sie ein zweites Mal ins @class-Attribut geschrieben.

        Man müsste also, wenn man mit Array arbeitet, dieses nach EineWeitereKlasse durchsuchen.

        Oder gleich den Wert von @class mit Stringfunktion nach EineWeitereKlasse durchsuchen.

        Wie man’s auch macht, dasselbe brauch man auch für die Methode deleteClass(), die man ja sicherlich auch implementieren möchte.

        Könnte man dann die dann nicht in addClass() verwenden?

        HTMLElement.prototype.addClass = function(sClassName)  
        {  
          this.deleteClass(sClassName);  
          this.className += " " + sClassName;  
        }
        

        Hm, würde dadurch für den Bruchteil eines Wimpernschlags das betreffende Element nicht der betreffenden Klasse angehören? Könnte das problematisch sein?

        Qapla'

        --
        Gut sein ist edel. Andere lehren, gut zu sein, ist noch edler. Und einfacher.
        (Mark Twain)
        1. Hallo,

          HTMLElement.prototype.addClass = function(sClassName)

          {
            this.deleteClass(sClassName);
            this.className += " " + sClassName;
          }

          
          >   
          > Hm, würde dadurch für den Bruchteil eines Wimpernschlags das betreffende Element nicht der betreffenden Klasse angehören?  
            
          ja, theoretisch.  
            
          
          > Könnte das problematisch sein?  
            
          IMHO nein. Die beiden Anweisungen werden ja unmittelbar nacheinander ausgeführt; der Browser hat zwischenzeitlich keine Gelegenheit, den Interims-Zustand zur Kenntnis zu nehmen, geschweige denn darauf zu reagieren - z.B. mit einem Rendern und Anzeigen dieses Zustands.  
            
          So long,  
           Martin  
          
          -- 
          Dem Philosoph ist nichts zu doof.  
          Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
          
    3. @@Miikku:

      nuqneH

      var aClasses = eMyElement.className.split(" ");

      Das ist problematisch. Du gehst davon aus, dass zwischen zwei Klassenbezeichnern jeweils genau ein Leerzeichen stehen. Das muss nicht so sein:
      (1) Es können auch mehrere sein, was unnötige leere Arrayelemente zur Folge hätte.
      (2) Es können auch andere Whitespace-Zeichen (\t, \r) zwischen den Klassenbezeichnern stehen.

      Qapla'

      --
      Gut sein ist edel. Andere lehren, gut zu sein, ist noch edler. Und einfacher.
      (Mark Twain)
    4. Also wie gesagt gibt es ein entsprechendes Konstrukt:
      Mozilla DevCenter
      Läuft in Gecko und Webkit (zumindest in Chrome). Dabei steht aber auch ein Nachrüst-Konstruktor, der zwar Arsch-lahm ist aber er funktionier.
      Ich musste eine Kleinigkeit anpassen, damit es in Presto läuft und hab das auch der Autorin mitgeteilt.
      Da mich der IE nicht interessiert, habe ich mich darum nicht gekümmert, aber so wie ich das verstanden habe soll es damit auch gehen.

      Und Klassen hinzufügen war nie das Problem, nur das Löschen/Finden etc. wollte ich halt gerne einzeilig haben.

      --
      sh:( fo:| ch:? rl:( br:& n4:& ie:{ mo:} va:) de:µ_de:] zu:) fl:( ss:| ls:[ js:(
  2. Lieber Deus Figendi,

    entsprechend diverser Empfehlungen aus dem self-Raum, dass "man das so macht" habe ich mir angewöhnt in meinem JavaScript üblicherweise wenn ich den Zustand (=Styles) von Elementen ändern möchte einfach die Klasse zu ändern und im zentralen Stylesheet eben die Unterschiede zu notieren.

    diese Richtlinie ist prinzipiell gut, genügt in manchen Fällen aber nicht den Anforderungen. Gerade dann, wenn Animationen ins Spiel kommen, reicht ein Ändern der Klassennamen nicht. In diesem Fall ist genau abzuwägen, wie Du im Einzelfall vorgehst.

    Für meine JS-Animationen habe ich auf der einen Seite auch mit Klassen gearbeitet, aber nicht, um per CSS das Aussehen zu steuern. Kannst es Dir ja anschauen.

    Liebe Grüße,

    Felix Riesterer.

    --
    ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
    1. Wha, meine Güte!
      Sorry Felix, ich schätze deine Beiträge normalerweise sehr, aber in diesem Fall scheint mir die Lösung über transitions und Classlist doch viel geschickter als dieser Riesenberg an JS.

      Sinn und Zweck meiner Übung ist, dass ein P-Element "bounced" (also groß und wieder klein wird) wenn man Tasten drückt. Das soll man abschalten können und dann soll auch das ganze Element ausgeblendet sein.

      Da ist deine Animations-Bibliothek völliger overkill.
      Zumal es in keinem IE laufen muss, das ist mir eh zu anstrengend ^^
      Gecko, Webkit und Presto mögen funktionieren, der Rest (Lynx z.B.) ist mir Schnuppe, zumal ich eh so viel modernes Zeug in dem Projekt habe, dass zuweilen sogar Fox3.6 versagte (das hab ich aber gefixt, nur nicht auf Anhieb bemerkt, weil ich auf Fox4 und Iron teste).

      Gleichzeitig ist das ganze allerdings total oldschool geschrieben, so mit extrem wenig Objektorientierung, vielen globalen Variablen etc.
      Es soll aber letztlich eh nur in einem leeren HTML-Dokument laufen, mein HTML sieht nämlich so aus:

      <!DOCTYPE HTML>  
      <html>  
       <head>  
        <title>Generator</title>  
       </head>  
       <body>  
        <p id="no_script">Ihr Browser beherrscht leider nicht die notwendigen Faehigkeiten um diesen Generator zu benutzen.  
        Oder Sie haben Ihr JavaScript deaktiviert.</p>  
       </body>  
       <script type="text/javascript" src="./Gen.js"></script>  
      </html>
      
      --
      sh:( fo:| ch:? rl:( br:& n4:& ie:{ mo:} va:) de:µ_de:] zu:) fl:( ss:| ls:[ js:(
      1. Lieber Deus Figendi,

        Felix, ich schätze deine Beiträge normalerweise sehr,

        danke für das Lob!

        aber in diesem Fall scheint mir die Lösung über transitions und Classlist doch viel geschickter als dieser Riesenberg an JS.

        Du kennst Dein Projekt am besten und deshalb kannst nur Du entscheiden.

        Da ist deine Animations-Bibliothek völliger overkill.

        Ich wollte nicht vorschlagen, dass Du sie einsetzt (was Du natürlich darfst), sondern nur meine Vorgehensweise bezüglich der diskutierten Richtlinie demonstrieren.

        Liebe Grüße,

        Felix Riesterer.

        --
        ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)