11521 sujets

JavaScript, DOM et API Web HTML5

Pages :
(reprise du message précédent)

...

bonsoir à tous !!

Concernant le 'problème ' de onload ( appel unique ) j'avais lu une "méthode"
contournant cette restriction et de mettre en lieu et place du traditionnel

<body onload="foo();">


l'utilisation d'une fonction anonyme (anonymous function) apellant les autres :


window.onload = Function() {
foo();
truc();
chouette();
 }



j'espère que cela peut aider un temps soit peu !
++
Modérateur
oui, enfin... refaisons un petit point... Il va m'être difficile d'être clair mais bon, j'essaye... Smiley murf

Le soucis des gestionnaires d'événement onmachinchose est qu'ils sont uniques pour un élément donné à un instant donné. Si on met un onload, on s'approprie l'action au chargement de la page. En effet, on peut, pour soi, fonctionner ainsi, ça ne posera pas de problème... jusqu'à ce qu'on récupère un script externe... Il faudra alors contourner le problème en adaptant notre code afin de ne lancer toujours qu'un onload via la fonction anonyme... C'est faisable sans trop de difficultés... MAIS, quelqu'un qui ne connait rien au JS et qui est intéressé par ce script dira, ça ne marche pas dès lors qu'il ajoutera un second script se servant du onload... Pour bien faire, il serait préférable de ne pas se servir des onmachinchose... Ces gestionnaires d'événement sont intrusifs et c'est celà qu'il faut éviter, dans la mesure du possible... ( Le code de Jep possède le même problème puisque la fonction start s'approprie le window.onload. )

Dans le tuto, je parle de la fonction suivante :
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;
}
Celle-ci, excepté en dernier recours, ne repose pas sur les gestionnaires d'événement intrusifs et peut servir à de multiples reprises...

Là où on aurait écrit :
window.onload = function
{
   fonction1();
   fonction2();
};

on écrirait maintenant :
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;
}

addEvent(window, 'load', fonction1, false);
addEvent(window, 'load', fonction2, false);
et ainsi de suite...

Pour l'événement 'load', c'est relativement simple puisqu'on n'a qu'à lancer nos fonctions... Pour un événement de type 'click', c'est déjà plus complexe puisque le this dans la fonction associée ne fonctionne plus. Il faut alors passer par target... oui, mais le soucis, c'est, comme je disais précédemment, que chaque navigateur utilise la méthode qui lui est adaptée... (addEventListener, attachEvent,...) ce qui veut donc dire que ça ne va pas fonctionner pareil et qu'il va donc falloir créer une nouvelle fonction pour déterminer notre target... Ca devient vite rude... Smiley ravi Dans un soucis de simplicité, on peut se cantonner à ne se servir de la fonction addEvent que sur l'événement 'load' en sachant que pour les autres événements, nous retrouverons notre contrainte des événements type DOM1 (onload, onclick, etc...).

Pour en revenir au code de Jep... Là où, pour moi, il y a soucis, c'est que la méthode eval() évalue une variable et non un chaîne de texte fixe, ce qui suppose qu'elle peut évaluer tout type de données. Là où ça pêche, c'est lorsqu'on donne la possibilité d'introduire des variables via l'url, ce qui est le cas avec le $_GET du code PHP... Certes, rien ne laisse à supposer qu'il y ait problème vu que le script PHP et le script JS sont indépendants... mais prenons le cas du cracker qui te cherche des noises... Pour lui, il suffit, afin d'introduire un code néfaste, de désactiver Javascript et de rentrer son code via l'url... Il ne passera donc pas par le code JS... Du coup, il faut impérativement vérifier le type de données reçues au sein du code PHP parce qu'il est clair qu'actuellement, on peut entrer n'importe quoi, un header (avec redirection par exemple) mais aussi toute sorte de caractères tels que des guillemets, des apostrophes, des points virgules, des sauts de ligne ou autres, ce qui donne la possibilité d'introduire de nouvelles fonctions...

Sans entrer dans les détails de la syntaxe pour arriver à ce résultat, on peut modifier ta ligne :
$son = $pre_son."&autoplay = 1";

qui en clair s'exprime par :
$son = "/musique/un_son.mp3"."&autoplay = 1";

