div an Mauskoordinaten ausrichten bei position:relative für body
Felix Riesterer
- javascript
1 molily
Liebe Mitlesende,
irgendwie will es mir nicht gelingen, ein HTML-Element so absolut zu positionieren, dass es sich unterhalb des Mauszeigers mitbewegt. Das Problem ist eine Layoutregel für das <body>-Element, die position:relative einstellt und damit Schwierigkeiten macht. Ist der Bereich, den das <body>-Element "bedeckt" schmaler, als die zur Verfügung stehende Breite, wird der Effekt umso stärker sichtbar, je mehr ungenutzte Anzeigefläche neben der <body>-Fläche verbleibt:
Hier ein jsFiddle zur Veranschaulichung. Wenn man die mittlere Trennlinie nach links verschiebt, um das "Result"-Fenster zu verbreitern, dann kann man auf breiten Monitoren bei maximiertem Browserfenster den Effekt sehr schön sehen.
Was muss ich tun, um den Effekt zu neutralisieren? Ich war so stolz, dass ich auf ein "Wrapper-Div" verzichten konnte und trotzdem meine visuelle Strukturierung mit CSS nach Wunsch umsetzen konnte... und jetzt das!
Liebe Grüße,
Felix Riesterer.
Hallo,
Willst du das wirklich ohne jQuery machen? Gibt es einen Grund, warum du Low-Level arbeitest (sogar mit DOM-0-Event-Handling)? Programmierst du für alte IEs?
Wenn du jQuery nicht verwenden willst, kannst du dich durch die Untiefen von jQuery wühlen, um dich in die Offset-Problematik einzuarbeiten:
2.x-Branch (neuere Browser): https://github.com/jquery/jquery/blob/master/src/offset.js
1.x-Branch (oldIE-Unterstützung): https://github.com/jquery/jquery/blob/1.x-master/src/offset.js
Siehe
http://api.jquery.com/offset/
http://api.jquery.com/position/
http://api.jquery.com/offsetParent/
Vom Ablauf her: Mausposition relativ zum Dokument (Viewport) holen, den Offset des ERSTEN offsetParents relativ zum Dokument berechnen (getBoundingClientRect mit Fallback) und davon substrahieren, dann hast du die Werte für left und top. Denn left und top beziehen sich ja auf dieses erste Element mit position: relative/absolute/fixed. Das ist hier anscheinend das main-Element.
Das sind in jQuery vielleicht vier Zeilen, ohne jQueryist das eine Menge Code, wenn man es richtig und für alte Browser machen will.
Mathias
Willst du das wirklich ohne jQuery machen?
Beispiel mit jQuery 1.11
Vollbild
Nur am Rande, ich hänge Overlays immer als Kinder von body ein, denn das macht das Positionieren einfacher und es kommt nicht zu hässlichen Effekten, wenn der Rand der Tabelle erreicht wird.
Lieber molily,
Schauen wir einmal in meinem FF (Version 30.0 Linux):
Wenn das Fenster so schmal ist, dass der Anzeigebereich nahezu völlig vom Inhalt des <body>-Elements bedeckt ist, dann "passt" die Position des Overlays wie gewünscht.
Ist der Viewport allerdings so breit, dass neben dem vom <body>-Inhalt bedeckten Bereich noch "Ränder" stehen bleiben, so stimmt die horizontale Position eben auch mit jQuery nicht mehr.
Ergo: jQuery kann mein Problem nicht lösen.
Auch hier kann man mein Problem im FF unter Linux nachvollziehen:
Wieder kann man sehen, dass bei einem schmalen Viewport die Position des Overlays "passt".
Auch in diesem Screenshot erkennt man, dass bei verbleibenden freien Flächen neben dem vom Inhalt des <body>-Elements bedeckten Bereich die horizontale Positionierung scheitert.
Leider kann jQuery auch hier anscheinend mein Problem nicht lösen.
Nur am Rande, ich hänge Overlays immer als Kinder von body ein, denn das macht das Positionieren einfacher und es kommt nicht zu hässlichen Effekten, wenn der Rand der Tabelle erreicht wird.
Es erscheint völlig unerheblich, ob das Overlay ein Kind von <body> oder ein Kindeskind davon ist, da die Layout-Eigenschaft position:relative des <body>-Elementes selbst das offensichtliche Problem bereitet. Ich kann das Overlay ja kein Kind von <html> sein lassen...
Es wäre interessant zu wissen, warum das <body>-Element bei position:relative kein offsetParent definiert. Nach meinem Verständnis wäre die Logik fehlerhaft, wenn <body> eine solche Layout-Eigenschaft erhalten kann, aber anders als seine Nachfahren-Elemente gerade nicht deren Verhalten hinsichtlich der offsetParent-Eigenschaften imitiert.
Was sagst Du dazu?
Liebe Grüße,
Felix Riesterer.
Hallo,
Ich kann den Fehler mit Firefox 30 unter Mac OS nicht nachvollziehen. Firefox-Bug? Kaputter Firefox-Build für Linux?
Ergo: jQuery kann mein Problem nicht lösen.
Das hat mit jQuery oder nicht auch gar nichts zu tun, sondern mit der Logik, die ich vorgeschlagen habe und beispielhaft mit jQuery umgesetzt habe. Es muss also etwas an der Logik falsch sein, oder am Browser, oder in der Implementierung von jQuery. Ich sehe aber nicht, was in der Logik zum von dir beobachteten Verhalten führen könnte.
Es wäre interessant zu wissen, warum das <body>-Element bei position:relative kein offsetParent definiert.
Es ist nun einmal so definiert.
http://dev.w3.org/csswg/cssom-view/#dom-htmlelement-offsetparent
Es ist keine gute Idee, Elemente mit Sonderbedeutung in CSS und JavaScript (html, body) wie normale, frei formatierbare Elemente zu behandeln, nur damit man ein Wrapper-Div einsparen kann. Das wird hier gerne gepredigt, in der Praxis spricht vieles dagegen.
Mathias
Lieber molily,
http://dev.w3.org/csswg/cssom-view/#dom-htmlelement-offsetparent
Es ist keine gute Idee, Elemente mit Sonderbedeutung in CSS und JavaScript (html, body) wie normale, frei formatierbare Elemente zu behandeln, nur damit man ein Wrapper-Div einsparen kann. Das wird hier gerne gepredigt, in der Praxis spricht vieles dagegen.
na, das ist doch mal eine sehr weiterhelfende Nachricht! Vielen herzlichen Dank dafür! Da muss ich gleich einmal Gunnars Meinung dazu lesen, denn ich meine mich zu entsinnen, dass vor allem er dafür plädierte, auch <body> und <html> mit Style-Rules zu belegen, um das Markup "sauber" zu halten...
Woher sollte ein Hobbyist soetwas sonst erfahren, wenn nicht hier im Forum!
Liebe Grüße,
Felix Riesterer.
Hi,
Ist der Viewport allerdings so breit, dass neben dem vom <body>-Inhalt bedeckten Bereich noch "Ränder" stehen bleiben, so stimmt die horizontale Position eben auch mit jQuery nicht mehr.
Das kann ich im Firefox 30 auf Win 7 nicht nachvollziehen – und ebenso in keinem anderen Browser.
Ergo: jQuery kann mein Problem nicht lösen.
Ergo: FF auf Linux scheint hier buggy zu sein.
MfG ChrisB
Lieber ChrisB,
Ergo: FF auf Linux scheint hier buggy zu sein.
oder durch ein Plugin (Firebug? FlashBlock?) "verbogen" zu werden. Muss das mal testen.
Ganz herzlichen Dank für's Testen!
Liebe Grüße,
Felix Riesterer.
Lieber ChrisB,
Ergo: FF auf Linux scheint hier buggy zu sein.
habe gerade Chromium extra deswegen installiert - identisches Problem! Was nun? Woran könnte es noch liegen?
Mir ist es ja etwas unheimlich, dass sich Chromium an der FF-History bedient - warum darf er das? Mal in den Settings schauen...
Liebe Grüße,
Felix Riesterer.
Welches Linux verwendest du? Ubuntu? Welche Version?
Mathias
Lieber molily,
Welches Linux verwendest du? Ubuntu? Welche Version?
eine extrem spannende Frage! Ich habe getestet und bin echt vom Glauben abgefallen!!
Gerade teste ich auf Ubuntu 14.04 (Kernel 3.13). Auf meinem Laptop mit Ubuntu 12.04 (Kernel 3.13) tritt dasselbe Phänomen auf. Auf einem anderen PC mit Ubuntu 12.04 (Übrigens alles 64bit!) dagegen nicht, auf dem läuft auch der 3.11-er Kernel. Nun habe ich dort auch den neuestmöglichen Kernel der Distribution (linux-generic-lts-trusty, 3.13) installiert, und prompt beobachte ich dort nach einem Reboot in den gerade installierten neuen Kernel dasselbe Phänomen. Reboote ich in den vorherigen Kernel bleibt das Phänomen erhalten(?!?).
So. Jetzt weiß ich definitiv nicht mehr weiter. Jetzt brauche ich einen wirklich guten Rat, was ich da gerade gesehen haben soll und woran das wohl liegen könnte... und wie ich aus diesem Dilemma wieder heraus komme.
Liebe Grüße,
Felix Riesterer.
Lieber molily,
gerade habe ich ein Ubuntu 12.04.4 (Kernel 3.5) als Live-CD (von Memory-Stick) gebootet. Das Problem bleibt erhalten. Ich verstehe es einfach nicht.
Liebe Grüße,
Felix Riesterer.
Om nah hoo pez nyeetz, Felix Riesterer!
gerade habe ich ein Ubuntu 12.04.4 (Kernel 3.5) als Live-CD (von Memory-Stick) gebootet. Das Problem bleibt erhalten. Ich verstehe es einfach nicht.
Unter Umständen ist es auch die js-fiddle-Umgebung. Denn ich erhalte bei http://jsfiddle.net/AP4GK/2/ FF30/Win7 dasselbe unerwünschte Verhalten (großer Abstand zwischen Mauszeiger und Tooltip) im Vollbild hingegen nicht.
FF 30 jsfiddlegesamt: kaputt Vollbild: i.O.
Chr 35 kaputt kaputt
Op 12 kaputt kaputt
Op 22 kaputt kaputt
IE 11 kaputt kaputt
Matthias
Lieber Matthias Apsel,
FF 30 jsfiddlegesamt: kaputt Vollbild: i.O.
Chr 35 kaputt kaputt
Op 12 kaputt kaputt
Op 22 kaputt kaputt
IE 11 kaputt kaputt
das muss doch heißen, dass jQuery hier nur unzureichend funktioniert, oder interpretiere ich das falsch?
Und wie kann man das ursprüngliche Problem (<body style="position:relative">
) mittels JS "abfangen"?
Liebe Grüße,
Felix Riesterer.
Om nah hoo pez nyeetz, Felix Riesterer!
FF 30 jsfiddlegesamt: kaputt Vollbild: i.O.
Chr 35 kaputt kaputt
Op 12 kaputt kaputt
Op 22 kaputt kaputt
IE 11 kaputt kaputtdas muss doch heißen, dass jQuery hier nur unzureichend funktioniert, oder interpretiere ich das falsch?
Es geht beim Zoomen bzw. Resizen kaputt. Wenn man danach die Seite neu lädt (F5), ist die Darstellung in Ordnung.
Matthias
das muss doch heißen, dass jQuery hier nur unzureichend funktioniert, oder interpretiere ich das falsch?
Es geht beim Zoomen bzw. Resizen kaputt. Wenn man danach die Seite neu lädt (F5), ist die Darstellung in Ordnung.
Ihr habt schon gesehen, dass in meinem Beispielscript die Offset-Position des main-Elements gecacht wird?
Natürlich funktioniert das nicht mehr, wenn resized wird. Das Script sollte nur das Prinzip veranschaulichen. Entweder man deaktiviert diesen Cache oder man aktualisiert ihn korrekt in einem resize-Handler. Ich würde letzteres bevorzugen, weil .offsetParent().offset() sehr teure Operationen sind, die man nicht in jedem mousemove aufrufen will.
Um das Problem zu vermeiden, hängt man Overlay-Elemente direkt ans body, sodass obige Operation wegfällt und direkt e.pageX/pageY an top und left gegeben werden können. Wie gesagt.
jQuery kocht auch nur mit Wasser, den Quellcode habe ich ja verlinkt. Ansonsten gilt: garbage in, garbage out.
Mathias
Lieber molily,
oder man aktualisiert ihn korrekt in einem resize-Handler. Ich würde letzteres bevorzugen, weil .offsetParent().offset() sehr teure Operationen sind, die man nicht in jedem mousemove aufrufen will.
der Hinweis ist sehr hilfreich! Damit klappt es dann auch wunderbar!
Um das Problem zu vermeiden, hängt man Overlay-Elemente direkt ans body, sodass obige Operation wegfällt und direkt e.pageX/pageY an top und left gegeben werden können. Wie gesagt.
Mal sehen, ob sich das immer beherzigen lässt...
Liebe Grüße,
Felix Riesterer.
Lieber Matthias Apsel,
Es geht beim Zoomen bzw. Resizen kaputt. Wenn man danach die Seite neu lädt (F5), ist die Darstellung in Ordnung.
AHA!!! DAS ist der Hinweis, nach dem ich gesucht hatte. Ganz herzlichen Dank!!
Liebe Grüße,
Felix Riesterer.
Lieber molily,
Willst du das wirklich ohne jQuery machen?
ja, denn jQuery hat mich exact in solchen Fällen in der Vergangen regelmäßig sitzen lassen.
Gibt es einen Grund, warum du Low-Level arbeitest (sogar mit DOM-0-Event-Handling)?
Einfachheit des Codes in einer Weise, dass ich alle Aspekte selbst überblicke. Dieses jQuery mag ja mittlerweile Standard geworden sein, jedoch benötige ich seine Fähigkeiten nur marginal und kann vieles, das jQuery vielleicht einfacher machen könnte, mit kleinen Funktionen selbst nachbilden. Auf jQuery-Komfort muss ich deshalb nur wenig verzichten und habe dadurch alles selbst in der Hand.
Man könnte mir jetzt vorwerfen, dass ich an dem "not invented here"-Syndrom leide oder zumindest gegen dieses Paradigma verstoße, jedoch werden wir gleich sehen, dass jQuery eben keine Lösung für mein Problem bieten kann.
Dieses DOM-0-Event-Handling ist für mich einfacher handzuhaben, da ich in den Umgebungen, in denen ich Eventhandling benötige, jeweils nur einen Handler einsetzen muss. Das fortgeschrittenere Event-Handling, bei dem ich mehrere Listener an ein Event binden kann, verkompliziert meine Anwendungsfälle anscheinend nur.
Programmierst du für alte IEs?
Nein, jedenfalls nicht absichtlich. Vielleicht habe ich nur aus älteren Projekten meine Code-Schnipsel neu organisiert, sodass ältere Browser (zumindest teilweise) mit unterstützt würden...
Wenn du jQuery nicht verwenden willst, kannst du dich durch die Untiefen von jQuery wühlen, um dich in die Offset-Problematik einzuarbeiten:
2.x-Branch (neuere Browser): https://github.com/jquery/jquery/blob/master/src/offset.js
1.x-Branch (oldIE-Unterstützung): https://github.com/jquery/jquery/blob/1.x-master/src/offset.js
Meine Tests haben ergeben, dass jQuery das problem in meinem Beispiel nicht lösen kann.
Vom Ablauf her:
Der ist in meinem Beispiel im Prinzip genau so, wenn auch gerade anders herum, gelöst: Ich klettere den Baum bis zu <body> hinauf, indem ich immer wieder offset.Parent als Knoten "nach oben" verwende, um dann dessen Eigenschaften offsetTop und offsetLeft von meinen Koordinaten (initialisiert mit scrollTop und scrollLeft) abzuziehen.
Das sind in jQuery vielleicht vier Zeilen, ohne jQueryist das eine Menge Code, wenn man es richtig und für alte Browser machen will.
Deine Beispiel-Links führen unter einem aktuellen FireFox unter Linux nicht zu dem von mir gewünschten Verhalten, sondern im Gegenteil zu dem von mir als Problemstellung bereits dokumentierten. Aber darauf gehe ich in Deinem zweiten Posting genauer ein.
Liebe Grüße,
Felix Riesterer.
Hallo,
Einfachheit des Codes in einer Weise, dass ich alle Aspekte selbst überblicke.
Dann versuche einmal den jQuery-Code zu verstehen, denn dort stecken 8 Jahre Arbeit, Erfahrung und Optimierung drin.
Dieses jQuery mag ja mittlerweile Standard geworden sein, jedoch benötige ich seine Fähigkeiten nur marginal
*Alles*, was du da machst, ist Kernaufgabe von jQuery und anderen DOM-Bibliotheken.
kann vieles, das jQuery vielleicht einfacher machen könnte, mit kleinen Funktionen selbst nachbilden.
Natürlich, in der Regel auf Kosten von Flexibilität, Robustheit, Kompatibilität und u.U. Performance.
Das fortgeschrittenere Event-Handling, bei dem ich mehrere Listener an ein Event binden kann, verkompliziert meine Anwendungsfälle anscheinend nur.
Ich finde nichts daran komplizierter, element.addEventListener('click', handler)
anstatt element.onclick = handler
zu schreiben. Du kannst ferner viel Code dadurch sparen, nicht mit IE 8 kompatibel zu sein.
Meine Tests haben ergeben, dass jQuery das problem in meinem Beispiel nicht lösen kann.
jQuery besitzt die robustesten Funktionen für solche Zwecke. Wenn sich das Problem damit nicht lösen lässt, dann mit eigenen höchstwahrscheinlich auch nicht.
Der ist in meinem Beispiel im Prinzip genau so, wenn auch gerade anders herum, gelöst: Ich klettere den Baum bis zu <body> hinauf, indem ich immer wieder offset.Parent als Knoten "nach oben" verwende, um dann dessen Eigenschaften offsetTop und offsetLeft von meinen Koordinaten (initialisiert mit scrollTop und scrollLeft) abzuziehen.
Das ist unnötig kompliziert, soweit ich das sehe. getBoundingClientRect existiert und offsetParent ist fehleranfällig. Über e.pageX und e.pageY lässt ferner sich das Einrechnen der Scrollposition vermeiden.
Grüße
Mathias