11548 sujets

JavaScript, DOM et API Web HTML5

Pages :
Bonjour ou bonsoir à tous,

Vous m'excuserez pour le titre un peu sibyllin, je ne suis pas sûr d'avoir trouvé les bons mots. Smiley cligne

Pour diverses intégrations, on m'a demandé des champs de formulaire affichant un texte explicatif ou destiné à attirer l'attention. L'exemple typique est le formulaire de recherche sur un site, sans options:
<form>
	<input type="text" value="Recherche" />
	<input type="submit" />
</form>
(Ce code est bien sûr simplifié à l'extrême, et par ailleurs pas valide en Strict.)

Le comportement attendu est que lorsque le champ texte reçoit le focus, le texte «Recherche» disparait, ce qui évite à l'utilisateur d'avoir à l'effacer.

J'ai fait quelque chose ressemblant à ceci:
<form id="recherche" method="get" action="#">
	<input class="text" type="text" title="Recherche sur le site Machin"
		value="Recherche..."
		onfocus="if(this.value=='Recherche...'){this.value=''};"
		onblur="if(this.value==''){this.value='Recherche...'};"
	/>
	<input class="button" type="image" src="styles/img/trans-square-24.png" alt="OK" />
</form>
(Le title est là pour l'accessibilité. J'ai cru comprendre qu'il pouvait être lu par les lecteurs d'écran, et ainsi remplacer un label en bonne et due forme lorsque le design ne permet pas d'en placer un. Mais peut-être est-ce juste une façon de limiter les dégâts? À confirmer.)

Sauf erreur de ma part, ça fonctionne correctement aussi bien à la souris qu'au clavier. Donc la solution me convient bien. Partant, mes questions sont:

1. En l'absence de JS, quel comportement vous semble préférable: pas de value, ou bien un value qu'il faut supprimer «à la main»? En sachant qu'il n'y a pas ou peu d'information autre que le title sur le rôle du champ de formulaire.

2. Y a-t-il, selon vous, un intérêt à externaliser le JavaScript?

3. Si oui, avez-vous des exemples simples que je pourrais étudier?

Merci d'avance pour vos avis. Smiley smile
Hello Florent Smiley smile ,

je dirais que l'intérêt d'externaliser est de séparer le contenu du comportement Smiley ravi !

Un pitit exemple :

index.php
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<title>Test addInputValue</title>
	<script type="text/javascript" src="script.js"></script>
</head>
<body>
<form action="#" method="post">
<p>
<input class="text" type="text" id="rechercher" name="rechercher" title="Recherche sur le site Machin" value="" />
<input class="button" type="image" src="styles/img/trans-square-24.png" alt="OK" />
</p>
</form>
</body>
</html>

script.js
//fonction addEvent
function addEvent(oElem, sEvType, fn, bCapture)
{
   return oElem.addEventListener?
      oElem.addEventListener(sEvType, fn, bCapture):
      oElem.attachEvent?
         oElem.attachEvent('on' + sEvType, fn):
         oElem['on' + sEvType] = fn;
}


//fonction addInputValue
function addInputValue()
{
	// si les méthodes n'existent pas, on sort...
	if(!document.getElementById || !document.getElementsByTagName)
	{
		return;
	}

	// Alimentation du value
	var oElem = document.getElementById("rechercher");
	oElem.value = 'Rechercher...';

	// addEventListener sur le onfocus
	addEvent(oElem, 'focus', addInputFocus, false);

	// addEventListener sur le onblur
	addEvent(oElem, 'blur', addInputBlur, false);
}
	
	
//fonction addInputFocus 
function addInputFocus(event)
{
		var oElem = document.getElementById("rechercher");
		if (oElem.value == 'Rechercher...') {
			oElem.value = '';
		}
}

//fonction addInputBlur 
function addInputBlur(event)
{
		var oElem = document.getElementById("rechercher");
		if (oElem.value == '') {
			oElem.value = 'Rechercher...';
		}
}


// Initialisation
addEvent(window, 'load', addInputValue, false);

Je suis pas un pro donc il y a peut-être mieux à faire Smiley murf !

A+
C'est une bonne idée, j'aime bien ce genre de petites attentions aux utilisateurs,

Encore un intérêt de la fonction addEvent avec le this :
function addEvent(obj, type, fn, par){
  if(obj.addEventListener){
    obj.addEventListener(type, function(event){        
      return fn.call(obj, event, par);
    }, false );
  }else if(obj.attachEvent){
    obj.attachEvent("on"+type, function(e){
      if (!e) var e = window.event;   
      return fn.call(obj, e, par);
    });
  }
}

addEvent(window,'load',function(){

  addEvent(document.getElementById('rechercher'),'focus',function(){
    if(this.value=='Recherche...') this.value='';
  });

  addEvent(document.getElementById('rechercher'),'blur',function(){
    if(this.value=='') this.value='Recherche...';
  });

});

Modifié par matmat (01 Jul 2008 - 00:07)
Bonsoir,
Florent V. a écrit :
1. En l'absence de JS, quel comportement vous semble préférable: pas de value, ou bien un value qu'il faut supprimer «à la main»? En sachant qu'il n'y a pas ou peu d'information autre que le title sur le rôle du champ de formulaire.

Il me semble que la présence d'une valeur par défaut inutile (en tant que valeur par défaut en tout cas) est plus une gêne pour l'utilisateur qu'autre chose, donc j'aurais tendance à choisir la première solution. Peut-être que quand JS est activé on peut utiliser la valeur de title comme valeur par défaut du champ ?
Florent V. a écrit :
2. Y a-t-il, selon vous, un intérêt à externaliser le JavaScript?

L'intérêt est à peu près le même qu'en CSS : organisation et factorisation du code, mais au prix d'un peu (parfois beaucoup) plus de complexité. A toi de voir, donc. Smiley smile

@matmat : ta fonction addEvent provoque une fuite de mémoire sous IE.
Modifié par Julien Royer (01 Jul 2008 - 01:28)
Salut Julien,

Je ne connais pas grand chose aux problèmes de fuite de mémoires, est ce que un simple :

    }else if(obj.attachEvent){
      var ieHandler = function(e){
        if (!e) var e = window.event;   
        return fn.call(obj, e, par);
      };
      obj.attachEvent("on"+type, ieHandler);
      var ieHandler = null;
    }

résoudrais le problème? ou alors il faut détacher la fonction de l'événement?

Désolé Florent, je vais peut-être faire un autre post, ça n'a plus trop de rapport avec le sujet initial.
Modifié par matmat (01 Jul 2008 - 02:55)
Merci à tous pour vos réponses.

À vrai dire, externaliser le JS ne me semble pas indispensable ici. La surcharge du code HTML est limitée tant qu'il y a peu de champs de ce type. Mais ça peut être intéressant.

Si on détache la gestion des évènements et qu'on estime que sans JS le value doit être vide, on peut imaginer une fonction JS qui reproduirait le comportement que je décris et à laquelle on passerait en arguments:
1. l'id du champ de formulaire;
2. le texte à utiliser pour le value;
3. éventuellement, un nom de classe à ajouter/supprimer lorsque le champ prend le focus ou le perd (pour palier à la non-gestion de :focus par IE, dans le cas où on voudrait du texte gris en temps normal et noir lorsque le champ a le focus, par exemple).

On peut imaginer une autre fonction fn(x,"value|title", focusclass) qui reproduirait ce comportement pour tous les champs de classe x, en utilisant soit la valeur du value, soit celle du title (au choix), et à nouveau avec un mécanisme de classe au focus pour IE.

On peut même faire ça avec une seule fonction, je pense. Il faudra que je tente ça un de ces quatre.
Et voilà une fonction, sur base jQuery:

http://web.covertprestige.info/test/45-champ-formulaire-texte-au-focus.html

J'ai pas mal galéré car mes connaissances en JS sont maigres, ma connaissance de jQuery presque inexistante (j'ai donc plongé dans la doc), et j'ai mis la barre un peu haut. Le résultat me semble correct.

