De quel préfixe vendeur votre visiteur a-t-il besoin ?
Mise à jour du 17/12/2013
Le script vient d'être mis à jour pour corriger un bogue de Safari (au moins la version 5.1.7 pour Windows) qui ne permet pas de lister les propriétés de l'objet HTMLElement.style.
La méthode pour récupérer le préfixe JavaScript a donc été réécrite (et du coup optimisée).
D'autre part, il est tenu compte désormais que certaines valeurs de propriétés peuvent nécessiter un préfixe. Si la syntaxe non préfixée de la valeur n'est pas trouvée, une recherche est faite sur une valeur préfixée (tester par exemple display avec la valeur box).
Enfin, il est possible d'intégrer une méthode supplémentaire au constructeur pour la gestion des at-rules, voici le code de cette méthode (à ajouter dans le prototype, voir la page d'exemple) :
Code javascript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | getPrefixedAt: function(prop){ var testStyle = document.createElement('style'); var myparent = document.getElementsByTagName('script')[0].parentNode; myparent.appendChild(testStyle); var testing = testStyle.sheet; try{ testing.insertRule(prop, 0); myparent.removeChild(testStyle); return prop.split(/[\s\{]/)[0]; } catch(e){ try{ prop = prop.replace(/^@/, '@'+this.prefixe.css); testing.insertRule(prop, 0); myparent.removeChild(testStyle); return prop.split(/[\s\{]/)[0]; } catch(e){ myparent.removeChild(testStyle); return false; } } } |
Par exemple, pour @keyframes, un nom est requis puis des accolades, il faudra donc tester @keyframes foo{} qui renverra @keyframes, @-webkit-keyframes ou false en fonction du navigateur.
----------
Présentation
PrefixFinder est un petit script utilitaire permettant de gérer facilement les préfixes vendeurs liés au navigateur de votre visiteur lorsque vous devez manipuler les styles CSS.
Fonctionnalités
PrefixFinder propose diverses fonctions utiles :
- PrefixFinder.prefixe contient un objet composé de deux propriétés (.css et .js) indiquant le préfixe CSS et JavaScript du navigateur ;
- PrefixFinder.getPrefixedProp(propriete) est une méthode retournant un objet composé de deux propriétés (.css et .js) indiquant pour la propriété CSS propriete sa syntaxe CSS et JavaScript pour le navigateur de l'utilisateur ;
- PrefixFinder.getPrefixedPropValue(propriete, valeur) est une méthode retournant un objet composé de trois propriétés (.css, .js et .valeur) indiquant pour la propriété CSS propriete si la valeur valeur est reconnue pour le navigateur de l'utilisateur et s'il faut un préfixe pour cette valeur ;
- PrefixFinder.listPrefixes contient un objet des valeurs déjà cherchées, les clés correspondant à la notation JavaScript non préfixée de la propriété ;
- enfin, deux méthodes .camelize() et .deCamelize() permettent de retourner la notation camelCase de la chaine passée en paramètre ou l'inverse.
Utilisation
Pour mettre en place PrefixFinder sur votre site, il suffit d'intégrer le script dans votre page (si possible dans le <head> (le script n'a pas besoin que la page soit complètement chargée pour fonctionner) puis de définir une instance :
Code javascript : | Sélectionner tout |
var prefixer = new PrefixFinder();
Notes
Certaines propriétés CSS implémentent différemment différentes valeurs. Par exemple, la propriété transform peut nécessiter un préfixe pour les transformations 3D mais pas pour les transformations 2D, la méthode .getPrefixedPropValue() tient compte de cela.
Internet Explorer 8 et inférieurs renvoie une erreur si l'on essaye d'affecter une valeur non valide pour une propriété, c'est pourquoi le code comporte des instructions try catch.
Les préfixes JavaScript, contrairement aux préfixes CSS, ne sont pas normalisés et les navigateurs n'utilisent pas la même règle : certains restent conformes à la règle de transformation de nom CSS <=> JavaScript (lowerCamelCase) alors que d'autres préfèrent indiquer qu'il s'agit d'un préfixe en distinguant les noms lowerCamelCase pour les propriétés standards et les nom UpperCamelCase pour les propriétés préfixées, c'est pourquoi le script fait une recherche du préfixe réel et non dans une liste figée.
Si aucun préfixe n'a été trouvé, on considère qu'il s'agit d'Internet Explorer.
Compatibilité
Sauf erreur de ma part, le script est compatible tous navigateurs et toutes versions.
Pourquoi intégrer les notations CSS ?
S'agissant d'un script JavaScript, la manipulation des styles se fera en JavaScript via l'objet style des éléments HTML. De ce fait, seules les propriétés en notation JavaScript sont supposées être utiles.
Au moins deux cas de figure peuvent cependant nécessiter l'utilisation des propriétés en notation CSS.
- Lorsque l'on modifie plusieurs propriétés de style simultanément, il est conseillé, pour des raisons de performance, d'utiliser la propriété cssText et non chaque propriété individuelle. Or cssText prend comme valeur une chaine dans laquelle la notation des styles est celle de CSS.
- On peut prévoir d'utiliser ce script comme préprocesseur des feuilles de style en parsant l'objet document.styleSheets par exemple et en réécrivant chaque règle si besoin avec les valeurs préfixées. Cela permettra de simplifier l'écriture des feuilles de style en n'écrivant que les propriétés standards et en adaptant celles nécessitant un préfixe directement dans la page Web.
Exemple d'utilisation
Vous pouvez voir sur cette page (regarder la source) une mise en oeuvre de ce script.
N'hésitez pas à poster vos commentaires à la suite voire à proposer des améliorations au code ou à indiquer d'éventuels bogues.
N'hésitez pas non plus à demander à la suite des explications sur des parties du code que vous que vous ne comprendriez pas (que ce soit la syntaxe ou l'utilité ).
Code
Code javascript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | var PrefixFinder = function(){ this.init(); }; PrefixFinder.prototype = { navFullySupported: true, listPrefixes: {}, prefixe: {}, tester: document.createElement('div').style, init: function(){ if(window.getComputedStyle){ var allStyles = window.getComputedStyle(document.documentElement, ''), pre = (Array.prototype.slice.call(allStyles).join('|')); var tmp = pre.match(/\|-([^-]+)-([^|]+)\|/) || (allStyles.OLink === '' && ['', 'o']); this.prefixe.css = '-'+tmp[1]+'-'; tmp[2] = this.camelize(tmp[2], true); if((tmp[1] + tmp[2]) in this.tester){ this.prefixe.js = tmp[1]; return true; } tmp[1] = this.camelize(tmp[1], true); if((tmp[1] + tmp[2]) in this.tester){ this.prefixe.js = tmp[1]; return true; } } this.navFullySupported = false; this.prefixe = {css: '-ms-', js: 'ms'}; }, camelize: function(st, full){ st = st.replace(/-([a-z])/g, function(reg, camel){ return camel.toUpperCase(); }); if(full){ return st.replace(st.charAt(0), st.charAt(0).toUpperCase()); } return st }, deCamelize: function(st){ return st.replace(/[A-Z]/g, function(camel, pos){ return (pos == 0 ? '' : '-') + camel.toLowerCase(); }); }, getPrefixedProp: function(prop){ prop = this.deCamelize(prop); var jsProp = this.camelize(prop); if(this.listPrefixes[jsProp]){ return this.listPrefixes[jsProp]; } if(jsProp in this.tester){ this.listPrefixes[jsProp] = {css: prop, js: jsProp}; return {css: prop, js: jsProp}; } var jsPropPref = this.prefixe.js + jsProp.replace(jsProp.charAt(0), jsProp.charAt(0).toUpperCase()); if(jsPropPref in this.tester){ this.listPrefixes[jsProp] = {css: this.prefixe.css + prop, js: jsPropPref}; return {css: this.prefixe.css + prop, js: jsPropPref}; } this.listPrefixes[jsProp] = {css: false, js: false}; return {css: false, js: false}; }, getPrefixedPropValue: function(prop, valeur){ prop = this.deCamelize(prop); var tester = document.createElement('div').style; var jsProp = this.camelize(prop); test:if(jsProp in tester){ try { tester[jsProp] = valeur; } catch (e) { break test; } if(tester[jsProp] != ''){ return {css: prop, js: jsProp, valeur: tester[jsProp]}; } } jsProp = this.prefixe.js + this.camelize(prop, true); test:if(jsProp in tester){ try { tester[jsProp] = valeur; } catch (e) { break test; } if(tester[jsProp] != ''){ return {css: this.prefixe.css + prop, js: jsProp, valeur: tester[jsProp]}; } } if(valeur.indexOf(this.prefixe.css) == -1){ return this.getPrefixedPropValue(prop, this.prefixe.css + valeur); } var ret = this.getPrefixedProp(prop); ret.valeur = false; return ret; }, getPrefixedAt: function(prop){ var testStyle = document.createElement('style'); var myparent = document.getElementsByTagName('script')[0].parentNode; myparent.appendChild(testStyle); var testing = testStyle.sheet; try{ testing.insertRule(prop, 0); myparent.removeChild(testStyle); return prop.split(/[\s\{]/)[0]; } catch(e){ try{ prop = prop.replace(/^@/, '@'+this.prefixe.css); testing.insertRule(prop, 0); myparent.removeChild(testStyle); return prop.split(/[\s\{]/)[0]; } catch(e){ myparent.removeChild(testStyle); return false; } } } }; |