Rand oben funktioniert nicht mit klar: beides

73

<div style="float: left;">Left</div>
<div style="float: right;">Right</div>
<div style="clear: both; margin-top: 200px;">Main Data</div>

Warum margin:topfunktioniert das für 'Hauptdaten' im obigen Code nicht?

Vishal
quelle
Welchen Browser benutzt du? Ich habe es auch mit Firefox und IE und Chrome überprüft.
Zain Shaikh
3
Die meisten Antworten hier bieten Problemumgehungen für dieses Verhalten, und das ist in Ordnung. Wenn Sie jedoch eine (lange, schwierige) Antwort wünschen, die tatsächlich erklärt, warum dies überhaupt geschieht, lesen Sie meine .
Mark Amery

Antworten:

73

Sie könnten die beiden schwebenden Divs in ein anderes setzen , das den Satz "overflow: hidden" hat:

<div style='overflow:hidden'>
  <div style="float: left;">Left</div>
  <div style="float: right;">Right</div>
</div>
<div style="clear: both; margin-top: 200px;">Main Data</div>

edit - Um dieser 5-jährigen Antwort etwas hinzuzufügen: Ich denke, die Ursache für das verwirrende Verhalten ist der etwas komplizierte Prozess des Randkollapses . Ein guter Trick mit dem ursprünglichen HTML aus dem OP ist das Hinzufügen einer CSS-Regel wie folgt:

div { border: 1px solid transparent; }

Poof! Jetzt (ohne mein zusätzliches <div>) funktioniert es gut! Nun, bis auf das zusätzliche Pixel von den Rändern. Insbesondere denke ich, dass es eine Kombination aus der Funktionsweise clear: bothund den Randkollapsregeln ist, die zu einem unerwarteten Layout des Codes im OP führen.

erneut bearbeiten - Die vollständige (und meiner Meinung nach absolut genaue) Geschichte finden Sie in Mark Amerys ausgezeichneter Antwort . Die Details haben eine gewisse Komplexität, die diese Antwort beschönigt.

Spitze
quelle
Können Sie es bitte näher erläutern?
Salman Virk
@ user395881 Nun, die vom Browser durchgeführten Randberechnungen beziehen sich auf Elemente, die sich im selben Layoutkontext befinden. Bei schwebenden Elementen werden die Ränder in Bezug auf andere lokale schwebende Elemente und (glaube ich) auch inline-Inhalten dargestellt. Diese statischen Blockelemente befinden sich jedoch in einem anderen Kontext. Die Layoutregeln sind kompliziert und schwer zu verstehen, da stimme ich zu. Die W3C-Dokumente enthalten einige Diagramme, die einige Dinge erklären.
Pointy
Die schwebenden Elemente, die effektiv nicht "da" sind, sind offensichtlich falsch als Erklärung für den Rand, der nicht funktioniert. Wenn das alles wäre, würden wir erwarten, dass die Oberseite des Randfelds des gelöschten Div einfach oben am übergeordneten Element beginnt und nicht unten an den schwebenden Elementen. Stattdessen sehen wir die Marge nicht Wirksamwerden überhaupt ; Das gelöschte Div im ursprünglichen Code des OP wird höher positioniert, als wenn die schwebenden Divs tatsächlich nicht vorhanden wären . Ihre Lösung funktioniert, aber Ihre Erklärung der Problemursache kann möglicherweise nicht richtig sein.
Mark Amery
@ MarkAmery Ich bin mir wirklich nicht sicher, ob ich sehe, wovon du sprichst.
Pointy
@Pointy Das Div mit Rand oben ist angesichts des OP-Codes anders positioniert als wenn die schwebenden Divs gelöscht oder die Position angegeben würden: absolut. Dies bedeutet, dass der Effekt nicht darauf zurückzuführen sein kann, dass die Floated Divs für die Margin-Berechnung nicht wirklich "da" sind.
Mark Amery
21

Während Pointy zeigt, wie Sie die Floats in ein Div einschließen können, können Sie alternativ ein leeres Div zwischen den Floats und dem Hauptdatenabschnitt einfügen. Zum Beispiel:

