Je profite de quelques beaux jours pour coder en plein soleil et améliorer les outils que je propose à mes clients. Notamment à passer mon interface d'administration en XHTML5 pour profiter des milles avantages qu'amènent ces nouvelles normes. Ce qui demande à la fois une lecture complète des documents, des tests sur les navigateurs modernes quant à l'implémentation des nouveautés, des heures et des heures de lecture des fora du WHATWG. Et bien évidemment de coder en Javascript par manipulations DOM une implémentation.
Le brouillon de la norme HTML5 essaie notamment de rendre facilement implémentable ses apports sur la plupart des navigateurs déjà déployés. Un principe directeur en réaction au rigide de la norme XHTML2 envisagée par le W3C et actuellement abandonné. Quant au XHTML5, le passage en une gestion XML stricte améliore la sécurité du code, permet une extension de la grammaire (microformats embarqués, SVG, schémas de données,...) mais se heurte actuellement au fait que pas mal de bibliothèques JS (éditeurs visuels notamment), abusent du document.write()
qui est à la fois à bannir et strictement absent de la grammaire Javascript/DOM d'une page XML.
Parmi les extensions proposées à la grammaire du HTML, j'en ai repéré une assez intéressante : <input placeholder="" />
. L'attribut placeholder
indique par un aspect fantômatique le type ou un exemple de valeur que l'on attend de la part de l'utilisateur dans un champ. C'est une indication supplémentaire pour les éléments de formulaires.
Arrivée dans la norme en Novembre dernier, cette fonction a déjà été implémentée en catimini dans Safari. Même si l'idée originale d'Apple était l'implémentation d'éléments du GUI uniquement dans des applications comme iTunes dans sa partie store qui utilisent WebKit, le moteur de rendu de Safari pour sa présentation (Songbird fait de même avec XULrunner, wrapup du moteur Gecko de Firefox), leur adoption dans le web correspond à la fois à une demande, et au besoin de ne plus continuellement réinventer la roue mais de standardiser l'idée.
Sur ce blog, lors de sa remise à neuf de 2007, j'avais mis en place un équivalent graphique pour le champ de recherche dans la sidebar : Une image qui disparait si le champ a le focus ou si du texte est présent.
Et comme je suis un mauvais ergonome, j'ai choisi le paradigme de la loupe, ce qui est une très mauvais idée : La même image pourrait être utilisée pour zoomer la page.
Exemples
Supposons que dans un champ d'un formulaire, nous attendons un numéro de téléphone portable en France. Celui-ci peut d'écrire de plusieurs manières :
- En numérotation nationale, sans séparateur :
06XXXXXXXX
- Idem mais en écriture “humaine”, avec des espaces séparant les paires de nombres :
06 XX XX XX XX
- Idem mais avec des tirets :
06-XX-XX-XX-XX
- La numérotation internationale standard :
+336XXXXXXXX
- La numérotation internationale avec code pays et indicatif national de réseau :
[+33](0)6XXXXXXXX
J'ai bien évidemment pris cet exemple parce que j'ai vécu plein de particularismes à vous rendre chèvre le Mouton.
Comme on est des gros flemmards, on va même pas supposer que votre code côté serveur corrige de lui-même parmi ces possibilités en obligeant l'utilisateur du site à utiliser la méthode de notation que l'on a prévu.
Si on veut dans le champ une notation internationale standard, il suffit d'indiquer l'exemple. Si votre navigateur supporte déjà placeholder
, vous devriez les voir :
Il est probable, mais peu pausible, que des navigateurs ultra-consciencieux ignorent l'attribut comme mon blog est en XHTML1, mais ça entrainerait l'existence du Père Noël, ce qui bouleverserait toutes mes théories. Si jamais vous entriez dans ce cas hautement improbable, ou que plus simplement, votre navigateur ne connait pas l'attribut placehover
, et parce que je suis un garçon gentil et poli, voici ce que vous avez manqué :
Le champ texte, sans valeur par défaut et sans le focus
Le même champ, avec le focus
Pour terminer sur le chapitre du support côté HTML, il est utile de noter qu'à l'heure actuelle, la balise <textarea></>
ne gère pas l'attribut placehover
.
Béquilles
Il se trouve qu'émuler cette fonction dans la plupart des navigateurs (j'écarte MSIE6 dans mes interfaces d'administrations pour que mes clients aient un niveau minimum de sécurité quand ils gèrent leurs sites. Et paraît que MSIE8 va enfin comprendre correctement le XHTML natif) se code en une dizaine de lignes JS. La solution que j'ai développée est néanmoins imparfaite car je ne connais pas le rendu de l'attribut placehover
dans les clients web pour déficients visuels.
Il y a aussi un autre problème, nettement plus gênant : placehover
est déjà géré à l'heure actuelle par certains navigateurs, Safari entres autres. Or mon code Javascript se substitue au fonctionnement natif de cette propriété. Ce qui pose plusieurs problèmes éthiques :
- Mon code remplace le fonctionnement normal du GUI du navigateur web, voire du système d'exploitation. Ce qui perturbe le paradigme de l'interface habituelle de l'utilisateur. Tous ne sont pas chevronnés et on imagine mal comme le simple changement d'un curseur de souris ou une couleur de survol perturbent les usages ;
- Cela alourdi, certes très très très légèrement, l'applicatif embarqué de la page, ce qui peut poser des plantages (pour dix lignes, m'étonnerais quand même) ou des incohérences voire des comas en cas de ralentissement. J'avoue que ces derniers cas sont moins fréquent avec des interpréteurs Javascripts de plus en plus véloce et l'arrivée du JIT. Idéalement, le moins de code serait le mieux ;
- C'est un ajoût inutile, comme donner des béquilles à un valide en parfaite santé, ou des lunettes de correction à quelqu'un qui a une excellente vision. Ce n'est même pas du confort, c'est encombrant et contre-productif ;
Si on utilise ce type de programmation plutôt que repérer la disponibilité de la fonction via son user-agent, c'est que dans le cas inverse, on est stratégiquement sûr à 100% de faire des oublis. Le user-agent sniffing est une méthode qui est facilement mis en défaut. Par exemple, de nombreux addons modifient le UA, des faux navigateurs camouflent le moteur réel utilisé (par exemple Trident, le moteur d'Internet Explorer pour Maxthon, ou WebKit Iphone pour Incognito et WebMate). Et oblige à mettre à jour très régulièrement le code avec de multiples tests, sans compter tous les oublis. Ni même envisagé un addon navigateur qui émule ce fonctionnement.
L'approche DOM de l'élément HTML
Et c'est là que commence ma galère. Pour savoir si je dois activer ma dizaine de ligne de code ou laisser le navigateur faire son travail tranquillement, je tente différentes approches, mais toutes échouent pitoyablement.
Le principal obstacle, c'est qu'on ne tente pas de chercher si une propriété javascript existe, comme le font les bibliothèques d'émulations des propriétés comme JSON.*
, mais si un attribut HTML est utilisé dans un comportement d'interface.
Ça semble pas clair ?
Tant pis, j'aurais dû prévenir en haut du billet. Supposons que objet
est un objet DOM lié à la balise <input placeholder />
. On essaie avec cette conditionnelle :
if ( typeof(objet.placeholder)=='undefined' )
{
// ici, mon code
}
Ici, l'idée est de travailler avec objet.placeholder
, propriété que je suppose équivalente à objet.title
par exemple. Mais cette approche échoue. Car il n'y aura plus de nouveaux attributs accessibles en brut. Cette méthode est déjà bannie des documents (correctement déclarés en) XHTML, et elle ne sera pas backportée en HTML.
Désormais, il faut utiliser les méthodes objet.getAttribute()
et objet.getAttribute()
. Mais que la fonction soit ou non implémentée, l'attribut sera toujours accessible par cette méthode, car elle permet justement de créer les émulations en Javascript. Je sais, ça fait râler.
OK, loupé.
L'approche DOM d'une propriété de style
if ( typeof(objet.style.inputPlaceholder)=='undefined' )
{
// ici, mon code
}
Une variante de l'approche précédente, sur l'idée que si l'attribut placeholder=""
est implémenté, alors son stylage l'est aussi :
input { input-placeholder : gray ; }
Pour l'instant, cette propriété CSS expérimentale (comme les colonnes en leur temps) ne propose que de changer la couleur du texte. Comme seule Safari l'a implémenté, celle-ci est préfixée par le nom du moteur comme le demande la norme CSS : -webkit-input-placeholder
. En connaissant le nom de la propriété, j'en ai déduit le nom de la propriété DOM correspondante. Or cela ne reste qu'une supposition, surtout dans les lettres en Majuscules, et comme les variables, fonctions et méthodes javascripts sont des noms sensibles à la casse, c'est pas garanti.
Et comme la norme n'est pas fixée, rien n'interdit que la propriété CSS finale soit complètement différente. Notamment par le fait que cette propriété ne prend qu'une valeur de couleur, ce qui pose un problème d'accessibilité pour les terminaux non-visuels. Après tout, pourquoi pas utiliser un pseudo-élément ?
input:placeholder { color : gray ; }
Comme dit mon confrère Caleb du R&D de la Flander's « Ben t'es pas dans la merde »....
Appel à commentaires
Et là, une fois bloqué sur ce message, il ne me reste plus qu'à espérer que l'un de mes savants lecteurs trouvera la bonne solution.
Sachant que lesdites normes (HTML5, CSS3 et JS2) ne sont encore qu'à l'état de brouillon, ça peut être l'occasion de trouver une solution standard et de la faire remonter au WHATWG.
Mais avant de commenter, vous serez gentils de copier-coller le bloc qui suit (et regardez pas le code source, il est ignoble exprès).
- user-agent :
- objet.getAttribute('id') :
- objet.getAttribute('placeholder') :
- objet.getAttribute('inexistant') :
- typeof(objet.placeholder) :
- typeof(objet.style.inputPlaceholder) :
9 réactions
1 De Da Scritch - 19/03/2009, 08:27
Pour info, mon nouveau téléphone (Nokia XpressMusic 5800, avec navigateur basé sur WebKit), comprend parfaitement l'attribut placeholder.
user-agent :Mozilla/5.0 (SymbianOS/9.4; U; Series60/5.0 Nokia5800d-1/10.0.010; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/413 (KHTML, like Gecko) Safari/413 (Apple Computer, Inc. sur S60)
objet.getAttribute('id') :placeholder (string)
objet.getAttribute('placeholder') :+336XXXXXXXX (string)
objet.getAttribute('inexistant') :null (object)
typeof(objet.placeholder) :undefined
typeof(objet.style.inputPlaceholder) :undefined
2 De VRic - 19/03/2009, 08:28
Un défi avec rien à gagner, c'est moins bon. Exemple :
----------
Créez la couverture du Guide Hachette des Vins 2010 !
Pour sa 25e édition, le Guide Hachette des Vins lance un grand concours de création graphique présidé par
Enki Bilal
Le gagnant verra son projet retenu pour la couverture
du Guide Hachette des Vins 2010, et recevra
également la somme de 1500 € et une cave à vin.
----------
http://www.hachette-vins.com/grand-...
3 De gilboy - 19/03/2009, 09:40
En parlant de lot...
http://www.gagnecado.com/jeu.php?id...
4 De Da Scritch - 19/03/2009, 10:40
Bon.
Déjà qu'il y a beaucoup plus de commentaires quand je sors un billet technique que dans mes chroniques de BD, ça me dégoûte de me lever le samedi matin pour aller à la radio. Mais si c'est pour faire des liens et remarques aussi débiles, j'aurais pu tout aussi bien poster ça sur hardwarefr, commentcamarche ou aspdotnetmasters...
5 De VRic - 19/03/2009, 12:53
Je suis incompétent en JS, donc je ne peux pas vraiment t'aider, mais tout de même mes réflexions de la matinée :
- Soit il existe une méthode normalisée pour tester la disponibilité d'attributs et de fonctions (et je trouve qu'il faudrait), soit non et on doit se rabattre sur tester des effets de bord. Donc trouver et choisir un effet de bord.
- Les effets de bord les plus exploitables ne sont pas forcément les plus bas niveau, c'est même probablement le contraire. Si j'ai bien vu, tes essais attaquent l'implémentation des fonctionnalités dans le moteur de rendu au moment même où tu veux utiliser l'objet, mais pas leur résultat, or comme tu as vu il est dur de deviner des effets de bords potentiels qui n'existent peut-être pas, au lieu d'évaluer le résultat dont on est sûr qu'il existe, de par le fait, pisque c'est un résultat comme son nom l'indique, donc effectif, même s'il est nul, l'effet, bref. C'est même le niveau noobie de l'effet de bord -- le vrai barbu cherche le résultat obscur non attendu pour être le seul à comprendre son code, mais nous on peut bien se contenter de tester si la lumière s'allume pour déduire s'il y a du courant. Reste à voir si on peut tester ça.
- ce que je veux dire :
- suis-je con d'envisager que ce ne soit pas le tout premier truc que tu aies essayé ? Ai-je lu le post trop vite avant de partir ?
- lorsque le navigateur sait gérer le placeholder, le contenu affiché est celui que tu veux, lorsqu'il ne sait pas il reste vide, non ?
- y a-t-il moyen d'attraper le contenu de l'objet tel qu'affiché et le comparer à ce que tu attends, ou juste regarder s'il est vide ?
- si c'est possible ça répond à la question de disponibilité, reste à savoir quand tester -- dans l'objet lui-même au moment de s'en servir, peut-être un peu tard pour remplacer par un script, ou une fois pour toutes à l'arrivée sur la page ou le site dans un objet invisible par ex.
6 De VRic - 19/03/2009, 12:57
user-agent :Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-en) AppleWebKit/523.10.5 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6 (Apple Computer, Inc. sur MacPPC)
objet.getAttribute('id') :placeholder (string)
objet.getAttribute('placeholder') :+336XXXXXXXX (string)
objet.getAttribute('inexistant') :null (object)
typeof(objet.placeholder) :undefined
typeof(objet.style.inputPlaceholder) :undefined
---------------
Concernant la question générale de testabilité des fonctionnalités des navs, je trouve que la suggestion à remonter c'est justement ça, si 1000 gus ne l'ont pas déjà fait, vu que tout le monde se pose la même question.
Si ça devait se normaliser ce ne serait pas exactement du html ou js, plutôt hybride entre les attributs d'identification du nav et les nouvelles fonctionnalités sgbd du html5, donc peut-être plus facilement discutable avec les dévs de navs qu'avec les normaliseurs.
En particulier, Apple devrait être sensible à l'idée puisqu'ils l'ont déjà eue pour le "dictionnaire" des applis scriptables -- l'appli contient et renvoie la liste des commandes et classes AppleScript qu'elle comprend, avec description des attributs et éventuels exemples de code si le dév est consciencieux, en tout cas au moins liste des noms de commandes, classes, attributs. Juste un petit glissement de paradygme pour autoriser l'interrogation du nav par script et à distance, alors qu'en AS on n'en a besoin que dans l'éditeur pendant qu'on code.
Plus exploitable que les man unix car sous forme de BDD normalisée, donc potentiellement queryable, quoiqu'en pratique on ne s'en serve à ma connaissance que pour afficher avec hiérarchies et colorisation syntaxique. Ah si, mon éditeur de script préféré, Smile (gratuit et français), s'en sert pour t'afficher la définition de la sélection, évitant de fouiller les dicos -- ce n'est pas toujours évident de savoir où est implémenté ce dont on se sert en AS ; ce peut être dans AppleScript lui-même, dans l'appli, ou dans des extensions à AS, certaines standard, d'autres tierces. J'ai oublié le détail du machin dans Smile, mais ça rappelle l'équivalent que j'aimais beaucoup dans un de mes vieux éditeurs Logo ou Lisp : sélectionner un mot dans le code et lui demander de le définir.
Si je ne suis pas clair ou que tu es curieux, emprunte le Mac le plus proche et ouvre une appli scriptable dans un éditeur de script ; celui d'origine est dans /Applications/Utilitaires/ (glisser l'icône de l'appli sur celle de l'éditeur, ou menu Fichier:Ouvrir un dictionnaire…)
Toutes les applis ne sont pas scriptables, mais tu devrais aimer par ex le dico de BBEdit (démo téléchargeable), ou de sa version gratuite TextWrangler où manquent peut-être les trucs specifiquement html.
Cette idée aurait aussi un énorme potentiel d'amélioration de l'état de l'art, car les dévs de nav seraient donc amenés à descrire leurs propres fonctionnalités, les plus consciencieux pouvant y inclure la norme en vigueur ; les dévs d'éditeurs se mettraient à inclure l'interrogation des navs en plus ou à la place des sources normalisées de références (qui décrivent la norme mais pas forcément ce qui va se passer, alors que le dico décrit ce qui va se passer, au bug près) ; et tout le monde se retrouverait en concurrence sur quelque chose de directement interrogeable, au lieu de l'état actuel où chacun peut se permettre de vanter sa compatibilité constatée, où IE gagne tant qu'on n'a pas les couilles de cesser de le baby-sitter, le sale con de merde qui pue.
7 De VRic - 19/03/2009, 13:04
Ah ben tiens, tu me rappelles que mon nav masquerade, merci. Alors en vrai:
user-agent :iCab/4.5 (Macintosh; U; PPC Mac OS X) (Apple Computer, Inc. sur MacPPC)
objet.getAttribute('id') :placeholder (string)
objet.getAttribute('placeholder') :+336XXXXXXXX (string)
objet.getAttribute('inexistant') :null (object)
typeof(objet.placeholder) :undefined
typeof(objet.style.inputPlaceholder) :undefined
Mais dis donc, tu serais pas en train d'essayer ce dont je parle ? C'était mentionné dans ton post ? J'ai réfléchi pour rien ?
---------------
Concernant tes insécurités ergonomistales, je note que ton exemple de cas est lui-même discutable : le placeholder c'est judicieux pour un truc indicatif (qui t'apprend la disponibilité ou la destination d'un truc, donc utilité qui disparaît dès que tu as compris que tu peux t'en servir, comme un placeholder), mais c'est con comme modèle (description de la forme d'un n° de tél à saisir), car dès que tu commences à saisir tu ne vois plus le modèle, donc exactement au moment où tu en aurais besoin. Fail :-)
8 De VRic - 19/03/2009, 13:12
Euh, erratum sans intérêt : l'éditeur par défaut est dans /Applications/AppleScript/
9 De Da Scritch - 19/03/2009, 13:33
Tu viens de pointer du doigt une des limites de placeholder, mais qui pourrait aisément être stylé, redéfini, imaginé différemment par CSS/DOM, tout comme je le fais pour l'attribut title dans mon blog (ou par exemple sur http://supplementweekend.fr ) . Et tu viens de montrer en quoi le user-agent sniffing, c'est une approche maladroite.
Après, non, j'ai rien trouvé qui permet de savoir si placeholder entraîne une interaction native du navigateur.