Nebeneinander liegenden DIVs die gleiche Höhe geben
Ich glaube jeder, der sich schon einmal mit Webdesign beschäftigt hat, stand schon mindestens ein mal vor der Frage, wie man bei zwei oder mehreren nebeneinander liegenden DIV-Containern dafür sorgen kann, dass diese die gleiche Höhe haben. Meistens stellt sich das Problem genau dann, wenn man die Höhe nicht mit absoluten (Pixel-) Werten festlegen will. Natürlich gibt es gerade für das Layout verschiedene Mittel, dies zu realisieren. Das meist verbreitete dürfte wohl die Faux-Columns-Technik sein, die sich eines HIntergrundbildes bedient, um – wie der Name schon sagt – falsche Spalten anzuzeigen. Dies kann aber auch komplett ohne Bilder, mehrere Layout-Spalten einfach mit CSS umsetzen.
In meinem Privat-Projekt mit dem Arbeitstitel “ein WordPress-Theme für meine eigenen Bedüfnisse erstellen” half mir das aber nicht wirklich weiter. Denn ich wollte die DIV-Container mit der gleichen Höhe nicht für das Seitenlayout einsetzen. Mir ging es darum, Bilder, die innerhalb eines DIV-Containers liegen, anzuzeigen und eben jenen Containern die gleiche Höhe zu geben. Im Folgenden werde ich meine Problemstellung ein wenig erörtern, die Lösungsansätze, die ich probiert habe erklären – und sofern ich es nachvollziehen kann auch, warum diese nicht funktioniert haben. Am Ende werde ich meine Lösung zeigen. Wen das ganze “drumherum” nicht interessiert, der kann direkt zur Lösung springen.
Das Problem
In meinem zukünftigen WordPress-Theme will ich in der Sidebar eine Fotovorschau zeigen. Die Bilder sollen aber nicht nur plump angezeigt werden und mit einem Link zum entsprechenden Beitrag versehen sein. Ich möchte, dass bei den Bildern ein “Hover-Effekt” angewendet wird. Da die nebeneinander liegenden Bilder – oder genauer gesagt: deren umgebene Container mit dem Hover-Effekt – entsprechend der Browserfenstergröße eine relative Breite haben, ist die Höhe abhängig von der (skalierten) Bildhöhe. Ich wollte aber, dass die Höhen der nebeneinander liegenden Container immer identisch ist.
Die Rahmenbedingungen
Wie in der Problembeschreibung kurz angerissen, soll die Bildbreite relativ sein. Das hat den Vorteil, dass die Darstellung nicht durch verschiedene Browserfensterbreiten “zerschossen” wird. Denn die Sidebar, die die Bildvorschau enthält, soll immer ein Viertel der Bildschirmbreite einnehmen. Da ich nicht für jeden Einzellfall eine entsprechende CSS-Definition schreiben will (was durchaus möglich gewesen wäre), musste ich die Breite der Bilder also ebenfalls relativ festlegen.
Das grobe HTML-Gerüst
Begonnen habe ich das Layout mit diesem einfachen Aufbau:
6 7 8 | if ($(this).innerHeight() > rowHeight) { rowHeight = $(this).innerHeight(); } |
Das würde etwa so aussehen (zur besseren Unterscheidung habe ich den DIV-Containern unterschiedliche Hintergrundfarben gegeben):