<div style="float: left;">Left</div>
<div style="float: right;">Right</div>
<div style="clear: both;"></div>
<div style="margin-top: 200px;">Main Data</div>

Dies kann sich in Fällen als nützlich erweisen, in denen das Hinzufügen eines Div-Wrappers um HTML nicht wünschenswert ist.

Randall Cook
quelle
Dies ist ein sehr kluger Ansatz. Sie können auch die Höhe des ~ <div style = "clear: both;"> </ div> ~ als ~ Rand oben ~
festlegen, wenn
17

Die Logik dahinter in der Spezifikation ist umwerfend und beinhaltet ein kompliziertes Zusammenspiel der Regeln für die Freigabe und das Zusammenfallen von Rändern .

Sie sind vermutlich mit dem herkömmlichen CSS vertrauen Box - Modell , in dem die Content - Box innerhalb einer enthaltenen padding - Box innerhalb eines enthaltenen Grenze Feld innerhalb einer enthaltenen Marge Box :

Diagramm des Kastenmodells

Für Elemente, cleardie auf etwas anderes als eingestellt sind none, kann eine zusätzliche Komponente in dieses Modell eingeführt werden: Abstand .

Andere Werte als "keine" führen möglicherweise zu einer Freigabe . Der Abstand verhindert das Zusammenfallen des Randes und wirkt als Abstand über dem Rand eines Elements.

Mit anderen Worten, das Box-Modell sieht in diesen Fällen wirklich eher so aus:

Box-Modell mit 'Abstand' über dem Rand der Randbox

Aber wann wird die Freigabe eingeführt und wie groß sollte sie sein? Beginnen wir mit der ersten dieser Fragen. Die Spezifikation sagt :

Die Berechnung des Abstands eines Elements, für das "Löschen" festgelegt ist, erfolgt, indem zunächst die hypothetische Position der oberen Randkante des Elements bestimmt wird. An dieser Position wäre die tatsächliche obere Randkante gewesen, wenn die Eigenschaft 'clear' des Elements 'none' gewesen wäre.

Wenn diese hypothetische Position der oberen Randkante des Elements nicht hinter den relevanten Gleitkommazahlen liegt, wird der Abstand eingeführt und die Ränder werden gemäß den Regeln in 8.3.1 kollabiert.

Wenden wir diese Logik auf den Code des Fragestellers an. Denken Sie daran, wir versuchen, die Position des dritten Div im folgenden Code zu erklären (Hintergründe wurden hinzugefügt, um die Visualisierung zu erleichtern):

<div style="float: left; background: red;">Left</div>
<div style="float: right; background: green;">Right</div>
<div style="clear: both; margin-top: 200px; background: blue;">Main Data</div>

Stellen wir uns vor, wie die Spezifikation fordert uns auf, dass clearzu setzen ist noneauf der dritten div statt both. Wie würde dann der obige Ausschnitt aussehen?

<div style="float: left; background: red;">Left</div>
<div style="float: right; background: green;">Right</div>
<div style="clear: none; margin-top: 200px; background: blue;">Main Data</div>

Hier überlappt die dritte Division die beiden schwebenden Divs. Aber warte; Warum ist das so? Sicher, es ist zulässig, dass schwebende Elemente Elemente auf Blockebene überlappen (gemäß der Floats-Spezifikation : "Da sich kein Float im Flow befindet, fließen nicht positionierte Blockboxen, die vor und nach dem Float-Box erstellt wurden, vertikal, als ob der Float nicht vorhanden wäre . " ), aber unser dritter Div hat jede Menge margin-topdrauf und kommt nach den beiden schwebenden Divs; sollten die beiden schwebenden Divs nicht oben am Körper erscheinen und die dritte Div 200px tiefer, weit darunter?

Der Grund, warum dies nicht auftritt, ist, dass der Rand des dritten Divs in den Rand des übergeordneten Divs (in diesem Fall des Körpers) fällt. Das gleiche Verhalten tritt jedoch auf, wenn Sie alle drei Divs in ein übergeordnetes Div einschließen. Die Collapsing Margin-Spezifikation (unten zitiert, wobei einige irrelevante Details weggelassen wurden) sagt uns, dass:

