11496 sujets

JavaScript, DOM et API Web HTML5

Soit un menu de 6 liens.
Comment modifier la caractéristique de tous ces liens-enfants en une seule fois avec javascript+DOM ?
De la même façon que le CSS définit ces caractéristiques à tous les enfants choisis d'un sélecteur ?
ex : #monMenu a{color: #111}

1) Je peux modifier le style de chaque lien avec une BOUCLE
listeLiens = getElementsByTagName() 
puis while()

2) Je peux modifier la CLASSE du sélecteur-parent en une classe prédéfinie
monMenu.setAttribute ('class', 'autreClass');  ou  monMenu.className = 'autreClass';

3) Mais est-il possible de modifier le style d'un parent de façon à ce qu'il répercute ce style à ses enfants ?
Un truc genre :
monMenu.setAttribute ('style', ' a{color: #111} ');

Modifié par Gill (18 Feb 2014 - 18:40)
Cela n'existe pas en JS natif. Tu ne peux pas trop comparer tout ça aux sélecteurs CSS vu que c'est une syntaxe interprétée (même si c'est pareil pour JS) mais les sélecteurs sont là pour nous simplifier la tâche.
Tu devras malgré tout passer par une boucle mais il existe une méthode qui te facilitera grandement la chose "querySelector" ou "querySelectorAll".


var els = document.querySelectorAll("#monMenu a"),
	i = 0,
	l = els.length;

for ( ; i<l; i++){
	els[i].style.color = "#111";
}
[/i]
Merci pour cette info ! Smiley smile
Je ne connaissais pas "querySelectorAll". AlsaCreations m'en a appris un peu plus :
http://www.alsacreations.com/article/lire/1445-dom-queryselector-queryselectorall-selectors-api.html

J'utiliserai donc désormais ma petite fonction :
//ex : setChildsStyle("#monMenu a", "color", "green")
function setChildsStyle(selCSS, attrCSS, valCSS){
	var els = document.querySelectorAll(selCSS);
	var len = els.length;
	for (var i=0,oldStyle; i<len; i++){
		oldStyle = els[ i ].getAttribute('style');
		els[ i ].setAttribute('style', ((oldStyle)?oldStyle+'; ':'') + attrCSS+':'+valCSS);
	}
}

Et tant pis s'il y a quand même une boucle Smiley decu

(groumpf ! attention aux els[ i ] avec le BBcode, qui prend ça pour une italique ! Faut mettre des espaces Smiley fache )
Modifié par Gill (18 Feb 2014 - 18:49)
Je ne vois pas le problème avec l'utilisation d'une boucle mais si ça te gène tant que ça (faudra m'expliquer pourquoi) tu peux toujours ajouter/enlever une classe sur le parent.


.une-classe a {
color: #111;
}


Et tu fais ta fonction toggleClass() en js pour ajouter/enlever .une-classe sur ton élément.
jb_gfx a écrit :
Je ne vois pas le problème avec l'utilisation d'une boucle mais si ça te gène tant que ça (faudra m'expliquer pourquoi)
Oh ! C'est juste une question d'élégance Smiley smile ! Les fonctions du DOM sont sensées prendre en charge l'arbre des éléments avec efficacité. Le CSS gère la notion d'héritage avec brio. Javascript, langage objet, devrait donc logiquement comprendre et gérer parfaitement l'héritage du DOM.
Je vois qu'il n'en est rien...
Et puis une boucle "humaine" est certainement plus longue à traiter qu'une propagation dans le DOM...
jb_gfx a écrit :
tu peux toujours ajouter/enlever une classe sur le parent.
une-classe a { color: #111; }
Et tu fais ta fonction toggleClass() en js pour ajouter/enlever .une-classe sur ton élément.
Je sais. C'est le 2ème exemple que je citais dans mon premier post.

Maintenant, supposons qu'au moment de créer ma feuille CSS (et ses sélecteurs interchangeables), je ne connaisse pas les valeurs à y appliquer ? Exemple :
#selecteurRemplacant  div{
   height: "quelque chose entre 100px et 900px selon l'état défini par l'user";
}
...comment je fais ?

Au moins, avec ma petite fonction, ça fonctionne :
var hauteur = div1.offsetHeight;
setChildsStyle("#monMenu div", "height", hauteur+'px');

Modifié par Gill (18 Feb 2014 - 22:01)
Gill a écrit :
Oh ! C'est juste une question d'élégance Smiley smile ! Les fonctions du DOM sont sensées prendre en charge l'arbre des éléments avec efficacité. Le CSS gère la notion d'héritage avec brio. Javascript, langage objet, devrait donc logiquement comprendre et gérer parfaitement l'héritage du DOM.
Je vois qu'il n'en est rien...


Tu sais que sous le capeau le moteur CSS va quand même réaliser une boucle sur tes éléments pour leur appliquer les styles ou classes ?

Gill a écrit :

Et puis une boucle &quot;humaine&quot; est certainement plus longue à traiter qu'une propagation dans le DOM...


Ça doit se chiffrer en micro-secondes. Smiley cligne

Gill a écrit :

Maintenant, supposons qu'au moment de créer ma feuille CSS (et ses sélecteurs interchangeables), je ne connaisse pas les valeurs à y appliquer ? Exemple :
#selecteurRemplacant  div{
   height: &quot;quelque chose entre 100px et 900px selon l'état défini par l'user&quot;;
}
...comment je fais ?

Au moins, avec ma petite fonction, ça fonctionne :
var hauteur = div1.offsetHeight;
setChildsStyle(&quot;#monMenu div&quot;, &quot;height&quot;, hauteur+'px');


Ouais en effet, si tu ne connais pas les valeurs à l'avance tu ne vas pas avoir le choix.
Modifié par jb_gfx (19 Feb 2014 - 07:57)
Gill a écrit :
Le CSS gère la notion d'héritage avec brio. Javascript, langage objet, devrait donc logiquement comprendre et gérer parfaitement l'héritage du DOM.

Je crois que beaucoup de choses sont mélangés. L'héritage CSS et l'héritage en JS n'a rien à voir.
L'héritage en CSS se résume à transmettre la valeur de certaines propriétés à tous ces enfants.
L'héritage en JS sera une toute autre chose. Le JS n'est pas censé gérer l'héritage CSS. Par ailleurs, si tu appliques une propriété CSS qui influe les valeurs de ces enfants (comme "line-height", "white-space", "text-align", etc), cela s'appliquera forcément à ces descendants mais ce n'est pas le JS qui impose cela, juste les postulats du CSS.
La fonction que tu as crée peut être franchement simplifiée
function setChildsStyle(selCSS, valCSS, valCSS){
	var els = document.querySelectorAll(selCSS),
		i = 0,
		l = els.length;

	for (; i<len; i++) els[i].style[valCSS] = valCSS;
}

Tu n'as pas besoin de passer par "getAttribute" pour appliquer un style et tu n'as pas besoin non plus de passer les anciens styles si tu ne modifies qu'une propriété.
Il existe une autre méthode de l'objet "Style" qu'est "cssText" qui te permet de passer plusieurs propriétés en une fois. Par contre elle est gérée différemment selon les anciens navigateurs et ceux plus récents.[/i]
Merci Zelalsan de me corriger Smiley confused
Zelalsan a écrit :
Je crois que beaucoup de choses sont mélangés. L'héritage CSS et l'héritage en JS n'a rien à voir.
C'est vrai que je faisais un trèèèès gros parallèle (héritages).
Ce que je voulais dire, c'est qu'il me semble anormal qu'avec Javascript, on ne puisse agir facilement sur une multitude d'éléments enfants, comme avec le CSS, sans une bidouille bien connue mais illogique (changer la classe). Si cette bidouille est si populaire, c'est qu'elle correspond à un besoin !
Zelalsan a écrit :
Tu n'as pas besoin de passer par "getAttribute"; pour appliquer un style et tu n'as pas besoin non plus de passer les anciens styles si tu ne modifies qu'une propriété.
Smiley confused Effectivement... Je savais bien qu'il existait cette structure indexée, mais je ne la retrouvais pas dans les docs et suis passé par getAttribute(). Et des tests buggés m'avaient convaincus à tort que je devais gérer les anciens styles... Smiley confused
Zelalsan a écrit :
Il existe une autre méthode de l'objet "Style" qu'est "cssText" qui te permet de passer plusieurs propriétés en une fois. Par contre elle est gérée différemment selon les anciens navigateurs et ceux plus récents.
Donc :
- cssText est utile pour gérer plusieurs propriétés d'un seul élément.
- mon setCSSStyle() est utile pour gérer une seule propriété de plusieurs sélecteurs et leurs enfants
function setCSSStyle(selCSS, attrCSS, valCSS){
	var els = document.querySelectorAll(selCSS),
		i = 0,
		l = els.length;
	for (; i<l; i++) els[ i ].style[attrCSS] = valCSS;
}
(version corrigée : l<>len, attrCSS<>valCSS, puis els[ i ] devenu invisible car BBcode italique)
Je peux désormais faire des trucs magiques comme :
setCSSStyle('#menudd a, #menudd button, #menudd table', 'height', '40px');
setCSSStyle('#menudd', 'height', hauteur+'px');

PS : pourquoi donc extraire var i de for() ? On m'a toujours dit qu'il fallait déclarer autant que possible les variables dans le contexte le plus réduit possible... Tu sembles préférer limiter au maximum les déclarations (un seul "var")...
Modifié par Gill (19 Feb 2014 - 19:42)
C'est exactement ce que tu dis.
Merci d'avoir corrigé le code je t'ai retourné j'ai un peu fait vite. D'ailleurs en le revoyant, on peut faire encore plus simple en ne passant que deux paramètres, le(s) sélecteur(s) et le style

function setCSSStyle(selCSS, css){
	var els = document.querySelectorAll(selCSS),
		i = 0,
		l = els.length;
	for (; i<l; i++) els[ i ].style[css.split(":")[0]] = css.split(":")[1];
}

Tu pourras faire encore plus magique Smiley langue

setCSSStyle('#menudd a, #menudd button, #menudd table', 'height:40px');

Tu pourrais même mettre les "css.split" en variable avant la boucle pour que ça soit un peu mieux.

Pour les déclarations de variables chacun fait ce qu'il veut c'est de tout façon exactement la même chose. Je préfère noté comme ça parce que c'est plu clair pou moi mais il m'arrive aussi de déclarer les variables dans le "for". Ça ne changera absolument rien.
a écrit :

els[ i ].style[css.split(":")[0]] = css.split(":")[1];


Avec ce code, attention au fait que les noms de propriétés composés ne s'écrivent pas de la même manière en CSS et en JavaScript. P.ex. background-color => backgroundColor.

Pour y remédier, remplacez
css.split(":")[0]

par quelque chose comme ceci :
css.split(":")[0].replace(/-\w/, function(m){ return m[0].toUpperCase(); })

(code non testé)
Exact bien vu QuentinC mais il faudrait remplacer l'ensemble des "-" et pas seulement un (ex: "border-top-width"). Je pense qu'on peut faire plus simple

function setCSSStyle(selCSS, css){
	var els = document.querySelectorAll(selCSS),
		prop = css.split(":")[0],
		dash = /-\w/g,
		i = 0,
		l = els.length;
	
	while(prop.match(dash)){
		prop = prop.replace(prop.match(dash)[0], prop.match(dash)[0][1].toUpperCase());
	}
	
	for (; i<l; i++) els[ i ].style[ prop ] = css.split(":")[1];
}

On pourrait aussi passer par "exec"
Modifié par Zelalsan (21 Feb 2014 - 08:25)
a écrit :
Exact bien vu QuentinC mais il faudrait remplacer l'ensemble des "-" et pas seulement un (ex: "border-top-width"). Je pense qu'on peut faire plus simple

La fonction replace remplace bien toutes les occurences, j'ai juste oublié l'option g.

Je viens de tester ce code et il fonctionne :

"border-top-width".replace(/-(\w)/g, function(_,m){ return m.toUpperCase(); });


Vous noterez aussi que je lève une autre petite confusion que j'ai fait dans mon code précédent: les différents éléments matchés sont envoyés à la fonction en tant qu'arguments individuels, pas en tant que tableau (je confondais avec preg_replace_callback en php)
Modifié par QuentinC (20 Feb 2014 - 22:54)
Oui, le pire c'est que je suis sur un script depuis quelques temps et j'ai fait exactement la même chose chez moi et je ne sais même pas pourquoi j'ai dit que c'est plus simple de passer par un "while" vu que ça fait exactement pareil mais en plus long.
D'ailleurs j'ai écris un peu trop vite et un peu n'importe quoi. J'ai édité mon précédent post donc.
En effet tu avais juste oublié le "g" et la parenthèse capturante.
Hahaha ! Je vois que vous vous amusez bien !

1) J'avais moi aussi hésité à réunir attrCSS et varCSS en un css unique, et puis je me suis dit que l'utilisateur de la fonction préférerait indiquer des variables déjà définies dans son programme, sans avoir à les concaténer pour les mettre en paramètres... dans une fonction qui devra ensuite les re-spliter Smiley lol !
Exemple :
var hauteur = 400 / nbBoutons;
setStyleCSS('#menudd button', 'height:'+hauteur+'px');
vs
setStyleCSS('#menudd button',  'height',  hauteur+'px');
Moui... La différence est bien minime (en externe) Smiley ohwell
- soit on considère que les paramètres doivent rester du full-CSS (pour éviter de se mélanger les pinceaux)
- soit on préfère réserver le code CSS au premier paramètre (et au second aussi...), pour rester le plus possible dans une optique javascript.
(bizarre, plus j'y réfléchis, plus je me dis que c'est vous qui avez raison : full CSS)

2) Mais justement, si on reste dans du full CSS, le programmeur pas trop bête ne pensera pas à mettre des structures javascript dans du code CSS (pas de minHeight pour du min-height) !
a écrit :
1) J'avais moi aussi hésité à réunir attrCSS et varCSS en un css unique, et puis je me suis dit que l'utilisateur de la fonction préférerait indiquer des variables déjà définies dans son programme, sans avoir à les concaténer pour les mettre en paramètres... dans une fonction qui devra ensuite les re-spliter

