11548 sujets

JavaScript, DOM et API Web HTML5

j'ai un élément de type input <input /> dans une page HTML. Je souhaite dynamiquement ajouter un wrapper à cet élément.

ce qui donne

<input /> -> <div><input /></div>

Ce qui signifie que <input /> devient un enfant de <div>

Il est facile de créer un élement dynaniquement avec document.createElement mais je bute sur comment changer dynamiquement l'aborescence (hiérarchie) du DOM
En utilisant les méthodes de l'objet node du DOM.
Notamment appendChild, insertBefore, cloneNode etc

edit : je n'avais pas eu le temps de détailler tout à l'heure, c'est chose faite :

Dans ce cas précis, la technique la plus "lisible" va consister à créer l'élément div, cloner l'élément input, remplacer l'input original par le "wrapper" (c'est quoi au juste un wrapper d'ailleurs ? ) et enfin insérer le clone de l'input dans l'élément div.

Comme pour toutes les manipulations de l'objet node, il existe plusieurs manières de faire les même traitements (l'implémentation de l'objet node est à ce sujet très souple et très riche) soit en naviguant dans la structure de l'arbre soit en utilisant des positions de référence parent-enfant valides et utilisables.

Pour le remplacement il te faudras disposer d'une référence valide et utilisable à l'un des parents de l'élément input.

Une représentation de l'arbre du DOM te sera donc indispensable pour travailler avec l'objet node et en la matière le dom inspector de mozilla sera ton ami Smiley smile

Imaginons donc une structure simple :


<form id="form" ...>
   <fieldset id="fieldset">
      <legend>mon formulaire</legend>
        <input id="input" ... />
   </fieldset>
</form>


La référence valide sera donc le fieldset, la cible sera adressée d'une part par l'id 'input' pour le clonage et d'autre part par l'id 'fieldset" pour le remplacement de l'input original.

L'élément div nouvellement créé n'à pas besoin d'avoir un id tant qu'il est un objet en cours d'utilisation par le traitement EcmaScript.

Néanmoins si tu à besoin de lui affecter un id ou une référence de classe CSS tu devras utiliser la méthode setAttribute (attribut, valeur) au moment de sa création.

Ton traitement se résume donc à quelquechose comme :

  function createWrapper(){
    //On cree
    newDiv=document.createElement("div");
    //On clone
    newInput=document.getElementById('input').cloneNode(true);
    //On remplace
document.getElementById("fieldset").replaceChild(newDiv,document.getElementById('input'));
//On insere    
newDiv.appendChild(newInput);
    //On affecte (si besoin)
    newDiv.setAttribute('id','idDiv');
    newDiv.setAttribute('class','nomClasse');
  }


