JS/AJAX und DOM - style.display im IE 6 macht nicht was er soll
dicon
- javascript
Moin community...
folgende Problemstellung:
unter diesem Link findet ihr den Anfang eines Projektes... http://www.embla.de/_lk76Z7hh55/
Bitte mal oben links einen Link klicken / ein Fenster öffnen. Im Fenster dann oben einen
Menüpunkt öffnen. Sowohl im FF als auch im IE (bei mir 6) klappt das Menü auf (zweite Menüebene
bitte ignorieren, soweit bin ich noch nicht.)
Ein erneuter klick auf den Menüpunkt sollte das ganze wieder schließen... wieder öffnen...
schließen... usw. FF alles fein. Im IE 6 bleib das Menü stehen - Warum?
Wie entsteht das Menü?... Während sich das Fenster öffnet wird via httpRequest die Menüzeile
durch ein PHP-Script (muss sein, da später alle Menüs/Submenüs aus einer MSQL dynamisch erstellt
werden) generiert und als Tabelle, die in ein neues DIV gekapselt ist, in die DOM-Struktur
eingebunden. Die Submenüs entstehen auf dem gleichen Weg.
Der Code für Menüs werden als komplette "Seite" eingebunden, beginnend mit <!DOCTYPE... und
endend mit </html>, alle externen CSS und JS sind entsprechend Deklaration enthalten.
Für das auf- und zuklappen der Menüs ist folgende JS-Funktion zuständig:
/* =========== snip begin =========== */
1 evtShowMenu = function(e){
2 var menu = getEventTarget(e);
3 var mId = (e.target)? e.target.id : e.srcElement.id;
4
5 if(!mId) {
6 window.alert('Fehler - evtShowMenu: Event-Element nicht vorhanden.');
7 } else {
8 var m = setMenuLev(mId); // erzeugt Id Folge-Menu
9 var isM = document.getElementById(m); // ermittelt Folge-Menu
10
11 if (!isM) { // prüft, ob Folge-Menu im DOM vorhanden
12 getAjaxData(mId); // wenn nein, erzeuge Folge-Menu
13 } else if (isM.style.display == 'none') {
14 isM.style.display = 'block'; // wenn ja und unsichtbar, mache sichtbar
15 } else if (isM.style.display == 'block') {
16 isM.style.display = 'none'; // wenn ja und sichtbar, mache unsichtbar
17 isM.style.border = 'solid 1px'; // nur zur Kontrolle für IE 6
18 } else {
19 window.alert('Fehler - evtShowMenu: Problem mit style.display .');
20 }
30 }
31 }
/* =========== snip end =========== */
Zeile 2 und 3: übernehmen das Eventobjekt vom Listener
Das Script läuft auch im IE 6 ohne Fehlermeldungen durch. Wenn Ihr das Menu schließt... öffnet,
schließt... seht ihr oben links neben dem Ausklapper eine dünne schwarze Linie (generiert in Zeile 17),
die tut was sie soll, nämlich erscheinen und verschwinden - nur eben der Ausklapper nicht.
Folgende Dinge habe ich ohne Ergebnis probiert:
a) Zeile 13 bis 18 in eine separate Funktion ausgelagert und durch window.setTimeout('func', 1000) ersetzt
Hintergrund: vielleicht braucht der IE ja 'n bisschen, bis er versteht, was er machen soll
b) Zeile 13 bis 18 durch einen Funktionsaufruf ersetzt, der via removeChild das komplette Menü wieder
aus dem DOM entfernt
c) document.all-Methode
Bleibt vielleicht noch zu erwähnen, dass es im IE 7 funktioniert. Leider muss ich bis IE 6 abwärtskompatibel
bleiben. Ich habe leider überhaupt keinen Plan, wo's klemmt - eventuell ja jemand von Euch
Vielleicht noch was: Ich beginne grad erst zu verstehen wie JS + DOM funktioniert und versuche es umzusetzen,
bewusst ohne auf fertige Sachen zurückzugreifen - etwas Nachsicht bitte, wenn der Code amateurhaft ist.
Grüße und Danke, dicon
hi,
9 var isM = document.getElementById(m); // ermittelt Folge-Menu
Ob isM hier wirklich auf das richtige Element verweist, hast du überprüft?
13 } else if (isM.style.display == 'none') {
14 isM.style.display = 'block'; // wenn ja und unsichtbar, mache sichtbar
15 } else if (isM.style.display == 'block') {
Und ob hier jeweils die Bedingung erfüllt ist, von der du es erwartest, hast du bereits überprüft?
gruß,
wahsaga
hallo wahsag
jep alles geprüft... hab mir an jeder Stelle des Scriptes via window.alert() die entsprechende
Elemente und Rückgabewerte auswerfen lassen... nach diesem Muster
13 } else if (isM.style.display == 'none') {
14 window.alert('evtShowMenu '+m+': - Menu vorhanden, aber nicht sichtbar, also werde sichtbar...');
15 isM.style.display = 'block'; // wenn ja und unsichtbar, mache sichtbar
16 } else if (isM.style.display == 'block') {
17 window.alert('evtShowMenu '+m+': - Menu vorhanden und sichtbar, also werde unsichtbar...');
18 isM.style.display = 'none'; // wenn ja und sichtbar, mache unsichtbar
Die Varialble m steht für die DIV-ID des generierten Menü-DIVs.
Via try {} catch(e) {} hab ich's auch probiert um event. "versteckte" Fehler abzufangen... naja, das Ergebnis
ist bekannt...
Für mich als Laien sieht es im IE irgendwie so aus, alsob das DIV mit dem Ausklapper irgendwie außerhalb der
Struktur steht?!
Vielleicht hilft die Funktion weiter, die das Menu-DIV generiert:
203 function createMenu(menuId) {
204 var toolbar = document.getElementById("toolbar");
205 var menuPos = setMenuPos(menuId);
206 var menu = document.createElement("div");
207 menu.style.position = "absolute";
208 menu.style.top = menuPos.Y+"px";
209 menu.style.left = menuPos.X+"px";
210 menu.style.display = "block";
211 menu.className = "menu";
212 menu.id = menuPos.Id;
213
214 if(http.readyState == 4) {
215 if (http.status == 200) {
216 menu.innerHTML = http.responseText;
217 } else {
218 window.alert("Fehler");
219 }
220 }
221
222 toolbar.appendChild(menu);
223 }
Grüße, dicon
hi,
Für mich als Laien sieht es im IE irgendwie so aus, alsob das DIV mit dem Ausklapper irgendwie außerhalb der Struktur steht?!
Das sollte sich ja bspw. mit der Developer Toolbar untersuchen lassen, wenn man mit der mal die DOM-Struktur unter die Lupe nimmt.
gruß,
wahsaga
Das sollte sich ja bspw. mit der Developer Toolbar untersuchen lassen, wenn man mit
der mal die DOM-Struktur unter die Lupe nimmt.
Das war der entscheidende Tip... wusste nicht, dass es für den IE was vergleichbares zu
Firebug gibt... egal. Ich konnte den Fehler eingrenzen.
Im IE 6 werden durch den httpRequest 5 DIVs erzeugt, alle mit indentischer ID, die ersten
4 leer, das 5. dann mit dem eigentlichen HTML-Code für das Menü. Wenn ich jetzt versuche,
mittels Event das Menu verschwinden zu lassen, spricht die Funktion evtShowMenu (s. oben)
natürlich das erste Element mit der ID im DOM an und... es passiert "sichtbar" nix.
Bleibt die Frage, was stimmt an dem httpRequest nicht. Für mich siehts so aus, alsob
http.onreadystatechange vor dem empfangen der HTML-Daten auch den kompletten TCP-
Handshake vorneweg verarbeitet und mit jeder Serverantwort (ACK, SYC usw.) ein DIV erzeugt.
Nice to know... hilft mir aber nicht weiter.
Vielleicht entdeckt ja jemand von Euch den Bug im httpRequest:
/* =========== snip begin =========== */
91 function getAjaxData(elementId) {
92 http = false;
93 url = 'dyn_menu.php?ID='+elementId;
94
95 if (window.XMLHttpRequest) {
96 http = new XMLHttpRequest();
97 if (http.overrideMimeType) {
98 http.overrideMimeType('text/xml');
99 }
100 } else if (window.ActiveXObject) {
101 try {
102 http = new ActiveXObject("Msxml2.XMLHTTP");
103 } catch (e) {
104 try {
105 http = new ActiveXObject("Microsoft.XMLHTTP");
106 } catch (e) {}
107 }
108 }
109
110 if (!http) {
111 alert('Ende :( Kann keine XMLHTTP-Instanz erzeugen');
112 return false;
113 }
114 http.open('GET', url, true);
115 http.setRequestHeader( 'Content-length', url.length );
116 http.onreadystatechange = new Function("createMenu('"+elementId+"')");
117 http.send(null);
118 }
/* =========== snip end =========== */
Vielleicht noch als Anmerkung: Ich hatte vor 'ner Zeit ein ähnliches Problem, allerdings
mit der http.open POST Methode... damals lag's am Parameter "true" - mit "fals" war das
Problem gelöst - wirkt aber im aktuellen Fall nicht.
Grüße, dicon
hi,
Im IE 6 werden durch den httpRequest 5 DIVs erzeugt
Bleibt die Frage, was stimmt an dem httpRequest nicht.
Für mich siehts so aus, alsob http.onreadystatechange vor dem empfangen der HTML-Daten auch den kompletten TCP-
Handshake vorneweg verarbeitet und mit jeder Serverantwort (ACK, SYC usw.) ein DIV erzeugt.
onreadystatechange reagiert, wie der Name schon sagt, auf jeden "Change" des redayStates - und dieser durchläuft 5 Zustände:
0 = uninitialized
1 = loading
2 = loaded
3 = interactive
4 = complete
Und jedes mal rufst du deine Funktion createMenu auf.
Die erzeugt jedes mal ein neues Div-Element.
_Dann_ fragt sie ob, ob der readyState gleich 4 ist, und will irgendwas mit irgendeinem innerHTML machen.
_Anschliessnd_ hängt sie, jetzt wieder vollkommen unabhängig vom aktuellen readyState, mit
toolbar.appendChild(menu);
das erzeugte Div-Element ins Dokument ein ...
gruß,
wahsaga
Hi wahsag...
Und jedes mal rufst du deine Funktion createMenu auf.
...
Korrekt, daher war ich ja auch der Meinung, dass, wenn ich den http.open-Parameter "true/false" auf "false"
setze, onreadystatechange erst abgearbeitet wird, wenn status == complete. Firefox versteht das, der
IE 6 wohl nicht.
Egal, ich hab das Problem erstmal gelöst, wobei ich nicht weiß, ob das 'ne wirklich elegante Lösung ist:
Ansatz: Soll der Browser die Funktion createMenu doch sooft aufrufen wie er will... wichtig ist nur, dass
das DIV mit dem Menü erst generiert wird, wenn readyState == 4/complete ist.
Lösung: ich habe die komplette Generierung des DIVs innerhalb des if(http.readyState == 4)-Blocks gesetzt,
vorher (s. weiter oben) stand dort ja nur innerHTML = http.responseText. Damit und dem Open-Parameter auf
"true" funktioniert es mit den Menüs auch im IE.
Jetzt habe ich eine andere Herausforderung - auch wieder nur für den IE. Die EventListener, die auf die
Element im Menü zutreffen, reagieren im IE einfach nicht... aber auch das wird zu lösen sein... ;)
Danke für die Hilfe / die Denkanstöße, dicon
hi,
Korrekt, daher war ich ja auch der Meinung, dass, wenn ich den http.open-Parameter "true/false" auf "false" setze,
Du redest also von einem synchronen Request?
onreadystatechange erst abgearbeitet wird, wenn status == complete.
gruß,
wahsaga