Angrenzende vertikale Ränder kollabieren ...

Zwei Ränder grenzen genau dann aneinander, wenn:

  • Beide gehören zu In-Flow-Boxen auf Blockebene, die am gleichen Blockformatierungskontext teilnehmen
  • Keine Linienkästen, kein Abstand, keine Polsterung und kein Rand trennen sie ...
  • beide gehören zu vertikal benachbarten Kastenkanten, dh bilden eines der folgenden Paare:
    • oberer Rand einer Box und oberer Rand ihres ersten in-flow-Kindes
    • ...

Die dritte Div in unserem Beispiel ist sicherlich nicht das erste Kind des Körpers , aber es ist das erste Kind im Fluss . Beachten Sie, dass gemäß https://www.w3.org/TR/CSS22/visuren.html#positioning-scheme :

Ein Element wird als nicht fließend bezeichnet, wenn es schwebend, absolut positioniert oder das Wurzelelement ist. Ein Element wird als In-Flow bezeichnet, wenn es nicht außer Fluss ist.

Da der erste und der zweite Div in unserem Beispiel schwebend sind, fließt nur der dritte Div. Somit grenzt sein oberer Rand an den oberen Rand seines Elternteils an, und die Ränder kollabieren - was den gesamten Körper einschließlich der beiden schwebenden Elemente nach unten drückt . Somit überlappt die dritte Division ihre Geschwister, obwohl sie eine große hat margin-top. Folglich erfüllen wir - in diesem hypothetischen Fall, in dem das dritte Element cleareingestellt ist none- die Bedingung, dass:

Die obere Randkante des Elements befindet sich nicht hinter den entsprechenden Gleitkommazahlen

So:

Die Freigabe wird eingeführt, und die Ränder fallen gemäß den Regeln in 8.3.1 zusammen

Wie viel Freiraum? Die Spezifikation bietet Browsern zwei Optionen mit einigen Erläuterungen:

Dann wird der Freiraum auf den größeren Wert eingestellt von:

  • Der Betrag, der erforderlich ist, um die Randkante des Blocks auch mit der unteren Außenkante des niedrigsten zu löschenden Schwimmers zu platzieren.
  • Der Betrag, der erforderlich ist, um die obere Randkante des Blocks an ihrer hypothetischen Position zu platzieren.

Alternativ wird der Abstand genau auf den Betrag eingestellt, der erforderlich ist, um die Randkante des Blocks auch mit der unteren Außenkante des untersten Schwimmers zu platzieren, der geräumt werden soll.

Hinweis: Beide Verhaltensweisen sind bis zur Bewertung ihrer Kompatibilität mit vorhandenen Webinhalten zulässig. Eine zukünftige CSS-Spezifikation erfordert entweder die eine oder die andere.

Hinweis: Der Abstand kann negativ oder Null sein.

Bevor wir mit der Anwendung dieser Regeln beginnen können, stoßen wir sofort auf eine Komplikation. Erinnern Sie sich an den zusammenbrechenden Spielraum, den wir im hypothetischen Fall berücksichtigen mussten, wo clearwar none? Nun, in diesem nicht hypothetischen Fall, in dem wir die zu verwendende Freigabe berechnen, existiert sie nicht , weil die Existenz der Freigabe sie hemmt. Erinnern Sie sich an die zuvor zitierten Regeln für kollabierende Ränder aus 8.3.1 , die vorschreiben, dass Ränder nur dann angrenzen, wenn:

  • Keine Linienkästen, kein Abstand , keine Polsterung und kein Rand trennen sie

(Betonung hinzugefügt). Somit grenzen der obere Rand des dritten Div und der obere Rand seines Elternteils nicht mehr aneinander. Wir können dieses Szenario vor der Freigabe in unserem Beispiel-Snippet simulieren clear: none, indem wir padding-top: 1pxden Body beibehalten, aber hinzufügen , wodurch auch der Randkollaps gemäß der oben angegebenen Regel deaktiviert wird.

