Felix Riesterer: warum kommt nix bei document.body.scrollTop?

Liebe Selfer,

ich versuche eine Art Javascript-Popup in meiner Seite zu realisieren, ohne dass sich ein weiteres Browserfenster öffnet. Dazu wird per Javascript eine in CSS absolut positionierte Tabelle in einen DIV-Bereich geschrieben. Nun soll aber die Tabelle immer in der momentanen Mitte erscheinen, egal wie weit der Anwender bereits gescrollt haben mag.

Leider muss man dazu wohl wieder auf die Unterschiede der DOM-Modelle der Browser Rücksicht nehmen... Mein Script sieht im Moment so aus:

var x_pos = y_pos = "0px";

if(window.innerWidth)
      {
      x_pos = Math.ceil((innerWidth + window.pageXOffset - 100)/2) + "px";
      y_pos = Math.ceil((innerHeight + window.pageYOffset - 80)/2) + "px";
      }
    else if(document.body.offsetWidth)
      {
      alert("AHA! IE also...\nX: " + document.body.scrollLeft.value + "px und Y: " + document.body.scrollTop.value + "px.");
      x_pos = Math.ceil((document.body.offsetWidth + document.body.scrollLeft - 100)/2) + "px";
      y_pos = Math.ceil((document.body.offsetHeight + document.body.scrollTop - 100)/2) + "px";
      }

Im Firefox wird die Position korrekt errechnet und es kommt auch kein alert(). Im IE dagegen kommt der alert() und gibt mir _immer_ "0px" für x- und y-offset an, egal wie ich auch scrolle (Seite ist lang genug, um scrollen zu können!). Wo liegt mein Denkfehler, bzw. was habe ich da vielleicht übersehen? Für mich scheint es so, als ob die Eigenschaften scrollLeft und scrollTop nicht auf meine Scrollbewegungen reagieren. Gibt es da vielleicht was Besseres als scrollTop oder scrollLeft?

Vielen Dank für Eure Hilfe!

Liebe Grüße aus Ellwangen,