Einbauen des Hover-Effektes
Nun möchte ich, dass der “Infotext” nicht immer, sondern nur beim Hovern angezeigt wird. Mit dem Infotext soll auch der Hintergrund dieses Containers angezeigt werden und das Bild “überlagern”. Dazu muss ich folgende Ergänzung im CSS machen:
9 | }); |
Um den Hover-Effekt dann auszuüben, wenn über das Bild gehovert wird, muss ich noch folgenden Eintrag im CSS machen:
10 11 | $(this).children('.picture.item').each(function() { $(this).height(rowHeight + 'px'); |
Das sieht dann so aus (um den Hover-Effekt anzuzeigen, mit der Maus über das Bild fahren):
Den Inhalt des IFrame in einem neuen Fenster öffnen (z.B. wenn es nicht angezeigt wird).
Den Hover-Container anpassen
Nun möchte ich aber, dass der “Infotext” nicht unterhalb des Bildes steht, sondern das Bilder überlagert. Der Infotext-Container soll die volle Fläche des Bilder überlagern. Das stylen des Textes nehme ich für die hier gezeigte Beispiele aber nicht vor.
Ich passte also nun den Infotext-Container wie folgt an:
12 13 14 15 16 | $(this).find('div img.picture').each(function() { marginHeight = ((rowHeight - $(this).height()) / 2); $(this).css('margin-top',marginHeight + 'px'); }); }); |
Ich muss hier mit position:absolute;
arbeiten, da sonst keine “Überlagerung” möglich ist und ich auch nicht die Höhe und Breite auf 100% setzen kann. Da sich aber die relativen Werte für Höhe und Breite immer auf das nächst höchste Element mit der Angabe position:relative
bezieht, welches im Zweifel immer der body
ist, muss ich dem Container, der das Bild und den Infotext enthält, diese Defintion mitgeben:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | $(window).load(function() { $('.picture-row').parent('.sidebar-contents').css('overflow-y','scroll'); $('.picture-row').each(function() { var rowHeight = 0; $(this).children('.picture.item').each(function() { if ($(this).innerHeight() > rowHeight) { rowHeight = $(this).innerHeight(); } }); $(this).children('.picture.item').each(function() { $(this).height(rowHeight + 'px'); $(this).find('div img.picture').each(function() { marginHeight = ((rowHeight - $(this).height()) / 2); $(this).css('margin-top',marginHeight + 'px'); }); // Ende div img.picture-each }); // Ende .picture.item-each }); // Ende .picture-row-each }); // Ende window.load |
Dies resultiert dann in diesem aussehen:
Den Inhalt des IFrame in einem neuen Fenster öffnen (z.B. wenn es nicht angezeigt wird).
Und wo ist nun das Problem?
Zugegeben bis hier war es recht einfach. Das eigentliche Problem lässt sich bisher noch nicht wirklich erkennen, oder? Wenn man jetzt aber ein bisschen weiter denkt und einmal darüber nachdenkt, wie Bilder so aussehen – besonders von den Abmessungen her, kann man darauf kommen. Um das Problem zu zeigen, hier das nächste kleine Beispiel:
Den Inhalt des IFrame in einem neuen Fenster öffnen (z.B. wenn es nicht angezeigt wird).
Wie das Beispiel zeigt, verhält sich das Layout ein wenig anders, wenn die Bilder unterschiedliche Abmessungen, besonders in der Höhe, haben. Natürlich könnte man das manuell steuern und nur Bilder nebeneinander legen, die auch exakt die gleiche Breite und Höhe haben. Oder mann könte die verwendeten Bilder dementsprechend “zurecht schneiden”. Will ich aber nicht. Da ich diese Darstellung für ein WordPress-Theme verwenden will, lautet der Befehl, der die Bilder anzeigt in etwa “Gebe mir die letzten x Beiträge aus deiner Datenbank, die als Fotobeitrag kategorisiert sind, und zeigen mir hier die Thumbnails der Bilder“. Hier die Abmessungen zu berücksichtigen und die Bilder mit identischen Abmessungen nebeneinander zu legen wäre wohl viel zu komplex. Schlimmstenfalls würde es sogar zur Folge haben, dass immer nur ein Bild pro Zeile angezeigt würde, da kein zweites mit den gleichen Maßen vorhanden ist.
Die missglückten Lösungsansätze
Wie schaffe ich es nun, dass die beiden DIV-Container, die nebeneinander liegen (ich könnte auch n-mal so viele nebeneinander legen – diese Option wollte ich mir zumindest offen halten), ideentische Höhen haben, so dass der Container mit dem Infotext auch immer gleich hoch ist?
Die Tabellenform
Das war das naheliegenste. Tabellen sind in Sachen Weblayout mittlerweile ziemlich veraltet und werden normalerweise nicht mehr verwendet. Es sei denn, es handelt sich tatsächlich um eine Tabelle. Für den gewünschten Zweck bot sich hier eine Tabelle aber an, da jede Tabellenzeile automatisch immer mindestens genau so hoch ist, wie das größte darin enthaltene Element. Das funktioniert bis zu den Bildern auch soweit genau, wie es soll. Es klappt aber nicht für den Container mit dem Infotext. Warum? Ganz einfach: Wie oben beschrieben, ist der Infotext-Container absolut positioniert und hat 100% Höhe und Breite. Diese Höhe und Breite sind relativ zu dem In der Hierarchie höherem Element, dessen Position mit position: relative
angegeben ist. Hat das nächst höhere (Eltern-) Element diese Angaben nicht, wird dessen Eltern-Container benutzt. Hat dieses dies auch nicht, dann eben dessen Eltern-Container und immer so weiter.
Nun ist das Elternelement, auf das sich der Infotext-Container beziehen würde, die Tabellenzelle. Dieser kann ich zwar das position: relative
mitgeben, das wird aber nicht von jedem Browser verstanden. Unabhängig davon, ob das klappen würde, wäre das darüber liegende Referenz-Element die Tabellezeile. Und schon hier wären wir an der falschen Stelle, da sich die Infotext-Container der beiden nebeneinander liegenden Bilder nun gegenseitig überlagern würden. Tatsächlich aber ist das nächst höhere Referenz-Element aber die Tabelle selbst, dem ich die Positionsangabe relativ angeben kann, was von jedem Browser verstanden wird.
Die ungeordnete Liste
Ich habe auch probiert, die Bilder – wie in einer Navigationsliste allgemein üblich – mit Hilfe einer ungeordneten Liste um zu setzen. Diese verhalten sich aber im Wesentlichen genau wie DIV-Container, so dass auch hier kein Erfolg zu erzielen war.
Es geht wohl doch nicht ohne fixe Angaben
Schließlich kam ich dann an den Punkt, dass ich mich geschlagen geben musste. Eine Lösung, ohne den umliegenden DIV-Containern eine absolute, in Pixelwerten gemessene Höhenangabe zu geben, scheint es nicht wirklich zu geben. Also musste ich mir anderweitig helfen. Und dies habe ich dann mit einem kleinen jQuery-Script realisiert.
Die Lösung
Da ich nun beschloss, die Höhenangaben fest zu definieren, ich aber trotzdem irgendwie halbwegs responsive bleiben wollte, musste ich also ein Script schreiben, dass die Browserfenstergröße beim jeweiligen Nutzer berücksichtigt. Dafür habe ich mir zuerst natürlich eine kleine Gedankenstütze gemacht, was das Script in welcher Reihenfolge abarbeiten muss. Der Zweck des Scripts ist ganz einfach beschrieben: Es soll aus jeder Bildreihe sich jenes Bild mit der größten Höhe heraussuchen und den Wert dieser Höhe für alle Bild-Container innerhalb der Bildreihe festlegen.
Der erste Schritt
Der erste Schritt war also nun zuerst einmal die Funktion korrekt zu eröffnen. Klingt banal, ist aber nicht ganz so trivial, wie es ausschaut. Denn die erste Script-Zeile lautet:
1 | $(window).load(function() { |
Es gibt einen kleinen aber feinen Unterschied zwischen der für jQuery üblichen Document-Ready-Funktion und der Window-Load-Funktion: Erstere wird ausgeführt, sobald das HTML-Gerüst der Seite geladen ist. Was bei der Bestimmung der Bildhöhen aber zu Fehlern führt, denn diese sind zu dem Zeitpunkt noch gar nicht geladen. Deshalb müssen wir warten, bis der Seiteninhalt komplett geladen ist und erst dann die Funktion ausführen.
Scrollbar aktivieren
Eine Eigenheit meines Designentwurfs erfordert, dass ich die Scrollbars aktiviere. Denn beim Laden der Seite ist die Bild-Navigation (noch) nicht sofort sichtbar. Sie wird erst beim Aufruf der entsprechenden Rubrik angezeigt. Da dann aber so viele Bilder gezeigt werden, dass der Bereich in dem sie dargestellt werden, eine Scrollbar enthält. Die ist aber im nicht-sichtbar-Modus nicht da. Sie muss deshalb manuell aktiviert werden, damit sich die Bilder entsprechend des verfügbaren Platzes ausbreiten können.
2 | $('.picture-row').parent('.sidebar-contents').css('overflow-y','scroll'); |
Diese Zeile sucht sich von der Bildreihe (hier mit der Klasse “picture-row” ausgewählt) das übergeordnete Elternelement (mit der Klasse “sidebar-contents” ausgewählt”) und aktiviert die vertikale Scrollbar.
Die eigentliche Funktion
Nun wo alle Voraussetzungen erfüllt sind, folgt die eigentliche Funktion.
3 | $('.picture-row').each(function() { |
In jeder Bildreihe (“picture-row”) soll die Funktion ausgeführt werden.
4 | var rowHeight = 0; |
Die Variable rowHeight
wird definiert und intial auf 0 (null) gesetzt. So wird sie bei jedem Durchlauf zurück gesetzt.
5 | $(this).children('.picture.item').each(function() { |
Bei jedem Kind-Element von diesem (= Bildreihe), das die CSS-Klassen “picture” und “item” hat, soll die nächste Funktion durchgeführt werden.
6 7 8 | if ($(this).innerHeight() > rowHeight) { rowHeight = $(this).innerHeight(); } |
Wenn die Innenhöhe des Elements (das die CSS-Klassen “picture” und “item” hat) größer ist, als die rowHeight
ist, soll die Variable mit dem entsprechendem Wert überschrieben werden.
9 | }); |
Beenden der in Zeile 5 eröffneten Funktion.
10 11 | $(this).children('.picture.item').each(function() { $(this).height(rowHeight + 'px'); |
Eine neue Teilfunktion wird eröffnet. Hier wird jedem Element, das die CSS-Klassen “picture” und “item” hat, die aktuelle rowHeigt
zugewiesen.
12 13 14 15 16 | $(this).find('div img.picture').each(function() { marginHeight = ((rowHeight - $(this).height()) / 2); $(this).css('margin-top',marginHeight + 'px'); }); }); |
Diese neue Teilfunktion dient lediglich der “Aufhübschung“. Sie definiert letztlich den oberen Außenabstand der Bilder relativ zur gemessenen rowHeight
, womit die Bilder vertikal zentriert werden.
Das komplette Script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | $(window).load(function() { $('.picture-row').parent('.sidebar-contents').css('overflow-y','scroll'); $('.picture-row').each(function() { var rowHeight = 0; $(this).children('.picture.item').each(function() { if ($(this).innerHeight() > rowHeight) { rowHeight = $(this).innerHeight(); } }); $(this).children('.picture.item').each(function() { $(this).height(rowHeight + 'px'); $(this).find('div img.picture').each(function() { marginHeight = ((rowHeight - $(this).height()) / 2); $(this).css('margin-top',marginHeight + 'px'); }); // Ende div img.picture-each }); // Ende .picture.item-each }); // Ende .picture-row-each }); // Ende window.load |
Mit diesem Script nun wird der DIV-Container, der den Infotext enthält, exakt über das mit der Maus gewählte Bild mit dem Hovereffekt gelegt und dieser Container ist genauso hoch, wie sein “Nebenmann” :-)
Das Ergebnis in voller Länge kann auch in diesem Fiddle nachvollzogen werden (Achtung: das Fiddle enthält Katzencontent! :-))