body {
  padding-top: 1px;
}
<div style="float: left; background: red;">Left</div>
<div style="float: right; background: green;">Right</div>
<div style="clear: none; margin-top: 200px; background: blue;">Main Data</div>

Jetzt, anders als beim Zusammenbruch der Ränder, liegt unsere dritte Division bequem unter ihren beiden schwebenden Geschwistern. Aber wir haben bereits beschlossen, auf der Grundlage eines hypothetischen Szenario , in dem die Margen haben Zusammenbruch, muss dieser Zwischenraum hinzugefügt werden; Alles was bleibt ist, die Höhe der Freigabe zu wählen , um:

Platzieren Sie die Randkante des Blocks auch mit der unteren Außenkante des untersten Schwimmers, der geräumt werden soll

Wir haben also keine andere Wahl, als einen negativen Abstand zum dritten Div anzuwenden , um dessen obere Randkante nach oben zu ziehen und die untere Außenkante (auch als Randkante bezeichnet ) der darüber schwebenden Elemente zu berühren . Wenn die schwebenden Elemente jeweils 10 Pixel hoch sind und der dritte Teil einen oberen Rand von 200 Pixel hat, wird ein Abstand von -190 Pixel angewendet. Das bringt uns schließlich zum Endergebnis, das der Fragesteller gesehen hat:

<div style="float: left; background: red;">Left</div>
<div style="float: right; background: green;">Right</div>
<div style="clear: both; margin-top: 200px; background: blue;">Main Data</div>

(Beachten Sie, dass Sie, wenn Sie das dritte Div im obigen Snippet mit den Entwicklertools Ihres Browsers untersuchen, immer noch die 200 Pixel des oberen Randes über dem Div sehen können, die weit über den Rest des Inhalts hinausgehen - es ist nur das Die gesamte Randbox wurde durch den großen negativen Abstand nach oben gezogen.)

Einfach!

Mark Amery
quelle
Dies - nun, ich wollte sagen "macht Sinn", aber das ist nicht ganz richtig - ist sicherlich die genaue Geschichte. Ich glaube, ich bin zu dem Schluss gekommen, dass es keine Möglichkeit gibt, einen "Randkollapszaun" einzuführen, der keine Auswirkungen auf das Layout hat (0px "Dicke"). Ist das richtig?
Pointy
@Pointy Für den Fall des Zusammenbruchs von Containern / Kindern overflow: autooder overflow: hiddenfür den übergeordneten Container sollte der Trick gemäß dem großen Hinweis in der Spezifikation unter w3.org/TR/CSS22/box.html#collapsing-margins und auch gemäß den gängigen Antworten ausgeführt werden wie stackoverflow.com/a/6204990/1709587 (obwohl es aus irgendeinem Grund nicht funktioniert, wenn der Container das bodyElement ist). Ich bin mir nicht ganz sicher, warum dies funktioniert, da es von der Definition eines "Blockformatierungskontexts" abhängt (den ich nicht untersucht habe und derzeit nicht verstehe).
Mark Amery
@Pointy mit einem Zaun von z. B. 0,02 Pixel scheint ebenfalls zu funktionieren (zumindest in Chrome - möglicherweise browserspezifisch), obwohl dies keine sichtbaren Auswirkungen auf das Layout hat. Kleinere Zäune wie 0.01px funktionieren jedoch nicht. Warum? Keine Ahnung.
Mark Amery
Ja, ich erinnere mich halb daran, immer kleinere Randbreiten (usw.) ausprobiert zu haben, als ich an meiner eigenen Version dieses Problems arbeitete, bis ich es satt hatte und feststellte, dass es mich nicht wirklich interessierte. Es ist sicherlich eine überraschende Reihe von Verhaltensweisen.
Pointy
Aus der Spezifikation: "Ränder von Elementen, die neue Blockformatierungskontexte erstellen (z. B. Floats und Elemente mit einem anderen" Überlauf "als" sichtbar "), kollabieren nicht mit ihren untergeordneten Elementen. Ich weiß nicht genau, warum dies so gemacht wurde (wie so viele andere BFC-bezogene Änderungen von CSS2 zu CSS2.1), aber ich habe eine fundierte Vermutung angestellt . Der Grund, warum es nicht mit dem body-Element zu funktionieren scheint, ist, dass das body-Element seine Überlaufeigenschaft an HTML verliert, wenn HTML einen Überlauf aufweist: sichtbar .
BoltClock
8