Felix Riesterer.

  1. Hallo Felix,

    probiere es mal mit document.documentElement anstelle von document.body.

    Ich hatte mal ein aehnliches Problem und da kam beim scroll von document.body auch immer 0 raus, bei Verwendung von document.documentElement jedoch nicht. Habe bisher noch nicht versucht zu ergruenden, wieso das so war, daher keine Garantie, dass das was bringt...

    soleil

    1. Liebe Sonne,

      ich habe Deinen Vorschlag gleich ausprobiert... ohne den gewünschten Erfolg.

      In meinem Posting hat sich ein Fehler eingeschlichen: Es darf nicht heißen "document.body.scrollTop.value", sondern muß ohne "value" sein.

      Aber auch document.documentElement.scrollTop bleibt immer "0". :-(

      Liebe Grüße aus Ellwangen,

      Felix Riesterer.

  2. Liebe Selfer,

    ich habe mit dem IE6 was ganz verrücktes herausgefunden. Meine Seite ist im XHTML1 strict gehalten, der IE6 also im CSS1Compat-Modus. Zuerst mein Script, das verdeutlicht, was ich meine:

    var x_pos = y_pos = "0px";

    if(window.innerWidth)
          {
          x_pos = Math.ceil((window.innerWidth + window.pageXOffset - 100)/2) + "px";
          y_pos = Math.ceil((window.innerHeight + window.pageYOffset - 100)/2) + "px";
          }
        else if(document.documentElement && document.documentElement.scrollTop)
          {
          alert("element!" + document.documentElement.scrollTop);
          x_pos = Math.ceil((document.documentElement.offsetWidth + document.documentElement.scrollLeft - 100)/2) + "px";
          y_pos = Math.ceil((document.documentElement.offsetHeight + document.documentElement.scrollTop - 100)/2) + "px";
          }
        else if (document.body)
          {
          alert("body!" + document.body.scrollTop);
          x_pos = Math.ceil((document.body.offsetWidth  - 200)/2) + "px";
          y_pos = Math.ceil((document.body.offsetHeight - 200)/2) + "px";
          }

    Wenn die Seite zu Ende geladen hat, der User aber noch nix gescrollt hat, dann kommt bei Click auf einen entsprechenden onclick-Link die Meldung "body!0", was bedeutet, das der if-Zweig mit document.body gegriffen hat. Wenn der User dann aber die Seite weiter scrollt, dann kommt auf denselben Click hin die Meldung "element!253". Das bedeutet doch wohl, dass durch das Scrollen des Users der IE sein DOM verändert, aus document.body mal eben document.documentElement macht (warum das _jetzt_ erst?!?), um mir dann mit der Eigenschaft scrollTop einen Wert != 0 ausgeben zu können.

    Empfindet das außer mir noch jemand als völlig idiotisch und durchgeknallt?

    Liebe Grüße aus Ellwangen,

    Felix Riesterer.

    1. Liebe Selfer,

      mittlerweile kann ich mit diesen völlig bescheuerten Marotten relativ gut leben... Mein Script hat nur noch einen Makel: Ich kann die verfügbare Höhe des Browserfensters nicht sinnvoll auslesen und daher die Grafik auch nicht mittig in Abhängigkeit des Weiterscrollens anzeigen. Die beiden Eigenschaften scrollHeight bzw. offsetHeight geben mir immer ein und denselben Wert, egal wie ich das Fenster resize. So ein Affenzirkus!

      Mir scheint, diese Werte entsprechen der tatsächlichen Länge des bodys, weniger der _sichtbaren_ Länge. Das macht der IE einfach nicht! Da nützt auch kein clientHeight, weil das genau denselben Mist liefert.

      Ich glaube, diese Frage wurde hier schon so oft gestellt, aber es gibt wohl keine _verlässliche_ Möglichkeit, die Höhe eines IE-Fensters zu bestimmen...

      Liebe Grüße aus Ellwangen,

      Felix Riesterer.

      1. Liebe Selfer,

        habe jetzt einen Würg-around gefunden, der _sehr_ würgig ist.

        Wenn die Seite im IE (und nur für den mache ich den ganzen Zirkus hier!) frisch geladen hat und im CSS1Compat-Modus ist, dann gibt es trotzdem noch document.body.scrollTop - also nehme ich es. Sobald der Anwender von der Nullposition wegscrollt, verändert sich das DOM: aus document.body wird document.documentElement. Wenn der Anwender wieder zurück bis zum Anschlag scrollt, dann verändert sich auch das DOM wieder zurück: jetzt ist document.documentElement wieder document.body. - Ich schrieb es ja schon: Affenzirkus!!!

        Aber: Wenn das document.documentElement existiert, dann kann man Objekte im Browserfenster zentriert ausrichten! Dann geht das plötzlich! Also folgt der Weisheit Schluss, dass man eben Scrollen _muss_! Und das tue ich mit scrollBy(0, 1). Damit wird um genau einen Pixel nach unten gescrollt und schwuppdiwupp habe ich keine Probleme mehr. Und dem Anwender tut dieses Ge-scrolle um einen Pixel nicht weh.

        Eben ein echter super-WÜRG-around *blärch!* SCNR

        Zur Krönung mein trauriges Script zum Abgewöhnen:

        function anzeigen(bild_url, Untertitel)
           {
           var html_code = '';
           var x_pos = "0px";
           var y_pos = "0px";

        if(window.innerWidth)
              {
              x_pos = Math.ceil((window.innerWidth - 150)/2 + window.pageXOffset) + "px";
              y_pos = Math.ceil((window.innerHeight - 150)/2 + window.pageYOffset) + "px";
              }
            else if(document.documentElement && document.documentElement.scrollTop)
              {
              x_pos = Math.ceil((document.documentElement.offsetWidth - 150)/2 + document.documentElement.scrollLeft) + "px";
              y_pos = Math.ceil((document.documentElement.offsetHeight -150)/2 + document.documentElement.scrollTop) + "px";
              }
            else if (document.body)
              {
              window.scrollBy(0, 1);   // Hier wird gescrollt. Danach gibt es document.documentElement,
              return anzeigen(bild_url, Untertitel); // also Funktion beenden und neu aufrufen!
              }

        html_code += '<table border="0" cellpadding="0" cellspacing="0"><tr>';
           html_code += '<td width="120" height="120" style="text-align: center;">';
           html_code += '<img id="anzeigebild" alt="kein Bild?" />';
           html_code += '</td></tr><tr><td class="zentriert">' + Untertitel + '</td></tr></table>';

        document.getElementById("mein_bild").innerHTML = html_code;
           document.getElementById("mein_bild").style.display = "block";
           document.getElementById("mein_bild").style.position = "absolute";
           document.getElementById("mein_bild").style.top = y_pos;
           document.getElementById("mein_bild").style.left = x_pos;
           document.getElementById("anzeigebild").src = bild_url;

        return false;
           }

        Im HTML-body steht dann irgendwo ein Link <a href="wo/mein/bild/ist" onclick="return anzeigen(this.href, 'Untertitel des Bildes');"> ... </a>

        Liebe Grüße aus Ellwangen,

        Felix Riesterer.

        1. window.scrollBy(0, 1);   // Hier wird gescrollt. Danach gibt es document.documentElement,
                return anzeigen(bild_url, Untertitel); // also Funktion beenden und neu aufrufen!

          Damit erzeugst du eine endlosschleife und der Browser sollte dies auch melden (too deep recursion oder so ähnlich)

          document.getElementById("mein_bild").innerHTML = html_code;
             document.getElementById("mein_bild").style.display = "block";
             document.getElementById("mein_bild").style.position = "absolute";
             document.getElementById("mein_bild").style.top = y_pos;
             document.getElementById("mein_bild").style.left = x_pos;

          document.getElement... Funktionen solltest du möglichst selten verwenden, sie sind relativ langsam.

          var mein_bild = document.getElementById("mein_bild");

          und dann mit der Variabel weiter arbeiten.

          Struppi.

          1. window.scrollBy(0, 1);   // Hier wird gescrollt. Danach gibt es document.documentElement,
                  return anzeigen(bild_url, Untertitel); // also Funktion beenden und neu aufrufen!

            Damit erzeugst du eine endlosschleife und der Browser sollte dies auch melden (too deep recursion oder so ähnlich)

            Kommentare beachten. Und dann den Code ein weiteres mal ansehen.

            1. Hallo Anonymous

              window.scrollBy(0, 1);   // Hier wird gescrollt. Danach gibt es document.documentElement,
                    return anzeigen(bild_url, Untertitel); // also Funktion beenden und neu aufrufen!

              Damit erzeugst du eine endlosschleife und der Browser sollte dies auch melden (too deep recursion oder so ähnlich)

              Kommentare beachten. Und dann den Code ein weiteres mal ansehen.

              und? Ist das keine endlosschleife?

              um das return auszuführen, muss die funktion erst wissen, was anzeigen() zurückgibt, also wird anzeigen() aufgerufen um aber zu Wissen was es zurück gibt wird wird anzeigen() aufgerufen um aber zu Wissen was es zurück gibt wird .....

              usw.

              Natürlich nur, wenn die Seite nicht im strict Modus ist

              Struppi.

              1. window.scrollBy(0, 1);   // Hier wird gescrollt. Danach gibt es document.documentElement,
                      return anzeigen(bild_url, Untertitel); // also Funktion beenden und neu aufrufen!

                Damit erzeugst du eine endlosschleife und der Browser sollte dies auch melden (too deep recursion oder so ähnlich)

                Kommentare beachten. Und dann den Code ein weiteres mal ansehen.

                und? Ist das keine endlosschleife?

                Nein. Zitat:
                "Hier wird gescrollt. Danach gibt es document.documentElement,"
                In der zweiten Rekursions-Stufe wird ein anderer Zweig des If-Gebildes gewählt. Und damit ist die Rekursion in der zweiten Stufe beendet.

                1. und? Ist das keine endlosschleife?

                  Nein. Zitat:
                  "Hier wird gescrollt. Danach gibt es document.documentElement,"
                  In der zweiten Rekursions-Stufe wird ein anderer Zweig des If-Gebildes gewählt. Und damit ist die Rekursion in der zweiten Stufe beendet.

                  und das ist falsch.
                  document.documentElement gibt es nur im strict Modus, aber nicht erst nach dem scrollen. Im Standardmode hat er eine endlosrekursion programmiert.

                  Struppi.

                  1. Lieber Struppi,

                    und das ist falsch.
                    document.documentElement gibt es nur im strict Modus, aber nicht erst nach dem scrollen. Im Standardmode hat er eine endlosrekursion programmiert.

                    Da ich keinen älteren IE auf meinem System habe, ist mir dieser Denkfehler passiert. Ich muss mein Konzept für IE<6 nochmal gründlich überdenken.

                    Danke für Eure Kritik! Dat Dingens wird überarbeitet!

                    Liebe Grüße aus Ellwangen,

                    Felix Riesterer.

                    1. Da ich keinen älteren IE auf meinem System habe, ist mir dieser Denkfehler passiert. Ich muss mein Konzept für IE<6 nochmal gründlich überdenken.

                      Du kannst auch im IE 6 den Quirksmode erzeugen.

                      Struppi.

                      1. Lieber Struppi,

                        Du kannst auch im IE 6 den Quirksmode erzeugen.

                        Wäre das anzuraten? Wo doch die ganze Website im CSS1Compat-Modus angezeigt wird... Ich dachte, ich setze den Compat-Modus für aktuelle Browser voraus, biete veralteten Browsern aber dennoch die Möglichkeit, das beabsichtigte Layout einigermaßen darzustellen - meinetwegen eben auch quirksig.

                        Nur aber wegen dieser Javscript-Spielerei, die ein Bild anzeigen soll, das ohne JS sonst per Link direkt erreichbar wäre, möchte ich den Modus deshalb eigentlich nicht ändern. Und wenn dann doch, wie?

                        Liebe Grüße aus Ellwangen,

                        Felix Riesterer.

                        1. Lieber Struppi,

                          hier nun meine verbesserte Version. Vielen Dank für Deine wertvollen Hinweise!! Werde es so bald als möglich mit einem älteren IE ausprobieren. :-)

                            
                          function anzeigen(bildobjekt)  
                             {  
                             var JS_popup = document.getElementById("JS_popup");  
                             var untertitel = bildobjekt[0];  
                             var bild_url = bildobjekt[1];  
                             var html_code = '';  
                             var x_pos = y_pos = "0px";  
                            
                             if(window.innerWidth)  
                                {  
                                x_pos = Math.ceil((window.innerWidth - 150)/2 + window.pageXOffset) + "px";  
                                y_pos = Math.ceil((window.innerHeight - 200)/2 + window.pageYOffset) + "px";  
                                }  
                              else  
                                {  
                                var IE = (document.compatMode && document.compatMode == "CSS1Compat") ? document.documentElement : document.body || null;  
                            
                                x_pos = Math.ceil((IE.offsetWidth - 150)/2 + IE.scrollLeft) + "px";  
                                y_pos = Math.ceil((IE.offsetHeight -200)/2 + IE.scrollTop) + "px";  
                                }  
                            
                             html_code += '<table border="0" cellpadding="0" cellspacing="0"><tr>';  
                             html_code += '<td width="120" height="120" style="text-align: center;">';  
                             html_code += '<img id="anzeigebild" alt="kein Bild?" />';  
                             html_code += '</td></tr><tr><td class="zentriert">' + untertitel + '</td></tr></table>';  
                             html_code += '<img id="x_beenden" alt="schlie&szlig;en" />';  
                            
                             JS_popup.innerHTML = html_code;  
                             JS_popup.style.display = "block";  
                             JS_popup.style.position = "absolute";  
                             JS_popup.style.top = y_pos;  
                             JS_popup.style.left = x_pos;  
                            
                             document.getElementById("x_beenden").src = "images/x-beenden.gif";  
                             document.getElementById("anzeigebild").src = bild_url;  
                            
                             return false;  
                             }  
                          
                          

                          Im HTML-body steht dann ein
                             <div id="JS_popup" style="display: none;"></div>

                          und ein Bild-Link mit Popup-Effekt funktioniert dann so:
                             <a href="wo/mein/bild/ist.gif" onclick="return anzeigen(["Das steht unter dem Bild", this.href])">Link-Text</a>

                          Liebe Grüße aus Ellwangen,

                          Felix Riesterer.

                        2. Du kannst auch im IE 6 den Quirksmode erzeugen.
                          Wäre das anzuraten? Wo doch die ganze Website im CSS1Compat-Modus angezeigt wird... Ich dachte, ich setze den Compat-Modus für aktuelle Browser voraus, biete veralteten Browsern aber dennoch die Möglichkeit, das beabsichtigte Layout einigermaßen darzustellen - meinetwegen eben auch quirksig.

                          nein, es ist natürlich nicht unbedingt sinvoll, mir ging's nur darum, dass du durchaus diesen Modus auch mit dem IE 6 testen kannst. Das Problem war ja auch, dass du im Quirks modus eine endlosschleife produziert hast.

                          Nur aber wegen dieser Javscript-Spielerei, die ein Bild anzeigen soll, das ohne JS sonst per Link direkt erreichbar wäre, möchte ich den Modus deshalb eigentlich nicht ändern. Und wenn dann doch, wie?

                          einen entsprechenden DOCTYPE setzen

                          Struppi.

    2. Wenn die Seite zu Ende geladen hat, der User aber noch nix gescrollt hat, dann kommt bei Click auf einen entsprechenden onclick-Link die Meldung "body!0", was bedeutet, das der if-Zweig mit document.body gegriffen hat. Wenn der User dann aber die Seite weiter scrollt, dann kommt auf denselben Click hin die Meldung "element!253". Das bedeutet doch wohl, dass durch das Scrollen des Users der IE sein DOM verändert, aus document.body mal eben document.documentElement macht (warum das _jetzt_ erst?!?), um mir dann mit der Eigenschaft scrollTop einen Wert != 0 ausgeben zu können.

      Weil du in der if Bedingung auf 0, Leerstring, undefined oder null prüfst und im zutreffenden Fall in den body zweig gehst.

      else if(document.documentElement && document.documentElement.scrollTop)

      heißt: wenn document.documentElement ungleich 0, null, Leerstring oder undefined ist und document.documentElement.scrollTop ungleich 0, null, Leerstring oder undefined, dann ...

      Empfindet das außer mir noch jemand als völlig idiotisch und durchgeknallt?

      Nein, es ist völlig logisch und von dir so programmiert.

      Struppi.

    3. if(window.innerWidth)
            {
            x_pos = Math.ceil((window.innerWidth + window.pageXOffset - 100)/2) + "px";
            y_pos = Math.ceil((window.innerHeight + window.pageYOffset - 100)/2) + "px";
            }
          else if(document.documentElement && document.documentElement.scrollTop)
            {
            alert("element!" + document.documentElement.scrollTop);
            x_pos = Math.ceil((document.documentElement.offsetWidth + document.documentElement.scrollLeft - 100)/2) + "px";
            y_pos = Math.ceil((document.documentElement.offsetHeight + document.documentElement.scrollTop - 100)/2) + "px";
            }
          else if (document.body)

      Und noch was.
      Du solltest nicht auf document.body oder element prüfen, den Fehler hab ich am Anfang auch gemacht, sondern explizit auf den Modus. Ich benutze dazu folgende Funktion:

      /////////////////////////////////////////////////
      // Der IE hat 2 verschiedene Objekte
      // für den strict und quirks Mode.
      function getBody(w)
      /////////////////////////////////////////////////
      {
          return (w.document.compatMode && w.document.compatMode == "CSS1Compat") ?
          w.document.documentElement : w.document.body || null;
      }

      und dann:

      ...
      var obj = getBody(win);
      s.left = obj.scrollLeft;
      s.top = obj.scrollTop;
      ...

      Struppi.