Ok, je met à disposition mon code ... Etant avant tout dévelopeur PHP, j'espère qu'il ne soit pas trop pourri
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:
Modifié par Francois44 (16 Dec 2006 - 15:38)