Voici la fonction en question:
function textInputDynamicValue (input_selector, text_to_use, def_class) {
	if (text_to_use == undefined) text_to_use = 'title';
	if (def_class == undefined) def_class = 'dvDefault';
	var dynamic_value = [];
	$(input_selector).each(function(j) {
		switch (text_to_use) {
			case 'title' :
				dynamic_value[j] = $(this).attr('title');
				break;
			case 'value' :
				dynamic_value[j] = $(this).attr('value');
				break;
			default :
				dynamic_value[j] = text_to_use;
		}
		$(this).attr('value', dynamic_value[j]).addClass(def_class)
		.focus(function() {
			if (this.value == dynamic_value[j]) {
				this.value = '';
				$(this).removeClass(def_class);
			}
		})
		.blur(function() {
			if (this.value == '') {
				this.value = dynamic_value[j];
				$(this).addClass(def_class);
			};
		});
	});
};

Je serais ravi d'avoir les commentaires de scripteurs expérimentés. Y a-t-il de gros problèmes de conception, des détails à améliorer, des contrôles à rajouter?

J'ai pensé rajouter un contrôle pour bloquer l'exécution de la fonction (enfin, d'une partie de la fonction) si jamais l'élément traité n'était pas un INPUT de type "text". J'ai bricolé ça rapidement avec un test de this.type, et ça marche dans Firefox, mais ça ne m'a pas semblé génial. Vu que j'utilise jQuery, j'imagine que je pourrais utiliser jQuery.filter? Au lieu d'avoir:
$(input_selector).each(function(j)

j'aurais alors:
$(input_selector).filter('input[@type=text]').each(function(j)

À voir si ça passe dans les navigateurs. Un élément INPUT sans attribut type sera automatiquement de type texte, ce qui est reconnu par Firefox, mais je ne suis pas sûr que tous les navigateurs s'en sortent avec ce cas particulier et un .filter('input[@type=text]'). Qu'en pensez-vous?
Bonjour à ceux qui passent par là.

J'aimerais avoir l'avis des gens plus calés que moi en JS/jQuery sur la nouvelle mouture du script (la troisième, tandis que la version citée ci-dessus est la première):

http://covertprestige.info/test/45-champ-formulaire-texte-au-focus.html
(En passant, j'ai enfin rédigé une page lisible pour le script.)

Questions à tout hasard:
- L'utilisation des variables est correcte? Pas de chose affreuse?
- Plus largement, côté respect des bonnes pratiques, ça va?
- Y a-t-il un intérêt à faire un plugin jQuery ici?

Merci d'avance. Smiley smile
Modifié par Florent V. (12 Mar 2009 - 01:39)
Dans ton cas, j'aurais tendance à attribuer les valeurs par défaut aux arguments de la manière suivante:
input_selector = input_selector || 'input.dynvalue'
Benjamin: ah oui, pourquoi pas, c'est plus simple ainsi. La différence c'est qu'avec la syntaxe que tu proposes, si pour l'argument text_to_use on passe une chaine vide cela sera évalué comme false. Mais ce n'est pas un problème car je vois mal pourquoi on utiliserait cette fonction pour ne pas afficher un texte... (Au pire on peut passer une chaine contenant une ou plusieurs espaces.)

En fait, plutôt que de tester que le paramètre est true ou !undefined, je devrais dans l'idéal tester le type avec typeof:
if (typeof mon_parametre !== string) mon_parametre = 'valeur par défaut';

Mais dans le cas présent c'est un peu overkill.
Florent V. a écrit :
Benjamin: ah oui, pourquoi pas, c'est plus simple ainsi. La différence c'est qu'avec la syntaxe que tu proposes, si pour l'argument text_to_use on passe une chaine vide cela sera évalué comme false. Mais ce n'est pas un problème car je vois mal pourquoi on utiliserait cette fonction pour ne pas afficher un texte... (Au pire on peut passer une chaine contenant une ou plusieurs espaces.)

Oui c'est pour ça que je précise que ça me semble adapté dans ce cas précis. Sinon effectivement, c'est périlleux dans certains cas (par exemple si je passe false ou 0).
Bonjour,

Pour répondre à ta question, je dirais intéressant et utile !

Une petite chose dans ta dernière version ci-dessus, la syntaxe 'input[@type=text]' n'est pas compatible avec jQuery 1.3+ (il faut enlever le @).

En outre, je me suis permis de modifier un peu le passage d'argument sur ton code :

(function($) {
	// Arguments par défaut
	jQuery.fn.inputdynvalue.defaults = {
		htext: 'title',
		hclass: 'hint'
	};
	
	$.fn.inputdynvalue = function (options) {
		var opts = $.extend({}, $.fn.inputdynvalue.defaults, options);
		
		return this.each(function(){
			// Initialisation de l'INPUT (attributs value, class)
			var hvalue = opts.htext;
			switch (opts.htext) {
				case 'title' : hvalue = $(this).attr('title'); break;
				case 'value' : hvalue = $(this).attr('value'); break;
			}
			$(this).attr('value', hvalue).addClass(opts.hclass)
	
			// Remise à zéro des gestionnaires d'évènement
			.unbind('focus.dynvalue blur.dynvalue')
	
			// Ajout et suppression du texte au focus ou à la perte de focus
			.bind('focus.dynvalue', function() {
				if (this.value === hvalue) {
					this.value = '';
					$(this).removeClass(opts.hclass);
				}
			})
			.bind('blur.dynvalue', function() {
				if (this.value === '') {
					this.value = hvalue;
					$(this).addClass(opts.hclass);
				}
			});
		});
	};
})(jQuery);


évidemment cela modifie la manière de passer les arguments lors de l'appel à la fonction.


dunjl
Pour le changement sur le sélecteur d'attribut entre jQuery 1.2 et 1.3, je l'avais déjà noté, et j'avais même placé une note à ce sujet dans les explications. Mais je préfère ne pas surcharger la page avec ce type de considération, et donner des exemples d'usage qui n'utilisent pas ce sélecteur.
(Ce n'est pas encore le cas mais c'est un brouillon. Smiley cligne )

dunjl, je ne connais pas le mécanisme que tu utilises pour les options. Quels sont ses avantages?

Par ailleurs, pourquoi avoir utilisé une fonction anonyme ici?

Dernière remarque: la doc de jQuery sur la création de plugins recommande de ne pas utiliser $ mais toujours jQuery dans les plugins, ce qui permet à l'utilisateur de redéfinir l'alias $ sans souci.
Florent V. a écrit :
Par ailleurs, pourquoi avoir utilisé une fonction anonyme ici?


Un petit tuto sur la réalisation de plugin jquery, il date de 2007 mais je suppose qu'il est encore bon. Smiley cligne

EDIT Voir la section : Keep private functions private.

Et dans la doc officielle :

a écrit :
Ok, now to the fun part:

(function($) {
  // plugin code here, use $ as much as you like
})(jQuery);


We pass "jQuery" to the function and can now use whatever alias for jQuery we like. So instead of "$" you could also use any other valid JavaScript variable name.

Modifié par Patidou (12 Mar 2009 - 17:53)
a écrit :
dunjl, je ne connais pas le mécanisme que tu utilises pour les options. Quels sont ses avantages?

Par ailleurs, pourquoi avoir utilisé une fonction anonyme ici?

Je voulais expliquer tout ceci mais lorsque j'ai rédigé le post, j'ai été contraint de le publier tel quel.

Je vais donc reprendre :

Pour les options, cela permet simplement de passer les options en un seul argument. Dans le cas où tu (ou d'autres personnes) ajouterais des options à ton plug-in pour une évolution éventuelle, cela est plus aisé à utiliser (l'ordre et le nombre d'argument sont sans importance).

En outre, cela correspond à la manière dont sont gérées les options dans la plupart des plug-ins jQuery (cf Options dans la doc jQuery.

Enfin, mais ce n'est peut-être pas applicable à ton plug-in, cela permet à la personne qui va utiliser le plug-in de définir facilement ses propres valeurs par défaut et ceci en dehors du plug-in sans modifier le code interne, par exemple :

jQuery.fn.inputdynvalue.defaults = {htext: 'value', hclass: 'tip'};


Pour l'utilisation de la fonction anonyme, c'est plus une habitude (voir custom alias in plugin code) et cela permet d'utiliser $ dans le plugin, mais ta manière est correcte également.

Je remets le code car j'avais mis les valeurs par défaut au mauvais endroit:

(function($) {
	$.fn.inputdynvalue = function (options) {
		var opts = $.extend({}, $.fn.inputdynvalue.defaults, options);		

		return this.each(function(){
			// Initialisation de l'INPUT (attributs value, class)
			var hvalue = opts.htext;
			switch (opts.htext) {
				case 'title' : hvalue = $(this).attr('title'); break;
				case 'value' : hvalue = $(this).attr('value'); break;
			}
			$(this).attr('value', hvalue).addClass(opts.hclass)

			// Remise à zéro des gestionnaires d'évènement
			.unbind('focus.dynvalue blur.dynvalue')	

			// Ajout et suppression du texte au focus ou à la perte de focus
			.bind('focus.dynvalue', function() {
				if (this.value === hvalue) {
					this.value = '';
					$(this).removeClass(opts.hclass);
				}
			})
			.bind('blur.dynvalue', function() {
				if (this.value === '') {
					this.value = hvalue;
					$(this).addClass(opts.hclass);
				}
			});
		});
	};
	// Arguments par défaut
	$.fn.inputdynvalue.defaults = {
		htext: 'title',
		hclass: 'hint'
	};
})(jQuery);


<edit> Quelques sources (ça peut être utile à d'autres):
Plugins/Authoring dans la documentation jQuery
A Plugin Development Pattern (déjà posté par Patidou)
The Basics of Writing a jQuery Plugin
jQuery Plugin Tutorial
</edit>
Modifié par dunjl (12 Mar 2009 - 20:25)
Pages :