par un truc du genre :
$son = [#red]monsupercodenefaste;
echo $son;
$null = $nawak[/#]."&autoplay = 1";
en remplaçant les données obtenues par le $_GET... autant dire, un véritable désastre... Smiley confus

Pour t'en protéger, tu peux créer un tableau des données acceptables pour le $_GET et ne lancer la fonction que si ce que tu reçois correspond à tes attentes... Tu mets ainsi un premier barrage nettement plus difficile à contourner.
Idem pour la méthode eval() du code JS... il faut faire un tableau des données acceptables si tu tiens à te servir de la fonction start()... Dans le cas présent, le code JS ne se sert pas des urls donc ça ne doit pas trop poser de problème (je crois Smiley langue ) mais rien ne dit que tu ne vas pas faire un script de ce type un de ces quatres, donc autant prendre les devants... Smiley cligne

Dormez bien les amis... Smiley lol
Modifié par koala64 (05 Nov 2006 - 00:28)
Très bien ton développement sur les évènements. Je suis d'accord pour tout.
koala64 a écrit :
Pour un événement de type 'click', c'est déjà plus complexe puisque le this dans la fonction associée ne fonctionne plus. Il faut alors passer par target...

On peut aussi modifier la fonction en remplaçant this par l'objet qu'il représente :

big_photo.onclick=function(){this.style.display='none';}
// devient
addEvent(big_photo,"click",function(){big_photo.style.display='none';},false)

Mais ce n'est pas non plus simple pour un débutant.
Modérateur
chmel a écrit :
Mais ce n'est pas non plus simple pour un débutant.
oui, ça, je te l'accorde, c'est loin d'être inné, qu'on soit débutant ou non, d'ailleurs. Smiley ravi

chmel a écrit :
On peut aussi modifier la fonction en remplaçant this par l'objet qu'il représente :

big_photo.onclick=function(){this.style.display='none';}
// devient
addEvent(big_photo,"click",function(){big_photo.style.display='none';},false)
En effet, on peut bien redéfinir l'élément au sein de la fonction anonyme mais ça perd de son intérêt puisque tu es contraint de constituer un script monobloc avec des méthodes privées, donc non réutilisables...

exemple :

[#blue]// Le code suivant fonctionne...[/#]
function fnInit()
{
   var big_photo = document.getElementById('big_photo');
   addEvent(big_photo,"click",function(){big_photo.style.display='none';},false);
}
[#blue]// Le code suivant ne fonctionne pas...[/#]
function fnInit()
{
   var big_photo = document.getElementById('big_photo');
   addEvent(big_photo,"click", fnDisplay, false);
}
function fnDisplay()
{
   big_photo.style.display='none';
}

Pour que ça marche, tu dois redéclarer var big_photo dans la fonction fnDisplay. On perd ici la référence qu'on pouvait obtenir avec this, ce qui ne nous arrange guère...

Je développe pour ceux que le sujet intéresse...

Tout d'abord, pour bien comprendre comment fonctionne addEvent, j'invite toujours à consulter le tuto... ( Je suis lourd hein ?! Smiley lol ... mais c'est indispensable pour appréhender la suite... Smiley cligne ) Je vais m'efforcer d'être au plus clair malgré la complexité de la chose... Accrochez vos ceintures ! Smiley ravi

Fonction addEvent
function addEvent(oElem, sEvType, fn, bCapture)
{
   return oElem.addEventListener? [#blue]// Si le navigateur comprend la méthode addEventListener[/#]
      oElem.addEventListener(sEvType, fn, bCapture): [#blue]// On l'applique[/#]
      oElem.attachEvent? [#blue]// Sinon, on teste la méthode propriétaire d'IE attachEvent[/#]
         oElem.attachEvent('on' + sEvType, fn): [#blue]// et on l'applique[/#]
         oElem['on' + sEvType] = fn; [#blue]// En dernier recours, on applique la méthode intrusive[/#]
}

La principale notion à acquérir est que la fonction addEvent va jouer avec l'événement (représenté par une variable arbitraire, e, par exemple) alors qu'un element.onclick joue sur element (représenté par le mot clé this).
Cette fonction prévoit trois cas de figure :
* Les navigateurs W3C compliant
* IE
* Les anciens navigateurs
La fonction définie en tant que troisième argument d'addEvent est donc indépendante (elle n'est attachée à aucun élément). Pour que ce soit le cas, on peut passer l'élément en tant qu'argument...

En repartant de notre code précédent, on obtient :
function fnInit()
{
   var big_photo = document.getElementById('big_photo');
   addEvent(big_photo,"click", function() { fnDisplay(big_photo); }, false);
}
function fnDisplay(elem)
{
   elem.style.display='none';
}
C'est tout de suite plus souple puisque la fonction fnDisplay est susceptible de s'appliquer à n'importe quel élément.

Maintenant, pour véritablement faire référence à l'élément d'où provient l'événement, on est censé se servir de la propriété e.target qui fait référence à l'objet source de l'événement. Ca, c'est la méthode W3C. Pour IE, on remplace e.target par window.event.srcElement parce qu'une difficulté ne suffisant pas, IE considère que tout événement dépend de window. (Je sens que j'ai déjà paumé pas mal de monde Smiley lol )

Bref, voici la méthode utilitaire permettant d'obtenir l'équivalent de notre this :

Fonction getTarget
[#blue]// Récupération de la source de l'événement[/#]
function getTarget(e)
{
	var target = window.event ? window.event.srcElement : e ? e.target : this;
	if(!target) return false;
	if(target.nodeName.toLowerCase() != 'a') target = target.parentNode; //Pour Safari
	return target;
}
Cette fonction renvoit window.event.srcElement pour IE, e.target pour les navigateurs modernes et this pour les autres.

On reprend une nouvelle fois notre fonction en appliquant tout cela :
function fnInit()
{
   var big_photo = document.getElementById('big_photo');
   addEvent(big_photo, 'click', fnDisplay, false);
}
function fnDisplay(e)
{
   var elem = getTarget(e);
   elem.style.display='none';
}
Ce n'est pas fini ! Smiley biggol Non... On a parlé du problème du this mais il y en a un autre -> return false; non plus, ça ne marche pas Smiley lol
Il est pourtant nécessaire d'annihiler la transmission de l'url si big_photo se trouve être un lien... et... on a toujours nos 3 cas à traiter... (esprit geek, es-tu là ? Smiley ravi )
Pour les W3C compliant, cela s'effectue via e.stopPropagation(); pour stopper la propagation de l'événement à d'autres éléments et e.preventDefault(); pour stopper la transmission du lien.
Sous IE, ça correspond respectivement à window.event.cancelBubble = true; et window.event.returnValue = false;
Pour les autres, on fait comme d'habitude avec un simple return false;
Une exception -> Safari. Celui-ci comprend la méthode W3C mais ne réagit pas correctement; la transmission de l'url s'effectue quand même, ce pourquoi on ajoute un return false; dans la première condition de la fonction qui sert à gérer tout ça :

fonction cancelClick
[#blue]// Empêche la propagation de l'événement et stoppe la valeur de retour[/#]
function cancelClick(e)
{
	if(e && e.stopPropagation && e.preventDefault) [#blue]// W3C compliant[/#]
	{
		e.stopPropagation();
		e.preventDefault();
		return false; [#blue]// Pour Safari[/#]
	}
	else if(window.event && window.event.cancelBubble && window.event.returnValue) [#blue]// IE[/#]
	{
		window.event.cancelBubble = true;
		window.event.returnValue = false;
		return false; [#blue]// Pour éviter un avertissement[/#]
	}
	else return false; [#blue]// les autres[/#]
}


Pour conclure, je vous laisse un exemple illustrant tout ce qu'on vient de dire...

http://koalnet.com/divers/test-events/

Dans celui-ci, j'affecte deux actions au clic sur le lien en utilisant les méthodes précédentes et j'annihile la transmission de l'url.

Ce qu'il faut avoir à l'esprit, c'est :

* qu'un navigateur W3C compliant interprétera le script correctement,
* qu'IE va bien prendre nos deux actions en compte mais dans l'ordre inverse (parce que c'est IE Smiley lol )
* que les anciens navigateurs n'interpréteront qu'une des deux actions...

On pourrait annihiler la méthode addEvent pour ces derniers en remplaçant oElem['on' + sEvType] = fn; par false; dès lors qu'on souhaiterait affecter plus d'une action mais ça perd tout son intérêt si on se veut compatible avec tout le monde...

C'est pourquoi tout ce que je viens de dire ne sert en gros qu'à votre culture générale et vos soirées tirage de cheveux... Smiley langue Smiley lol Smiley exit
Modifié par koala64 (05 Nov 2006 - 15:39)
.....
Bonsoir !

Ben c'est bien ce que j'ai commencé à faire , et il ne m'en reste déjà pas des masses ...

mais c'est pour la bonne cause , et de plus étant "à l'étude" des fonctions , méthodes et prototype en javascript ( ainsi que la portée des objets et variables, ce qui me semble essentiel --> this pour l'objet global ou objet courant) ...

Alors toutes ces précisions sont du pain béni , meme si je n'ai pas encore tout compris mais je vais re-re-lire à nouveau ...

en un mot : merci
++
Modérateur
Je crois que je vais le mettre dans mes favoris ce sujet... ça m'évitera de tout retaper la prochaine fois... Smiley ravi
Smiley jap bravo, mais que c'est compliqué de remplacer les "onmachinchose". mon cerveau fume Smiley fulmine , je garde ça sous le coude.
bonne nuit
....

ben moi je l'ai déjà imprimer --> la re-lecture -- des cheveux encore en moins !
Smiley bawling

++
Pages :