Skip to content

Instantly share code, notes, and snippets.

@tow8ie
Created October 19, 2009 19:56
Show Gist options
  • Save tow8ie/213677 to your computer and use it in GitHub Desktop.
Save tow8ie/213677 to your computer and use it in GitHub Desktop.
Some JS code I wrote back in 2005 to create a sliding menu. Animation timing and “cross-browser” functionality was all done “by hand”. Despite of bad coding style it still shows how jQuery & Co. and a culture of widespread JS knowledge totally changed the
/*
* SlideMenu
* ~~~~~~~~~
*
* Version: 1.0.0
* Datum: 29.12.2005
* Autor: Tobias Adam
*
* Getestet auf: Win IE 6.0
* Win IE 5.5
* Win IE 5.0
* Win Firefox 1.07
* Win Opera 8.5
*
* WICHTIG !!!
* Leerzeichen und Umbrüche im Quelltext können u.U. dazu führen, dass das Script nicht
* funktioniert. Deshalb bei Erweiterungen unbedingt an die Vorlage des XHTML-Quelltextes halten.
*
*/
/*** VARIABLEN-DEKLARATIONEN ***/
/*** Parameter zum Feintunen des Auffahreffekts ****/
/**/
/**/ var iterationen= 10;
/**/ var verzoegerung= 5;
/**/ var auffahreffektAn= true;
/**/
/*******************************************************/
/* Globale Variablen zur logischen Steuerung des Menüs*/
var zuletztGeoeffneteMenueNummer= -1;
var ersterProzessGestartet= false;
var prozess2An= false;
var zeitSchritte= 0;
/* Bietet Zugriff auf die oberste Ebene der geschachtelten ul-Listen */
var ebene1= document.getElementById("ersteListe");
/* Nach dem Durchlauf von initEbene2() bietet ebene2 Zugriff auf alle ul-Listen der 2. Ebene
Dabei enspricht ebene2[0] dem Untermenü der ersten Kategorie, ebene[2] dem der zweiten usw. */
var ebene2= new Array();
/* Nach dem Durchlauf von hoehenErmitteln() hat dieses Array die Höhen aller Unter-Menüs
gespeichert */
var hoehen= new Array();
/* Nach dem ersten Aufruf von ersteEbeneFunktionalisieren() enthält diese Variable die Anzahl
der Menü-Punkte */
var anzahlMenuePunkte= 0;
/*** PROGRAMM ***/
init();
/*** FUNKTIONEN ***/
function init() {
ersteEbeneFunktionalisieren();
initEbene2();
hoehenErmitteln();
zweiteEbeneFunktionalisieren();
adaptWidths();
unterMenuesVerstecken();
/*
* Der IE zickt rum: erst beim 2. Aufruf der Funktion menuePunktAktion() funktioniert diese.
* Deshalb werden die Menüs "initialisiert" indem sie zunächst einmal ohne Grund aufgerufen
* werden
*/
for (i= 0; i < hoehen.length; i++) {
unterMenueAuffahren(i,0);
}
}
/*
* Speichert die Höhen aller Untermenüs im Array hoehen.
* WICHTIG: Kann erst NACH der Funktion initEbene2() aufgerufen werden !
* Durch Zuweisen eines Strings mit Textcharakter an jedes Element des hoehen-Arrays kann man auf
* bequeme Weise den Auffahreffekt abschalten. Gesteuert wird der Effekt über den Parameter
* "auffahreffektAn"
*/
function hoehenErmitteln() {
for (i= 0; i < ebene2.length; i++) {
hoehen[i]= ebene2[i].offsetHeight;
}
if (!auffahreffektAn) {
for (j= 0; j < hoehen.length; j++) {
hoehen[j]= "Tobias Adam";
}
}
} // end of hoehenErmitteln()
/*
* Diese Funktion trägt in die a-Tags der obersten Ebene zusätzlich folgende Attribute ein:
* onMouseOver="menuePunktAktion(menuePunktNummer)"
* onMouseOut="unterMenueAktionOut()"
* mit der entsprechenden Menüpunkt-Nummer
* Dabei hat der erste Eintrag die Menüpunkt-Nummer 0, der zweite die Nummer 1 usw.
*/
function ersteEbeneFunktionalisieren() {
menuePunktZaehler= 0;
for (i= 0; i < ebene1.childNodes.length; i++) {
if (ebene1.childNodes[i].nodeName == "LI") {
for (j= 0; j < ebene1.childNodes[i].childNodes.length; j++) {
if (ebene1.childNodes[i].childNodes[j].nodeName == "A") {
// a-Node merken
aNode= ebene1.childNodes[i].childNodes[j];
// a-Href-Attribut merken
aNodeHrefAttribute= aNode.getAttribute("href");
// a-Id-Attribut merken
aNodeIdAttribute= aNode.getAttribute("id");
// Text im a-Node merken
aNodeText= aNode.firstChild.data;
// ul-Node merken
var ulNode;
for (l= 0; l < ebene1.childNodes[i].childNodes.length; l++) {
if (ebene1.childNodes[i].childNodes[l].nodeName == "UL") {
ulNode= ebene1.childNodes[i].childNodes[l];
}
}
// class-Attribute des UL-Node merken
ulNodeClassAttribute= ulNode.getAttribute("class");
/*
* Der IE kann das Class-Attribut nur mit dem Bezeichner "className" auslesen, deshalb diese Abfrage
*/
if (!ulNodeClassAttribute) ulNodeClassAttribute= ulNode.getAttribute("className");
//Daten im UL-Node merken
ulNodeData= ulNode.innerHTML;
ebene1.childNodes[i].removeChild(aNode);
ebene1.childNodes[i].innerHTML= "<a href=\"" + aNodeHrefAttribute + "\" id=\"" + aNodeIdAttribute + "\" onMouseover=\"menuePunktAktion(" + menuePunktZaehler + ")\" onMouseout=\"unterMenueAktionOut()\">" + aNodeText + "</a><ul class=\"" + ulNodeClassAttribute + "\">" + ulNodeData + "</ul>";
menuePunktZaehler++;
}
}
}
}
anzahlMenuePunkte= menuePunktZaehler;
} // end of ersteEbeneFunktionalisieren()
/*
* Diese Funktion liest in das Array ebene2 alle ul-Listen der 2. Ebene ein
*/
function initEbene2() {
var arrayZaehler= 0;
for (i= 0; i < ebene1.childNodes.length; i++) {
if (ebene1.childNodes[i].nodeName == "LI") {
for (j= 0; j < ebene1.childNodes[i].childNodes.length; j++) {
if (ebene1.childNodes[i].childNodes[j].nodeName == "UL") {
ebene2[arrayZaehler]= ebene1.childNodes[i].childNodes[j];
arrayZaehler++;
}
}
}
}
} // end of initEbene2()
/*
* Diese Funktion trägt in die a-Tags der zweiten Menü-Ebene zusätzlich folgende Attribute ein:
* onMouseOver="unterMenueAktionOver()"
* onMouseOut="unterMenueAktionOut()"
*/
function zweiteEbeneFunktionalisieren() {
for (k= 0; k < ebene2.length; k++) {
for (i= 0; i < ebene2[k].childNodes.length; i++) {
if (ebene2[k].childNodes[i].nodeName == "LI") {
for (j= 0; j < ebene2[k].childNodes[i].childNodes.length; j++) {
if (ebene2[k].childNodes[i].childNodes[j].nodeName == "A") {
// a-Node merken
aNode= ebene2[k].childNodes[i].childNodes[j];
// a-Href-Attribut merken
aNodeHrefAttribute= aNode.getAttribute("href");
// a-Id-Attribut merken
aNodeIdAttribute= aNode.getAttribute("id");
// Text im a-Node merken
aNodeText= aNode.firstChild.data;
ebene2[k].childNodes[i].removeChild(aNode);
ebene2[k].childNodes[i].innerHTML= "<a href=\"" + aNodeHrefAttribute + "\" id=\"" + aNodeIdAttribute + "\" onMouseover=\"unterMenueAktionOver()\" onMouseout=\"unterMenueAktionOut()\">" + aNodeText + "</a>";
}
}
}
}
}
} // end of zweiteEbeneFunktionalisieren()
function unterMenuesVerstecken() {
for (i= 0; i < ebene2.length; i++) {
ebene2[i].style.visibility= "hidden";
}
} // end of unterMenuesVerstecken
function unterMenueAufdecken(menueNummer) {
ebene2[menueNummer].style.visibility= "visible";
} // end of unterMenueAufdecken
function menuePunktAktion(menueNummer) {
if (ersterProzessGestartet) {
clearTimeout(prozess);
}
// if-Abfrage notwendig um mehrfaches Öffnen beim Überfahren des gleichen Menüpunkts zu vermeiden
if (menueNummer != zuletztGeoeffneteMenueNummer) {
zuletztGeoeffneteMenueNummer= menueNummer;
unterMenuesVerstecken();
startPosition= - hoehen[menueNummer];
hoehe= hoehen[menueNummer];
unterMenueAufdecken(menueNummer);
zeitSchritte= 1; //reset der Zeitschritte
unterMenueAuffahren(menueNummer,startPosition);
}
} // end of function menuePunktAktion()
function unterMenueAuffahren(menueNummer,anfangsPosition) {
if (anfangsPosition <= 0) {
if (prozess2An) clearTimeout(prozess2);
/* Original: unterMenuePositionieren(menueNummer,anfangsPosition); */
/* angepasst */
unterMenuePositionieren(menueNummer,anfangsPosition + 3);
/* angepasst */
neuePosition= Math.round(- hoehen[menueNummer]*Math.exp(-(zeitSchritte/iterationen)));
zeitSchritte++;
prozess2= setTimeout("unterMenueAuffahren(" + menueNummer + "," + neuePosition + ")",verzoegerung);
prozess2An= true;
}
} // end of function unterMenueAuffahren
/*
* Die Position wird relativ zur "normalen" Position angegeben (die Position, die per CSS angegeben
* wurde)
*/
function unterMenuePositionieren(menueNummer,position) {
stil= ebene2[menueNummer].style;
stil.position= "relative";
stil.top= position + "px";
} // end of unterMenuePositionieren
function unterMenueAktionOver() {
if (ersterProzessGestartet) {
clearTimeout(prozess);
}
}
function unterMenueAktionOut() {
prozess= setTimeout("tatsaechlichSchliessen()",500);
ersterProzessGestartet= true;
}
function tatsaechlichSchliessen() {
unterMenuesVerstecken();
zuletztGeoeffneteMenueNummer= -1;
}
function getChildsNamed(node,tagName) {
var childs= new Array();
var arrayCount= 0;
for (i= 0; i < node.childNodes.length; i++) {
//alert(node.childNodes[i].nodeName);
if (node.childNodes[i].nodeName == tagName) {
childs[arrayCount]= node.childNodes[i];
//alert(arrayCount);
arrayCount++;
}
}
return childs;
}
function getFirstChildNamed(node,tagName) {
for (i= 0; i < node.childNodes.length; i++) {
if (node.childNodes[i].nodeName == tagName) {
return node.childNodes[i];
}
}
return false;
}
/*
* Diese Funktion passt die Breite der einzelnen Untermenüs an die Breite des jeweils größten Eintrags an.
* Um eine Browser-übergreifende Kompatibilität zu erreichen werden nicht direkt die offsetWidths ausgelesen, da diese vom
* FF nicht richtig verstanden werden, wenn Textelemente das sie umgebende Element verlassen; der IE streckt dagegegen das
* ihn umgebende Element
* Zur Lösunmg des Problems wird die Anzahl der Buchstaben der einzelnen Einträge ausgelesen und diese Anzahl dann mit einer
* mittleren Buchstabenbreite multipliziert, um eine annähernd angepasste Breite zu bekommen.
*/
function adaptWidths() {
var ulTag1= document.getElementById("ersteListe");
liTags1= getChildsNamed(ulTag1,"LI");
// jedes liChild hat genau ein ul-Child, deshalb wird ein gleichgroßes Array ulTags2 erstellt
var ulTags2= new Array();
for (j= 0; j < liTags1.length; j++) {
ulTags2[j]= getFirstChildNamed(liTags1[j],"UL");
}
// jedes ulTag hat mehrere liTags, deshalb gibt es jeweils ein Array aus liTags
var liTags2= new Array();
for (j= 0; j < ulTags2.length; j++) {
liTags2[j]= getChildsNamed(ulTags2[j],"LI");
}
// jedes dieser liTags hat ein aTag
// der IE kann daraus bereits die richtige Offset-Width berechnen
var aTags= new Array(liTags2.length);
for (i= 0; i < liTags2.length; i++) {
aTags[i]= new Array(liTags2[i].length);
}
for (j= 0; j < liTags2.length; j++) {
for (k= 0; k < liTags2[j].length; k++) {
aTags[j][k]= getFirstChildNamed(liTags2[j][k],"A");
}
}
// die entsprechenden OffsetWidths abspeichern im Array offsetWidths
var offsetWidths= new Array(aTags.length);
for (i= 0; i < aTags.length; i++) {
offsetWidths[i]= new Array(aTags[i].length);
}
for (j= 0; j < aTags.length; j++) {
for (k= 0; k < aTags[j].length; k++) {
offsetWidths[j][k]= parseInt(aTags[j][k].offsetWidth);
}
}
/* Diese hier ausgeklammerte Lösung funktioniert ausschließlich für den IE und auch nur dann, wenn umgebenden ul-Tags eine
* sehr kleine width bekommen, so dass der IE das umgebende ul auf die richtige Größe streckt
*/
/*
var biggestWidths= new Array(offsetWidths.length);
for (i= 0; i < biggestWidths.length; i++) {
biggestWidths[i]= -1;
}
for (i= 0; i < offsetWidths.length; i++) {
for (j= 0; j < offsetWidths[i].length; j++) {
if (offsetWidths[i][j] > biggestWidths[i]) {
biggestWidths[i]= offsetWidths[i][j];
}
}
}
for (i= 0; i < aTags.length; i++) {
for (j= 0; j < aTags[i].length; j++) {
aTags[i][j].style.width= biggestWidths[i];
}
}
*/
/* Dies ist die oben angesprochene weniger exakte, dafür aber allgemeingültige Lösung, die mit dem FF und dem IE
* funktioniert
*/
// hier werden die textNodes referenziert und im 2-dim. Array textNodes gespeichert
var textNodes= new Array(aTags.length);
for (i= 0; i < aTags.length; i++) {
textNodes[i]= new Array(aTags[i].length);
}
for (j= 0; j < aTags.length; j++) {
for (k= 0; k < aTags[j].length; k++) {
textNodes[j][k]= aTags[j][k].firstChild;
}
}
// die entsprechenden Buchstabenanzehlen der textNodes werden im 2-dim. Array textLengths gespeichert
var textLengths= new Array(textNodes.length);
for (i= 0; i < textNodes.length; i++) {
textLengths[i]= new Array(textNodes[i].length);
}
for (i= 0; i < textLengths.length; i++) {
for (j= 0; j < textLengths[i].length; j++) {
textLengths[i][j]= textNodes[i][j].nodeValue.length;
}
}
// die jeweils größte Buchstabenanzahl einer Kategorie wird im Array biggestTLength gespeichert
var biggestTLength= new Array(textLengths.length);
for (i= 0; i < biggestTLength.length; i++) {
biggestTLength[i]= 0;
}
for (i= 0; i < textLengths.length; i++) {
for (j= 0; j < textLengths[i].length; j++) {
if (textLengths[i][j] > biggestTLength[i]) {
biggestTLength[i]= textLengths[i][j];
}
}
}
// es wird eine durchschnittliche Buchstabenbreite von 8 Pixeln angenommen
var averageLetterWidth= 8;
/* den ulTags der Unterkategorien und den in Ihnen beinhalteten a-Tags werden die maximalen Breiten ihrer jeweiligen
* Kategorie zugewiesen
*/
for (i= 0; i < ulTags2.length; i++) {
ulTags2[i].style.width= ((biggestTLength[i] * averageLetterWidth) + 6) + "px";
}
for (i= 0; i < aTags.length; i++) {
for (j= 0; j < aTags[i].length; j++) {
aTags[i][j].style.width= biggestTLength[i] * averageLetterWidth + "px";
}
}
// hier wird noch die Position des letzten Menüpunktes so korrigiert, dass er nicht nach rechts über den Rand hinausgeht
// Breite des letzten Kategoriepunkts ermitteln
var letzterMenuePunkt= getFirstChildNamed(liTags1[liTags1.length - 1],"A");
var letzterMenuePunktWidth= letzterMenuePunkt.offsetWidth;
var diff= letzterMenuePunktWidth - (biggestTLength[biggestTLength.length - 1] * 8);
ulTags2[ulTags2.length - 1].style.left= (diff - 20) + "px";
} // end of adaptWidths()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment