11548 sujets

JavaScript, DOM et API Web HTML5

Bonjour à tous,

j'ai réalisé un menu à partir de celui disponible à cette adresse :
http://forum.alsacreations.com/topic-5-17759-1-rsolu-Menu-avec-javascript-non-intrusif-simplifi.html

Les modification que j'ai apporté sont les suivantes:
- gestion de plusieurs niveau
- initialisation des menus par leurs attribut "class" et non par leurs attribut "id"

Tout marche très bien.

La dernière étape qu'il me reste à réaliser me cause plus de souci ... Smiley biggol

Je voudrais que lorsque l'utilisateur click sur une rubrique contenant des sous-rubrique, la sous-rubrique qui était ouverte à la même profondeur se referme. De sorte de n'avoir qu'un sous menu d'ouvert à la fois par profondeur.

Un peu comme le menu disponible à cette adresse http://css.alsacreations.com/modelesmenus/vd1.htm
Sauf que j'ai plusieur niveau.

Existe t'il un moyen simple en JavaScript pour calculer la profondeur d'une balise dans une liste?

Voici une illustration de ce que j'entend par profondeur

<ul class="menu">     [#red]<!--profondeur 0-->[/#]
	<li>>Lib 1
		<ul>     [#red]<!--profondeur 1-->[/#]
			<li class="actif"><strong><a href="unLien">Lib 1.0</a></strong></li>
			<li><a href="unLien">Lib 1.1</a></li>
		</ul>
	</li>
	<li>Lib 2
		<ul>     [#red]<!--profondeur 1-->[/#]

			<li><a href="unLien">Lib 2.0</a></li>
			<li><a href="unLien">Lib 2.1</a></li>
			<li>Lib 2.2
				<ul>     [#red]<!--profondeur 2-->[/#]
					<li><a href="unLien">Lib 2.2.0</a></li>
					<li><a href="unLien">Lib 2.2.1</a></li>
				</ul>

			</li>
			<li><a href="unLien">Lib 2.3</a></li>
			<li><a href="unLien">Lib 2.4</a></li>
			<li><a href="unLien">Lib 2.5</a></li>
			<li><a href="unLien">Lib 2.6</a></li>
		</ul>
	</li>

	<li>Lib 3
		<ul>     [#red]<!--profondeur 1-->[/#]
			<li><a href="unLien">Lib 3.0</a></li>
		</ul>
	</li>
</ul>

Modifié par Francois44 (14 Dec 2006 - 13:42)
Ok, après quelques analyses du code j'ai résolu mon problème ...

Dernière étape (pour l'instant ) validé! Smiley cligne

Merci
Modérateur
dac,

De quel type de gestionnaire d'événements te sers-tu ? Est-ce des 'onclick' ou autres ? Le code JS est-il intrusif ou non ? (Apparemment non) Bref, serait-il possible de voir ce que tu as fait ? Smiley smile
Modifié par koala64 (14 Dec 2006 - 14:14)
Ok, je met à disposition mon code ... Etant avant tout dévelopeur PHP, j'espère qu'il ne soit pas trop pourri Smiley sweatdrop

la classe "actif" est utilisé pour spécifier quel rubrique est active dans le menu, si une tel classe est trouvé, le menu est ouvert sur cette rubrique par défaut, sinon, toutes les rubriques du menu sont fermé.

Pour le moment, il y a 2 type de menu possible:
- menu dont les rubrique s'ouvre (ou se ferme) lors d'un click. (class="menu_click")
- menu dont les rubrique s'ouvre (ou se ferme) lors d'un click et qui ferme automatiquement les autres rubriques de la même profondeur. (class="menu_click_unique")


Dans un premier temps, voici mon fichier comp_menu.js:
Il s'agit en fait d'une classe en singleton avec laquelle je cré mes menus

function MxpComp_Menu()
{
	obj_Pointeur = this.constructor;
     
	if(obj_Pointeur.obj_Instance == undefined)
	{
		obj_Pointeur.obj_Instance = this;
		
		/*------------------------------------------------------------
		PROPRIETES
		------------------------------------------------------------*/
		  
		/*------------------------------------------------------------
		METHODES PRIVEES
		------------------------------------------------------------*/
		
		/**
		*exec_AffichageMasquage
		*Affichage ou masquage d'un menu.
		*
		*@param     object[Evenement]
		*
		*@return     boolean
		*/
		obj_Pointeur.obj_Instance.exec_AffichageMasquage = function(obj_Evenement)
		{
			var oTarget = Bib_Elements.get_Cible(obj_Evenement);
			var oList = oTarget.parentNode.getElementsByTagName('ul')[0];
			var oA = oTarget.parentNode.getElementsByTagName('a')[0];
			
			if(oList.style.display == 'none')
			{
				oA.className = 'menu_on';
				oList.style.display = 'list-item';
			}
			else
			{
				oA.className = 'menu_off';
				oList.style.display = 'none';
			}
		};
		
		/**
		*exec_AffichageMasquageUnique
		*Affichage ou masquage d'un menu en s'assurant qu'un seul des menus soit visible par profondeur.
		*
		*@param     object[Evenement]
		*
		*@return     boolean
		*/
		obj_Pointeur.obj_Instance.exec_AffichageMasquageUnique = function(obj_Evenement)
		{
			var oTarget = Bib_Elements.get_Cible(obj_Evenement);
			var oList = oTarget.parentNode.getElementsByTagName('ul')[0];
			var oA = oTarget.parentNode.getElementsByTagName('a')[0];
			
			if(oList.style.display == 'none')
			{
				oA.className = 'menu_on';
				oList.style.display = 'list-item';
			}
			else
			{
				oA.className = 'menu_off';
				oList.style.display = 'none';
			}
			
			var lis = oTarget;
			while(lis.nodeName.toLowerCase() != 'ul' && lis)
			{
				lis = lis.parentNode;
			}
			
			var array_Ul = lis.getElementsByTagName('ul');
			var int_NbUl = array_Ul.length - 1;
			for(int_NbUl; int_NbUl >= 0; int_NbUl--)
			{
				if(array_Ul[int_NbUl] != oList)
				{
					if(array_Ul[int_NbUl].parentNode.nodeName.toLowerCase() == 'li')
						array_Ul[int_NbUl].parentNode.getElementsByTagName('a')[0].className = 'menu_off';
					array_Ul[int_NbUl].style.display = 'none';
				}
			}
		};
		
		/*------------------------------------------------------------
		METHODES PUBLIQUES
		------------------------------------------------------------*/
		
		/**
		*check_Compatible
		*Spécifit si le navigateur du client est compatible ou non avec le code de la classe.
		*
		*@return     boolean
		*/
		obj_Pointeur.obj_Instance.check_Compatible = function()
		{
			//liste des propriétés et méthodes requisent
			var array_Requis = new Array(
				document.getElementById,
				document.getElementsByTagName,
				document.createElement,
				document.createTextNode,
				Bib_Elements.get_ElementsByClassName,
				Bib_Elements.get_Cible,
				Bib_Elements.set_Action,
				Bib_Elements.unset_Action
				);
			
			var int_NbRequis = array_Requis.length - 1;
			for(var int_I = 0; int_I<int_NbRequis; int_I++)
			{
				if(!array_Requis[int_I])
					return false;
			}
			return true;
		};
		
		/**
		*exec_Initialisation
		*Execute l'initialisation d'un menu.
		*
		*@param     object[element HTML]
		*
		*@return     void
		*/
		obj_Pointeur.obj_Instance.exec_Initialisation = function(obj_Menu)
		{
			if(!this.check_Compatible())
				return;
		
            if(!obj_Menu)
				return;
			
			var array_Ul = obj_Menu.getElementsByTagName('ul');
			var int_NbUl = array_Ul.length - 1;
			for(int_NbUl; int_NbUl >= 0; int_NbUl--)
			{
				var obj_LiTemp = array_Ul[int_NbUl].parentNode;
				if(!obj_LiTemp)
					continue;
			
				if(obj_LiTemp.nodeName.toLowerCase() != 'li')
					continue
				
				var str_Libelle = obj_LiTemp.firstChild.nodeValue;
				if(str_Libelle == '' || str_Libelle == null)
					continue;

				//masquage par défaut
				array_Ul[int_NbUl].style.display = 'none';
			
				//création des liens "fictif" pour y associer les évenements (on pourrait faire sans lien, mais cela rendrait la rubrique inacessible au clavier) 
				var obj_ATemp = document.createElement('a');
				var obj_LibelleA = document.createTextNode(str_Libelle);
				obj_ATemp.setAttribute('href', '#ouverture-fermeture-Menu');
				obj_ATemp.setAttribute('class', 'menu_off');
				obj_ATemp.appendChild(obj_LibelleA);
				obj_LiTemp.replaceChild(obj_ATemp, obj_LiTemp.firstChild);
				
				//affectation des évenements
				if(obj_Menu.className == 'menu_click')
				{
					Bib_Elements.set_Action(obj_ATemp, 'click', this.exec_AffichageMasquage, false);
				}
				else if(obj_Menu.className == 'menu_click_unique')
				{
					Bib_Elements.set_Action(obj_ATemp, 'click', this.exec_AffichageMasquageUnique, false);
				}
			}
			
			//on rend visible l'item actif
			if(obj_Menu.className == 'menu_click' || obj_Menu.className == 'menu_click_unique')
			{
				var array_Menu = Bib_Elements.get_ElementsByClassName(obj_Menu, 'li', 'actif');
				var int_NbMenu = array_Menu.length;
				if(int_NbMenu>0)
				{
					var obj_Parent = array_Menu[0].parentNode;
					while(obj_Parent != obj_Menu)
					{
						if(obj_Parent.nodeName.toLowerCase() == 'ul')
						{
							obj_Parent.style.display = 'list-item';
						
							var obj_A = obj_Parent.parentNode.getElementsByTagName('a')[0]
							obj_A.className = 'menu_on';
						}
						
						obj_Parent = obj_Parent.parentNode;
					}
				}
			}
        };
		
		/**
		*exec_InitialisationGlobale
		*Execute l'initialisation des menus du document.
		*
		*@return     void
		*/
		obj_Pointeur.obj_Instance.exec_InitialisationGlobale = function()
		{
			if(!this.check_Compatible())
				return;

			var array_Menu = Bib_Elements.get_ElementsByClassName(document, 'ul', ['menu_click', 'menu_click_unique']);
			var int_NbMenu = array_Menu.length;
			for(var int_I=0; int_I<int_NbMenu; int_I++)
			{
				this.exec_Initialisation(array_Menu[int_I]);
			}
        };
	}          
	else
	{
		return obj_Pointeur.obj_Instance;
	}
}
if(Mxp_Init.set_NouvelleFonction)
	Mxp_Init.set_NouvelleFonction(function(){var obj = new MxpComp_Menu();obj.exec_InitialisationGlobale();});


Ensuite, j'ai une bibliothèque sur les éléments dont je me sert dans plusieurs code (bib_elements.js)

var Bib_Elements =
{
	
	/**
	*get_ElementsByClassName
	*Retourne tous les éléments dont la classe correspond à celle spécifier.
	*Diverses possibilités pour utiliser cette fonction:
	*     - Mxp_Elements.getElementsByClassName(conteneur, "div", array("maclasse", "uneclasse")); => retourne tout les éléments de type "div" contenu dans l'élément "conteneur" dont la classe correspond à "maclasse" ou à "uneclasse".
	*     - Mxp_Elements.getElementsByClassName(conteneur, "*", array("maclasse", "uneclasse")); => retourne tout les éléments contenu dans l'élément "conteneur" dont la classe correspond à "maclasse" ou à "uneclasse".
	*     - Mxp_Elements.getElementsByClassName(conteneur, "div", "maclasse"); => retourne tout les éléments de type "div" contenu dans l'élément "conteneur" dont la classe correspond à "maclasse".
	*
	*@param     object[Element HTML]
	*@param     string
	*@param     string|array
	*
	*@return     void
	*/
	get_ElementsByClassName: function(obj_Element, str_Balise, variant_Classe)
	{
		if(typeof Array.prototype.push == "undefined")
		{
			// méthode push() pour la compatibilité IE5
			Array.prototype.push = function()
				{
					var int_Indice = 0;
					var int_Nb = arguments.length;
					for(int_Indice = 0; int_Indice < int_Nb; int_Indice++)
					{
						this[this.length] = arguments[int_Indice];
					}
					return this.length;
				};
		}
	
		var array_Elements = (str_Balise == "*" && obj_Element.all)? obj_Element.all : obj_Element.getElementsByTagName(str_Balise);
		var array_Resultat = new Array();
		var array_Classe = new Array();
		if(typeof variant_Classe == "object")
		{
			var int_NbClasse = variant_Classe.length;
			for(var int_I=0; int_I<int_NbClasse; int_I++)
			{
				array_Classe.push(variant_Classe[int_I]);
			}
		}
		else
			array_Classe.push(variant_Classe);
		
		var obj_ElementTemp;
		var bool_Ok;
		var int_Nb = array_Elements.length;
		var int_NbClasse = array_Classe.length;
		for(var int_I=0; int_I<int_Nb; int_I++)
		{
			obj_ElementTemp = array_Elements[int_I];
			bool_Ok = false;
			var y = 0
			while(!bool_Ok && y<int_NbClasse)
			{
				bool_Ok = (array_Classe[y] == obj_ElementTemp.className)
				y++;
			}
			if(bool_Ok)
				array_Resultat.push(obj_ElementTemp);
		}
		return (array_Resultat)
	},
	
	/**
	*get_Cible
	*Retourne la source d'un événement.
	*
	*@param     object[Evenement]
	*
	*@return     boolean | string
	*/
	get_Cible: function(obj_Evenement)
	{
		var target = window.event ? window.event.srcElement : obj_Evenement ? obj_Evenement.target : this;
		if(!target) return false;
		if(target.nodeName.toLowerCase() != 'a') target = target.parentNode; //Pour Safari
		return target;
	},
	
	/**
	*set_Action
	*Ajoute une action sur un événement.
	*
	*@param     object[Element HTML]
	*@param     string
	*@param     object[Fonction]
	*@param     boolean
	*
	*@return     void
	*/
	set_Action: function(obj_Element, str_Type, obj_Fonction, bool_ModeCapture)
	{
		return obj_Element.addEventListener ?
			obj_Element.addEventListener(str_Type, obj_Fonction, bool_ModeCapture):
			obj_Element.attachEvent ?
				obj_Element.attachEvent('on' + str_Type, obj_Fonction):
				obj_Element['on' + str_Type] = obj_Fonction;
	},
	
	/**
	*unset_Action
	*Enlève une action sur un événement.
	*
	*@param     object[Element HTML]
	*@param     string
	*@param     object[Fonction]
	*@param     boolean
	*
	*@return     void
	*/
	unset_Action: function(obj_Element, str_Type, obj_Fonction, bool_ModeCapture)
	{
		return obj_Element.removeEventListener?
			obj_Element.removeEventListener(str_Type, obj_Fonction, bool_ModeCapture):
			obj_Element.detachEvent?
				obj_Element.detachEvent('on' + str_Type, obj_Fonction):
				obj_Element['on' + str_Type] = null;
	}
};


Enfin, j'ai un objet permettant de gérer l'évènement window.onLoad (mxp_init.js)

var Mxp_Init =
{
	/**
	*set_NouvelleFonction
	*Ajoute une fonction dans la pile des initialisations.
	*Créer à partir des travaux de Dean Edwards/Matthias Miller/John Resig
	*  http://dean.edwards.name/weblog/2006/06/again/
 
	*  http://www.vivabit.com/bollocks/2006/06/21/a-dom-ready-extension-for-prototype
 
	*  http://simon.incutio.com/archive/2004/05/26/addLoadEvent
 
	*
	*@param     object[Evenement]
	*
	*@return     boolean
	*/
	set_NouvelleFonction: function(obj_Fonction)
	{
		if(typeof Array.prototype.push == "undefined")
		{
			// méthode push() pour la compatibilité IE5
			Array.prototype.push = function()
				{
					var int_Indice = 0;
					var int_Nb = arguments.length;
					for(int_Indice = 0; int_Indice < int_Nb; int_Indice++)
					{
						this[this.length] = arguments[int_Indice];
					}
					return this.length;
				};
		}
	
		if(!window.array_MxpInit)
		{
			var exec_Initialisation = function ()
			{
				if(arguments.callee.done)
					return;

				arguments.callee.done = true;
				if(window.int_MxpTimer)
				{
					clearInterval(window.int_MxpTimer);
					window.int_MxpTimer = null;
				}

				var int_Nb = window.array_MxpInit.length;
				for(var int_I=0;int_I < int_Nb;int_I++)
				{
					if(window.array_MxpInit[int_I])
						window.array_MxpInit[int_I]();
				}
				window.array_MxpInit = null;

				/*@cc_on @*/
					/*@if (@_win32)
						document.getElementById("__ie_onload").onreadystatechange = "";
				/*@end @*/
			};

			if(document.addEventListener)
				document.addEventListener("DOMContentLoaded", exec_Initialisation, false);
      
			// Internet Explorer
			/*@cc_on @*/
				/*@if (@_win32)
					document.write("<scr"+"ipt id=__ie_onload defer src=javascript:void(0)><\/scr"+"ipt>");
				var script = document.getElementById("__ie_onload");
				script.onreadystatechange = function()
				{
					if (this.readyState == "complete")
					{
						exec_Initialisation();
					}
			};
			/*@end @*/

			if(/WebKit/i.test(navigator.userAgent))
			{
				window.int_MxpTimer = setInterval(
					function()
					{
						if (/loaded|complete/.test(document.readyState))
						{
							exec_Initialisation();
						}
					},
					10);
			}

			window.onload = exec_Initialisation;
			window.array_MxpInit = [];
		}

		window.array_MxpInit.push(obj_Fonction);
	}
};


en ce qui concerne mon fichier html :

<!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="fr" lang="fr">
	<head>
		<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
		<title>MEXUP - JavaScript - composant menu</title>
		<link rel="author" title="Autheur" href="http://www.mexup-soft.com">
		<meta http-equiv="Content-Style-Type" content="text/css">
		<meta http-equiv="Content-Script-Type" content="text/javascript" />
		<link rel="stylesheet" type="text/css" href="style.css" media="screen, projection" />
		<script type="text/javascript" src="../mxp_init.js"></script>
		<script type="text/javascript" src="../bib_elements.js"></script>
		<script type="text/javascript" src="comp_menu.js"></script>
	</head>
	<body>
		<ul id="menus" class="menu_click">
			<li>Lib 1
				<ul>
					<li><a href="unLien">Lib 1.0</a></li>
					<li><a href="unLien">Lib 1.1</a></li>
				</ul>
			</li>
			<li>Lib 2
				<ul>
					<li><a href="unLien">Lib 2.0</a></li>
					<li><a href="unLien">Lib 2.1</a></li>
					<li>Lib 2.2
						<ul>
							<li><a href="unLien">Lib 2.2.0</a></li>
							<li><a href="unLien">Lib 2.2.1</a></li>
						</ul>
					</li>
					<li><a href="unLien">Lib 2.3</a></li>
					<li>Lib 2.4
						<ul>
							<li><a href="unLien">Lib 2.4.0</a></li>
							<li><a href="unLien">Lib 2.4.1</a></li>
						</ul>
					</li>
					<li>Lib 2.5
						<ul>
							<li><a href="unLien">Lib 2.5.0</a></li>
							<li><a href="unLien">Lib 2.5.1</a></li>
							<li>Lib 2.5.2
								<ul>
									<li class="actif"><strong><a href="unLien">Lib 2.5.2.0</a></strong></li>
									<li><a href="unLien">Lib 2.5.2.1</a></li>
									<li><a href="unLien">Lib 2.5.2.2</a></li>
									<li><a href="unLien">Lib 2.5.2.3</a></li>
								</ul>
							</li>
							<li><a href="unLien">Lib 2.5.3</a></li>
							<li>Lib 2.5.4
								<ul>
									<li><a href="unLien">Lib 2.5.2.0</a></li>
									<li><a href="unLien">Lib 2.5.2.1</a></li>
								</ul>
							</li>
						</ul>
					</li>
					<li><a href="unLien">Lib 2.6</a></li>
				</ul>
			</li>
			<li>Lib 3
				<ul>
					<li><a href="unLien">Lib 3.0</a></li>
				</ul>
			</li>
		</ul>
		
		<hr />
		
		<ul class="menu_click_unique">
			<li>Lib 1
				<ul>
					<li class="actif"><strong><a href="unLien">Lib 1.0</a></strong></li>
					<li><a href="unLien">Lib 1.1</a></li>
				</ul>
			</li>
			<li>Lib 2
				<ul>
					<li><a href="unLien">Lib 2.0</a></li>
					<li><a href="unLien">Lib 2.1</a></li>
					<li>Lib 2.2
						<ul>
							<li><a href="unLien">Lib 2.2.0</a></li>
							<li><a href="unLien">Lib 2.2.1</a></li>
						</ul>
					</li>
					<li><a href="unLien">Lib 2.3</a></li>
					<li><a href="unLien">Lib 2.4</a></li>
					<li><a href="unLien">Lib 2.5</a></li>
					<li><a href="unLien">Lib 2.6</a></li>
				</ul>
			</li>
			<li>Lib 3
				<ul>
					<li><a href="unLien">Lib 3.0</a></li>
				</ul>
			</li>
		</ul>
		
	</body>
</html>


et finalement mon CSS (style.css)

.menu_click, .menu_click_unique {
	padding: 0;
	margin: 0;
	background: #CCC;
	width: 120px;
	border: 1px solid #000;
}
.menu_click *, .menu_click_unique * {
	padding: 0;
	margin: 0;
	list-style-type: none;
}
.menu_click li, .menu_click_unique li {
	display: list-item;
	width: 120px;
	font-size: 0.9em;
	line-height: 1.3em;
	text-align: center;
	background: #CCC;
}
.menu_click li ul, .menu_click_unique li ul {
	border-top: 1px solid #000;
}
.menu_click li ul li, .menu_click_unique li ul li {
	clear: both;
	width: 120px;
	border: 0;
	margin: 0;
	text-align: left;
}
.menu_click li a, .menu_click_unique li a {
	text-decoration: none;
	color: #000;
	background-color: #CCC;
	display: block;
	cursor: pointer;
}
.menu_click li ul a, .menu_click_unique li ul a {
	height: 1.6em;
	line-height: 1.6em;
	text-indent: 10px;
	background-color: #EEE;
	cursor: pointer;
}
.menu_click li ul a:hover, .menu_click_unique li ul a:hover {
	background-color: #58F;
}

.menu_on {
	background: url('minus.png') no-repeat left center;
	padding-left: 15px;
}

.menu_off {
	background: url('plus.png') no-repeat left center;
	padding-left: 15px;
}


les 2 images utilisés:
upload/9927-minus.png
upload/9927-plus.png
Modifié par Francois44 (16 Dec 2006 - 15:38)
Bonjour,
Ton code semble interessant. J'ai essayé de le reconstituer en local mais le js ne fonctionne pas. Ne pourrais-tu pas nous donner un exemple en ligne ? Smiley cligne