Cet exemple est très décomposé, il est sans doute possible de faire la même chose de manière plus concise (je n'ai pas cherché) mais pour débuter avec l'objet node et la navigation dans l'arbre du dom tu à intérêt à décomposer et optimiser ensuite faute de quoi tu seras vite comme Thésée dans le labyrinthe.

L'exemple ci-dessus est fonctionnel car on utilise des id, la même opération sans le recours à des id en se fondant uniquement sur l'arbre lui-même demanderais certainement de devoir garantir que l'élément traité lors du remplacement soit bien l'input cible, compte tenu du statut particulier des retours chariots insérés dans le code (pas les br mais les "retour à ligne" du code) que notre ami IE refuse obstinément de considérer alors même qu'ils sont des nodes valides (pourquoi tant de haine bilou ?).

A noter enfin que le paramètre true de la méthode cloneNode permets de cloner l'élément avec sa descendance, il faut utiliser false pour cloner un exemplaire vide.
En revanche les attributs natifs (id, class...) seront systématiquement clonés.

L'objet node est l'élément central du DOM, sa plus petite expression, et ses méthodes sont extrèmement riches et offrent bien souvent des alternatives de traitement et de manipulation des objets d'une rare élégance.

En revanche sa syntaxe est un peut verbeuse et demande un peut d'expérience mais y gouter c'est l'adopter Smiley cligne

Pour l'objet node, l'essentiel est ici : Self HTML - l'objet Node

JP

PS: A propos des "retours à la ligne" inséré dans le code, je ne suis jamais parvenus à comprendre si il s'agissait d'un élément des spécifications de l'API du DOM EcmaScript ou un comportement lié au navigateur.
Le fait est que le dom inspector les valide en tant qu'élément #text et qu'apparemment seul IE refuse de les prendre en compte.
Les éléments impactés par ce "bug" (???) d'IE sont les propriétés du genre nextSibling ou les méthodes du genre childNodes[n] qui donnent des résultats différents IE/Reste du monde fonction de ces fameux "retour à la ligne" du code.
Si Gilles auteur d'un remarquable cours sur le DOM traine dans le coin, ou un autre afficionado (bobe ?) se serait gentil d'éclairer ma lanterne à ce sujet.
Ca me permettrais de pas mourir idiot et d'avoir enfin une raison vraiment irréfutable de proposer à bilou une gentille propriété dans un node perdus au find fond de l'hyper-espace Smiley smile
Merci d'avance...
Modifié par jpv (26 May 2005 - 04:23)
Je te remercie, j'avais moi-même pensé à une méthode similaire mais je me demande toujours s'il existe une fonction du DOM permettant de changer la hiérarchie d'un objet créé statiquement dans la page (présent dans le fichier source HTML).

La méthode que j'emploie (fichier .htc) est :

wrapper		= doc.createElement("DIV");
				wrapper.className	= 'input-wrapper';
				
				element.insertAdjacentElement("beforeBegin",wrapper);
				clone 			= element.cloneNode(true);		// attention clonage partiel
				clone.rounded		= false;				// évite d'enclencher un processus récursif d'appel de ie-input.htc
			
				wrapper.appendChild(clone);
				element.parentNode.removeChild(element);			// on détruit l'original


element étant l'élément à emballer (wrapper)
A ma connaissance non,

Nos deux méthodes sont identiques à l'exception de la méthode insertAdjacentElement() qui n'est pas une spécification standard Ecmascript, mais une méthode Js/Microsoft.

En conclusion ta méthode ne fonctionne hélas que sous IE, je viens de tester sous mozilla qui me renvois "not a function...".

Tu gagnerais vraiment à utiliser replaceChild, ou insertBefore qui est plus proche de ta conception et qui assurera la portabilité de ton behavior.

JP
Modifié par jpv (26 May 2005 - 17:32)
il est vrai que insertAdjacentElement() est une méthode microsoft. D'un autre côté, les .htc aussi.

On peut aussi facilement émuler insertAdjacentElement() dans mozilla
Ben oui, mais comme les méthodes standards font la même chose et marche partout pourquoi s'en priver ? Smiley smile

Ceci dit, renseignements pris je n'ai pas trouve de méthodes permettant de modifier l'arbre du dom de manière générique.

Et en y repensant d'ailleurs, je n'en vois pas bien l'utilité, l'implémentation de node permettant un controle au plus près de l'arbre, me semble infiniment plus précis qu'une fonction générique.

Au sujet des htc, même si je suis legèrement acide sur microsoft, je fais également partie de ceux qui trouve le concept des behavior très intéréssant comme d'autre chose comme XMLHttpRequest dont je fais une utilisation intensive.

a écrit :
On peut aussi facilement émuler insertAdjacentElement() dans mozilla


Là en revanche je ne comprends pas du tout, pourquoi émuler une fonction propriétaire quand on dispose d'un équivalent standardisé qui fonctionne partout ???

Ceci dit dans un contexte d'exécution de behavior, évidemment le problème ne se pose pas, mais ça doit sans doute t'obliger à utiliser deux codes au lieu d'un "universel"...

Bien sur insertAdjacentElement() et surtout les méthodes associés Text() et HTML() font en une fois ce qui necessite quelques lignes suplémentaires avec node.
On peut même regretter que ces implémentations n'aient pas été choisies.

Mais utiliser insertAdjacentElement() et bricoler un prototype pour émuler le bazar sous Gecko, au final ça me parait bien plus long et compliqué.

C'est la même chose avec XMLHttpRequest vs Load and Save pour DOM3, le jour ou le DOM 3 sera dominant je me poserais la question de laisser XMLHttpRequest de coté et me fader les specs Load and Save.
Quoique pour être parfaitement honnête là quand même je sais pas... Smiley langue

Mais pour inserAdjacent par contre je n'en vois pas l'intérêt...

JP
Modifié par jpv (26 May 2005 - 23:50)
rickman a écrit :
j'ai un élément de type input <input /> dans une page HTML. Je souhaite dynamiquement ajouter un wrapper à cet élément.

ce qui donne

<input /> -> <div><input /></div>

Ce qui signifie que <input /> devient un enfant de <div>

Bonjour,
Je ne sais pas ce qu'est un wrapper, mais je vous propose une solution testée sur IE/Mozilla:
Principe:
Entourer n'imprte quel élément de la page par un élément <div>


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">
<html>
<head>
<title>test</title>
<style type="text/css">
.marq{background:#aee;padding:3px}
}
</style>
<script type="text/javascript">
<!--
D=document;gk=window.Event?1:0;

function Wrap(e){
el=gk?e.target:event.srcElement;
if(!el.tagName)el=el.parentNode; // noeud #text
if(el.className=="wrapp"){
  Nouveau=D.createElement('div'); 
  Nouveau.className="marq";
  el.parentNode.insertBefore(Nouveau,el);
  Nouveau.appendChild(el)
  }
return false;
}
D.onclick=Wrap
// -->
</script>
</head>

<body>

<form id="form" >
<fieldset>
<legend>mon formulaire</legend>
<input type="text" value="non wrappable"/>
<input type="text" value="wrappable" class="wrapp" />
<p><input type="button" value="Bouton wrappable" class="wrapp" /></p>
</fieldset>
</form>
<p class="wrapp">Paragraphe wrappable</p>
<p>Paragraphe non wrappable</p>

</body>
</html>
Effectivement, cette méthode est meilleure car elle évite d'utiliser un clonage qui semble poser quelques problèmes sous IE et en plus c'est une méthode W3C... alors que demande le peuple....

par ailleurs, il existe un équivant au .htc sur mozilla c'est xbl