C'est un point de vue, je pense que les deux se valent. Techniquement c'est effectivement crétin de concaténer des chaînes pour les re-split ensuite, mais dans les faits c'est pas ça qui va faire beaucoup ramer la machine, sauf si tu le fais souvent ou avec de nombreuses propriétés.
Donc, vaut mieux choisir l'option qui met le plus à l'aise les utilisateurs de la fonction, tout simplement.

a écrit :
2) Mais justement, si on reste dans du full CSS, le programmeur pas trop bête ne pensera pas à mettre des structures javascript dans du code CSS (pas de minHeight pour du min-height) !

C'est pour ça qu'il faut faire la conversion au cas où. Dans les faits, avec la dernière fonction proposée, les deux versions minHeight ou min-height passeront. C'est toujours une question de moins que le programmeur fainéant devra se poser.
QuentinC a écrit :
Donc, vaut mieux choisir l'option qui met le plus à l'aise les utilisateurs de la fonction, tout simplement.
toutafé Smiley smile
QuentinC a écrit :
avec la dernière fonction proposée, les deux versions minHeight ou min-height passeront. C'est toujours une question de moins que le programmeur fainéant devra se poser.
Là aussi, tu as raison : sécurité, sécurité.
Mais tout de même, un truc comme ça, ça ferait grincer des dents Smiley biggol :
setStyleCSS('#menudd button', 'minHeight : 200px');
Quoiqu'il en soit, encore merci à tous les deux pour vos réflexions et ajouts !
Modifié par Gill (21 Feb 2014 - 17:12)