Pointy und Randall Cook haben ausgezeichnete Antworten. Ich dachte, ich würde noch eine Lösung zeigen.

<div style="float: left;">Left</div>
<div style="float: right;">Right</div>
<div style="float: left; clear: both; margin-top: 200px;">Main Data</div>

Wenn Sie das 3. Element "float: left;" UND "klar: beides;" sollte den gewünschten Effekt haben, dem 3. Element einen Rand von 200 Pixel zu geben. Hier ist ein Link zu einem Beispiel.

Dies kann sich auch auf andere Folgeelemente auswirken, ob sie Floats sein müssen oder nicht. Es könnte jedoch auch den gewünschten Effekt haben.

rvbyron
quelle
4

Alternative Lösung:

Sie können tatsächlich ein margin-bottomschwebendes Element auf die schwebenden Elemente setzen, um das darunter liegende Element nach UNTEN zu drücken clear: both.

http://jsfiddle.net/9EY4R/

Geben Sie hier die Bildbeschreibung ein

Hinweis: Nachdem ich diesen Vorschlag gemacht habe, muss ich ihn sofort zurückziehen, da dies im Allgemeinen keine gute Idee ist, aber in einigen begrenzten Situationen angemessen sein kann.


<div class='order'>

    <div class='address'>
        <strong>Your order will be shipped to:</strong><br>
        Simon</br>
        123 Main St<br>
        Anytown, CA, US
    </div>

    <div class='order-details'>
        Item 1<br>
        Item 2<br>
        Item 3<br>
        Item 4<br>
        Item 5<br>
        Item 6<br>
        Item 7<br>
        Item 8<br>
        Item 9<br>
        Item 10<br>
    </div>

    <div class='options'>
        <button>Edit</button>
        <button>Save</button>
    </div>
</div>

Das Panel mit Elementen wird order-detailsmit diesem CSS aufgerufen

.order-details
{
    padding: .5em;
    background: lightsteelblue;

    float: left;
    margin-left: 1em;

    /* this margin does take effect */
    margin-bottom: 1em;
}

In der obigen Geige hat das gelbe Feld ein margin-top, aber wenn es nicht größer als das höchste schwebende Objekt ist, wird es nichts tun (das ist natürlich der springende Punkt dieser Frage).

Wenn Sie margin-topdas gelbe Feld auf 20em einstellen, wird es sichtbar, da der Rand vom oberen Rand des äußeren blauen Felds berechnet wird.

Simon_Weaver
quelle
3

Verwenden Sie stattdessen 'padding-top' in Ihrem Hauptdatenbereich. Alternativ können Sie das Hauptdaten-Div mit 'padding-top' in eins einschließen.

Freeworlder
quelle
0

Versuchen Sie, einen unteren Rand für eines der schwebenden Elemente festzulegen. Alternativ können Sie die Floats in ein übergeordnetes Element einschließen und einen CSS-Hack verwenden, um es ohne zusätzliches Markup zu löschen .

Jack Cheng
quelle
0

Manchmal kann eine Kombination aus Positionsrelativ und Rand diese Art von Problemen lösen.

Ich verwende diese Technik für meine Alignright- und Alignleft-Klassen in WordPress.

Zum Beispiel, wenn ich einen "unteren Rand" möchte, der durch das Löschen von Elementen respektiert wird, die Sie verwenden können.

.alignright{
   float: right;
   margin-left: 20px;
   margin-top: 20px;
   position: relative;
   top: -20px;
}

Für Ihr Beispiel könnten Sie so etwas tun

<div style="float: left;">Left</div>
<div style="float: right;">Right</div>
<div style="clear: both; margin-bottom: 200px; position: relative; top: 200px;">Main Data</div>
Christian
quelle