anyone: Welcher Browser unterstützt transform-style: preserve-3d?

Moin,

Ich habe bischen was mit transform-style: preserve-3d; in CSS3 gebastelt, nach dem Beispiel von:

http://css-tricks.com/almanac/properties/p/perspective-origin/

Es funktioniert auf den aktuellen Browsern und auf dem Samsung S3 jedoch nicht auf älteren Android Handys. Apple kann ich erst testen, wenn der Saturn wieder auf hat ;)

Frage nun: wie kann ich testen, ob eine Engine das transform-style: preserve-3d; unterstützt und falls nicht auf eine andere Seite oder zumindest eine andere CSS Klasse umleiten?

Eine Useragent Abfrage mit Ergebnissen wie "Firefox" oder "Android" reicht ja in dem Fall nicht...

Danke & frohe Ostern :)

  1. vielleicht hilft Can I Use: http://caniuse.com/#search=transform

    grüsse Kai

    1. vielleicht hilft Can I Use: http://caniuse.com/#search=transform

      Danke ich weiss es aber suche ein php oder javascript un beducher mit altem browser um zu leiten..

  2. Hallo,

    Es ist immer schwierig, CSS-Unterstützung mit JavaScript zu testen. Grundsätzlich kann man testen, ob das Browser eine CSS-Eigenschaft kennt, indem man prüft, ob eine entsprechende Objekteigenschaft in JavaScript beim CSSStyleDeclaration-Interface existiert.

    Beispiel:
    var supported = 'perspectiveOrigin' in document.documentElement.style;

    document.documentElement ist hier das HTML-Element. Zum in-Operator siehe: http://aktuell.de.selfhtml.org/artikel/javascript/objektabfragen/#in

    Das liefert einem, ob der Browser schon einmal etwas von dieser Eigenschaft gehört hat. Es garantiert nicht, dass der Browser die Eigenschaft mit allen spezifzierten Werten korrekt umsetzt. Vielleicht unterstützt er perspective-origin, aber nur mit dem für dich uninteressanten Standardwert »flat«.

    Außerdem werden bei obigem einfachen Test Vendor-Prefixes nicht beachtet. Man muss sie alle durchtesten, bis man einen Treffer findet. Den gefundenen Namen kann man nutzen, wenn man Inline-Styles per JavaScript setzen will.

    Ich verwende immer diese allgemeine Funktion, die vom Aufbau der aus der Modernizr-Bibliothek entspricht.

    // camelCaseHelper  
    var camelize = function(str) {  
      return str.replace(/[-_\s]+(.)?/g, function(_, c) {  
        return c.toUpperCase();  
      });  
    };  
      
    /*  
     * Tests browser support of a given CSS property.  
     * @param property The base property to test  
     * @returns The prefixed property as a string or false if not supported.  
     */  
    var testCSSProperty = function(property) {  
      var style = document.documentElement.style;  
      var stringType = 'string';  
      if (typeof style[property] === stringType) {  
        return property;  
      }  
      var prefixes = ['Moz', 'Webkit', 'Khtml', 'O', 'ms'];  
      var camelCaseProperty = property.charAt(0).toUpperCase() +  
          camelize(property.substring(1));  
      for (var i = 0, l = prefixes.length; i < l; i++) {  
        var prefix = prefixes[i];  
        var prefixedProperty = prefix + camelCaseProperty;  
        if (typeof style[prefixedProperty] === stringType) {  
          return prefixedProperty;  
        }  
      }  
      return false;  
    };
    

    Anwendungsbeispiele:

    console.log('color', testCSSProperty('color'));  
    console.log('animation', testCSSProperty('animation'));  
    console.log('transform', testCSSProperty('transform'));  
    console.log('perspective-origin', testCSSProperty('perspective-origin'));  
    console.log('husseldiguggeldidu', testCSSProperty('husseldiguggeldidu'));
    

    Gibt in Chrome z.B.

    color color  
    animation WebkitAnimation  
    transform WebkitTransform  
    perspective-origin WebkitPerspectiveOrigin  
    husseldiguggeldidu false
    

    Wenn der Browser also die Eigenschaft kennt, wird der JavaScript-Eigenschaftsname ggf. mit Präfix zurückgegeben. Andernfalls wird false zurückgegeben.

    Das lässt sich natürlich auch verwenden, um eine Klasse zu setzen oder eine Umleitung auszulösen:

    if (!testCSSProperty('perspective-origin')) {  
      // Keine Unterstützung. Setze z.B. eine Klasse  
    }
    

    Wie gesagt, hiermit wird nur getestet, ob der Browser die Eigenschaft aus JavaScript-Sicht kennt, nicht ob sie konkret den Wert preserve-3d erlaubt und dieser korrekt implementiert ist. Das lässt sich m.W. nicht ohne weiteres mit JavaScript prüfen. (Mit PHP sowieso nicht.)

    Mathias

    1. Das liefert einem, ob der Browser schon einmal etwas von dieser Eigenschaft gehört hat. Es garantiert nicht, dass der Browser die Eigenschaft mit allen spezifzierten Werten korrekt umsetzt. Vielleicht unterstützt er perspective-origin, aber nur mit dem für dich uninteressanten Standardwert »flat«.

      Zur Ergänzung, es gibt durchaus eine Möglichkeit, um die Unterstützung von Werten zu testen. Das macht Modernizr meines Wissens auch.

      Es wird zunächst geprüft, ob der Browser die Eigenschaft kennt, und der Eigenschaftsname wird in Erfahrung gebracht. (Das habe ich bereits demonstriert.)

      Dann wird der gewünschte Inline-Style gesetzt und wieder ausgelesen. Wenn ausgelesene Wert mit dem gesetzten identisch ist, geht man davon aus, dass der Wert akzeptiert wurde. Der Browser muss unbekannte und syntaktisch falsche Werte nämlich ignorieren. Beispiel:

      var testCSSValue = function(property, value) {  
        var property = testCSSProperty(property);  
        if (!property) return false;  
        // Erzeuge ein Test-Element ohne Inline-Styles  
        var testElement = document.createElement('div');  
        console.log('vorher:', testElement.style[property]);  
        // … müsste einen leeren String ergeben, weil noch kein Inline-Style gesetzt wurde  
        // Setzen des fraglichen Wertes:  
        testElement.style[property] = value;  
        console.log('nachher:', testElement.style[property]);  
        // … müsste den gesetzten Wert ergeben ODER einen leeren String, wenn der Browser den Wert nicht akzeptiert hat  
        // Gleichheit prüfen  
        return testElement.style[property] === value;  
      };  
        
      var preserve3dSupported = testCSSValue('perspective-origin', 'preserve-3d');  
      console.log('preserve3dSupported', preserve3dSupported);
      

      Mathias

      1. Wow hammer VIELEN DANK :)

        Einzig kleines Problem: Es läuft nicht. Ist es kein Javascript?

        <html><body>

        <script type="text/javascript">
        <!--
        var $testCSSvalue = function($property, $value) {
          var $property = testCSSproperty($property);
          if (!$property) return false;
          // Erzeuge ein Test-Element ohne Inline-Styles
          var $testElement = document.createElement('div');
          alert('vorher:'+ $testElement.style[$property]);
          // … müsste einen leeren String ergeben, weil noch kein Inline-Style gesetzt wurde
          // Setzen des fraglichen Wertes:
          $testElement.style[$property] = $value;
          alert('nachher:'+ $testElement.style[$property]);
          // … müsste den gesetzten Wert ergeben ODER einen leeren String, wenn der Browser den Wert nicht akzeptiert hat
          // Gleichheit prüfen
          return $testElement.style[$property] === $value;
        };

        var $preserve3dSupported = $testCSSvalue('perspective-origin', 'preserve-3d');
        alert('$preserve3dSupported' + $preserve3dSupported);

        //-->
        </script>

        </body></html>

        1. Hallo,

          Einzig kleines Problem: Es läuft nicht. Ist es kein Javascript?

          Bitte lies meine Beschreibungen zum Code und versuche zu verstehen, was der Code tut. Bitte schau dir mein vollständiges Beispiel an.

          Wenn ein Script »nicht läuft«, dann schau am besten auf der JavaScript-Konsole nach, ob es Fehlermeldungen gibt. Diese helfen in den allermeisten Fällen schon weiter.

          In der zweiten Zeile wird die Funktion testCSSproperty aufgerufen, die in deinem Code fehlt. Wie gesagt baut der Test, ob eine CSS-Eigenschaftswert unterstützt wird, auf dem Test auf, dass die Eigenschaft unterstützt wird.

          Nur am Rande: Warum du sämtliche Bezeichner geändert hast und mit einem $ versehen hast, ist mir schleierhaft. Das ist natürlich dir überlassen, ich möchte nur anmerken, dass das in JavaScript nicht nötig und nicht üblich ist, im Gegensatz zu PHP, wo alle Variablen mit $ beginnen.

          var $preserve3dSupported = $testCSSvalue('perspective-origin', 'preserve-3d');

          Den Fehler in dieser Zeile habe ich bereits korrigiert: Du suchst transform-style, nicht perspective-origin – ich habe mich durch deinen Link im Ausgangsposting verwirren lassen.

          Mathias

          1. Om nah hoo pez nyeetz, molily!

            Bitte lies meine Beschreibungen zum Code und versuche zu verstehen, was der Code tut.

            Schon der CamelCaseHelper dürfte viele überfordern. Ich verstehe zwar den Code (wahrscheinlich nur, weil ich weiß,  was als Ergebnis geliefert werden soll), wäre aber nie auf diese Idee gekommen. Also, wenn du mal Zeit und Lust hast, würde ich mich über eine ausführliche Darstellung des Helpers freuen.

            Matthias

            --
            Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Mist und Mistral.

            1. Schon der CamelCaseHelper dürfte viele überfordern. Ich verstehe zwar den Code (wahrscheinlich nur, weil ich weiß,  was als Ergebnis geliefert werden soll), wäre aber nie auf diese Idee gekommen.

              Für die Feature-Abfrage ist der Helper nur ein Implementierungsdetail, ich kann ihn aber gerne beschreiben.

              CSS-Eigenschaften verwenden Bindestriche, die zugehörigen JavaScript-Objekteigenschaften hingegen camelCase. Beispiel: background-color > backgroundColor. Die camelize-Funktion wandelt die Eigenschaft entsprechend um.

              Dazu könnte man einen kleinen Parser schreiben: Eine Schleife durchläuft den String und fügt das aktuelle Zeichen an einen neuen String. Findet man einen Bindestrich, wird dieser ignoriert und mithilfe eines Flags das nächste eingelesene Zeichen in einen Großbuchstaben umgewandelt.

              Einfacher geht es mittels Suchen/Ersetzen mit einem regulären Ausdruck. Finde beliebige Zeichen, die auf einen Bindestrich folgen:

              /-./

              Diese Zeichenfolge wollen wir nun durch das gefundene Zeichen ersetzen, nachdem es mit http://de.selfhtml.org/javascript/objekte/string.htm#to_upper_case@title=toUpperCase behandelt wurde.

              Die replace-Methode erlaubt es, als zweiten Parameter anstelle eines festen Strings eine Callback-Funktion anzugeben. Schema:

              [String].replace([RegExp], [Function])

              Diese Funktion wird für jeden Treffer ausgeführt. Sie bekommt den gesamten Treffer sowie die Teiltreffer in Klammern (Submatches) als Parameter übergeben und kann einen String zurückgeben, der den Treffer ersetzt. Damit lässt sich ein individueller Ersatz für jeden Treffer erzeugen. Erster Versuch:

              "foo-bar-qux".replace(/-./g, function(match) {  
                return match.toUpperCase();  
              });
              

              Ergebnis: "foo-Bar-Qux"

              Jetzt müssen wir noch den Bindestrich ignorieren. Dafür können wir mit Submatches arbeiten (ginge auch anders, z.B. mit http://de.selfhtml.org/javascript/objekte/string.htm#substring@title=substring(1)). Wir klammern einfach den ».«-Platzhalter:

              "foo-bar-qux".replace(/-(.)/g, function(match, submatch) {  
                return submatch.toUpperCase();  
              });
              

              Den gesamten Match ignorieren wir, sondern benutzen nur Submatch und rufen toUpperCase auf.

              Ergebnis: "fooBarQux"

              Das ist eigentlich alles. Die Funktion, die ich gepostet habe, ist noch etwas flexibler, aber für den hiesigen Anwendungsbereich würde obiges ausreichen.

              Mathias

              1. Om nah hoo pez nyeetz, molily!

                Vielen Dank. Hast du was dagegen, wenn aus dem Gesamtpaket ein Artikel für das Blog wird?

                Matthias

                --
                Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Lebensmittel und Lebensmittelpunkt.

                1. Hast du was dagegen, wenn aus dem Gesamtpaket ein Artikel für das Blog wird?

                  Nein, natürlich nicht. Solange ich ihn nicht schreiben muss! ;-)

                  Ich helfe natürlich gerne, aber in den nächsten Wochen bin ich erstmal verplant.

                  Vor allem die Referenzen auf Modernizr sollte man noch ausarbeiten (Ehre wem Ehre gebührt):

                  camelize:
                  https://github.com/Modernizr/Modernizr/blob/master/src/cssToDOM.js
                  Vgl. auch lodash.string:
                  https://github.com/epeli/underscore.string/blob/master/lib/underscore.string.js#L339-L341
                  Vgl. auch Prototype:
                  https://github.com/sstephenson/prototype/blob/master/src/prototype/lang/string.js#L552-L573
                  (Es gibt wahrscheinlich noch ältere Vorkommen, ich unterbreche einmal hier…)

                  CSS-Eigenschaften testen:
                  https://github.com/Modernizr/Modernizr/blob/master/src/omPrefixes.js
                  https://github.com/Modernizr/Modernizr/blob/master/src/testPropsAll.js
                  https://github.com/Modernizr/Modernizr/blob/master/src/testProps.js
                  https://github.com/Modernizr/Modernizr/blob/master/src/nativeTestProps.js

                  Letztere Datei zeigt, dass es eine native Erkennung über eine JavaScript-API gibt: window.CSS.supports(property, value). Das war mir noch nicht bekannt. Falls diese nicht verfügbar ist, wird eine @support-Regel verwendet, erst dann als Fallback die von mir beschriebene Methode verwendet (prüfen, ob Objekteigenschaft existiert, Wert setzen, Wert vergleichen.)

                  Der Artikel sollte dann auf jeden Fall die modernen Methoden nennen.

                  Mathias

                  1. Om nah hoo pez nyeetz, molily!

                    Hast du was dagegen, wenn aus dem Gesamtpaket ein Artikel für das Blog wird?

                    Nein, natürlich nicht. Solange ich ihn nicht schreiben muss! ;-)

                    Oh oh.

                    Matthias

                    --
                    Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Dia und Diana.

                  2. Hallo,

                    wen es interessiert, ich habe auf der JSunconf in Hamburg einen Vortrag zum Thema gehalten:

                    http://molily.de/weblog/css-support
                    https://molily.github.io/css-support/

                    Mathias

    2. Hallo,

      ich habe den Code auf transform-style ausgerichtet (der Verweis auf perspective-origin hatte mich verwirrt ;)) und hier zusammengefasst:

      http://codepen.io/molily/pen/kFwJc/?editors=001

      Mathias