Dlaczego wartości procentowe marginesów / wypełnień w CSS są zawsze obliczane na podstawie szerokości?

131

Jeśli spojrzysz na specyfikację modelu pudełkowego CSS , zauważysz, co następuje:

Procent [marginesu] jest obliczany w odniesieniu do szerokości bloku zawierającego wygenerowane pudełko. Zauważ, że dotyczy to również „margin-top” i „margin-bottom”. Jeśli szerokość zawierającego bloku zależy od tego elementu, to wynikowy układ jest niezdefiniowany w CSS 2.1. (podkreślenie moje)

To jest rzeczywiście prawda. Ale dlaczego ? Co u licha zmusiłoby kogoś do zaprojektowania tego w ten sposób? Łatwo jest wymyślić scenariusze, w których chcesz, np. Pewna rzecz zawsze znajduje się 25% w dół od góry strony, ale trudno jest wymyślić jakikolwiek powód, dla którego chcesz, aby dopełnienie pionowe było względem poziomego rozmiaru rodzic.

Oto przykład zjawiska, do którego się odnoszę:

<div style="border: 1px solid red; margin: 0; padding: 0; width: 200px; height: 800px;">
  This div is 200x800.
  <div style="border: 1px solid blue; margin: 10% 0 0 10%;">
    This div has top-margin of 10% and left-margin of 10% with respect to its parent.
  </div>
</div>

http://jsfiddle.net/8JDYD/

mqp
źródło
3
Moja pierwsza myśl była taka, że ​​spowodowałoby to niejasność co do tego, co margin: 25%właściwie oznacza. Nie byłby to równy margines, mimo że kod sugeruje, że tak. Nie mam dowodów na poparcie tego, ale wydaje się to rozsądne.
Ryan Kinal
4
js Skrzypce moich myśli . Wysokość jest znacznie bardziej zmienna niż szerokość, więc marginesy będą się przesuwać w trudny do przewidzenia sposób, gdy zmieni się zawartość.
RichardTowers,
1
@Ryan: To prawda, nie byłby to równy margines, ale z drugiej strony, jeśli powiesz height: 10%; width: 10%, że nie dostaniesz też elementu kwadratowego.
mqp
4
Według dev.w3.org/csswg/css3-box/#the-margin-properties stwierdza się, Note that in a horizontal flow, percentages on ‘margin-top’ and ‘margin-bottom’ are relative to the width of the containing block, not the height (and in vertical flow, ‘margin-left’ and ‘margin-right’ are relative to the height, not the width).że to idzie w obie strony
Adam Sweeney
1
@Ryan Myślę, że mamy zwycięzcę. Spójrz tutaj na demo - jsFiddle
RichardTowers

Odpowiedzi:

60

Przeniesienie mojego komentarza do odpowiedzi, bo ma to logiczny sens. Należy jednak pamiętać, że jest to bezpodstawne przypuszczenie. Faktyczne uzasadnienie, dlaczego specyfikacja jest napisana w ten sposób, jest nadal technicznie nieznane.

Wysokość elementu jest określona przez wysokość dzieci. Jeśli element ma padding-top: 10% (względem wysokości rodzica), wpłynie to na wysokość rodzica. Ponieważ wzrost dziecka zależy od wzrostu rodzica, a wysokość rodzica zależy od wzrostu dziecka, będziemy mieć albo niedokładny wzrost, albo nieskończoną pętlę. Jasne, dotyczy to tylko przypadku, w którym offset rodzic === rodzic, ale nadal. To dziwny przypadek, który jest trudny do rozwiązania.

Aktualizacja: ostatnie kilka zdań może nie być do końca poprawne. Wysokość elementu liścia (dziecko bez dzieci) ma wpływ na wysokość wszystkich elementów nad nim, więc ma to wpływ na wiele różnych sytuacji.

Ryan Kinal
źródło
1
Zauważ, że są wyjątki od tej reguły - sekcja 10.6 CSS2.1 obejmuje prawie wszystkie przypadki obliczania wysokości dla różnych typów pudełek. I rzeczywiście, przypadki, w których występuje współzależność między rodzicem a dzieckiem pod względem wzrostu, zwykle prowadzą do nieokreślonego zachowania.
BoltClock
1
@sanjaypoyzer Domyślam się, że w najprostszym przypadku przeglądarki obliczają szerokość bloków od zewnątrz (od początku do końcówek), a następnie wlewają zawartość do tych bloków, aby określić ich wysokość (wskazówki do podstawy).
sam
9
Dlaczego W3C ciągle to robi? Najwyraźniej „semantyczne” dla W3C jest odpowiednikiem konieczności zaspokajania potrzeb ludzi, którzy nie potrafią logicznie myśleć, co nie ma sensu. To tak, jakby narzekać na to, jak zagubieni i głupi są ludzie na świecie, a następnie szerzyć więcej zamieszania i głupoty. Podane przez ciebie rozumowanie nie ma sensu: ten sam argument dotyczy szerokości, ale działa to dobrze. Wszystko, co byłoby potrzebne, to ustawić kontekst i kierunek określania wysokości, chyba że wysokość rodzica jest ustawiona w pikselach lub czymś niezmiennym, wtedy nie ma większego problemu.
u353
3
Zależność ta jest formułą algebraiczną, która ma rozwiązanie i nie spowoduje nieskończonej pętli, chyba że zdecydujesz się rozwiązać ją w sposób, który tego nie przestrzega. Powiedzmy, że wzrost rodzica to h_p. Wypełnienie o wysokości 10% zapewni wzrost dziecka h_c = h_ci + 0.1h_p, gdzie h_cijest to wewnętrzna wysokość dziecka i nie zależy od wzrostu rodzica. W przypadku jednego dziecka po prostu znajdź wysokość rodzica z równania h_p = h_ci + 0.1h_p=> h_p = h_ci / 0.9. Zawsze możesz to zrobić, bez względu na to, ile masz dzieci, nawet w przypadku różnych wypełnień. To tylko jedna nieznana ( h_p) i jedno równanie.
jeteon
3
Nie sądzę, by przyczyną była nieskończona kalkulacja. Z prostego powodu: używanie widthprowadzi do tego samego problemu w poziomie.
Radość
30

Aby margines „n%” (i wypełnienie) był taki sam dla marginesu górnego / prawego marginesu / dolnego marginesu / lewego marginesu, wszystkie cztery muszą odnosić się do tej samej podstawy. Gdyby góra / dół używały innej podstawy niż lewa / prawa, wtedy margines „n%” (i wypełnienie) nie oznaczałby tego samego ze wszystkich czterech stron.

(Zwróć również uwagę, że margines górny / dolny w stosunku do szerokości umożliwia dziwny hack CSS, który pozwala określić pudełko z niezmiennym współczynnikiem proporcji ... nawet jeśli zostanie przeskalowane).

Chuck Kollars
źródło
... zaskakująco prosta odpowiedź. Chociaż wszystkie powyższe przypuszczenia są uzasadnione, jest to całkiem dobry punkt. Wydaje się, że prawdopodobnie jest to przypadek, w którym wiele rozwiązań byłoby poprawnych, więc wybrali tylko jedno z najbardziej prawdopodobnych do wykorzystania… może?
dudewad
1
Przypuszczam, że byłoby miło mieć dodatkowy element w składni, aby można było pisać powiedzmy padding: n [type]. Tak więc miałbyś padding: 5% logical(co sugeruje OP) w przeciwieństwie do padding: 5% widthdrugiego parametru domyślnego na „szerokość” dla kompatybilności wstecznej.
jeteon
6
„Margines n% (i wypełnienie) nie oznaczałoby tego samego ze wszystkich czterech stron” - ale dlaczego miałby to być problem?
Herbertusz
4

Głosuję na odpowiedź od @ChuckKollars po zabawie tym JSFiddle (na Chrome 46.0.2490.86) i odwołaniu się do tego postu (napisanego po chińsku).


Głównym powodem przemawiającym przeciwko hipotezie nieskończonych obliczeń jest to, że: używanie widthnapotyka ten sam nieskończony problem obliczeniowy .

Spójrz na ten JSFiddle , parentwyświetlacz jest inline-block, który może zdefiniować margines / wypełnienie na nim. childMa wartość depozytu 20%. Jeśli podążymy za nieskończoną hipotezą obliczeniową :

  1. Szerokość childzależy odparent
  2. Szerokość parentzależy odchild

W rezultacie Chrome gdzieś zatrzymuje obliczenia, w wyniku czego:

wprowadź opis obrazu tutaj

Jeśli spróbujesz rozwinąć panel „wynik” w poziomie w JSFiddle, zauważysz, że ich szerokość się nie zmieni. Zwróć uwagę, że zawartość w childtagu jest zawinięta w dwa wiersze (nie, powiedzmy, jeden wiersz), dlaczego? Myślę, że Chrome gdzieś go na stałe zakodował. Jeśli edytujesz childzawartość, aby była bardziej widoczna ( JSFiddle ), zauważysz, że dopóki jest dodatkowa przestrzeń w poziomie, Chrome zachowuje zawartość w dwóch wierszach.

Widzimy więc: istnieje sposób, aby zapobiec nieskończonym obliczeniom .


Zgadzam się z przypuszczeniem, że: ten projekt ma po prostu zachować cztery wartości marginesu / wypełnienia w oparciu o tę samą miarę.

ten post (napisany po chińsku) proponuje również inny powód: wynika to z orientacji na czytanie / skład. Czytamy od góry do dołu, ze stałą szerokością i nieskończoną wysokością (praktycznie).

Radość
źródło
Dzieje się tak, ponieważ strony internetowe przewijają się w pionie w ogólnie nieskończonym kierunku; więc szerokość można obliczyć z szerokości okna przeglądarki. Jedynym sposobem, w jaki elementy są szersze niż ekran, jest określenie ich szerokości jako większej. Typowe elementy blokowe mają szerokość 100% i rozszerzają się automatycznie w kierunku pionowym (nieznane). Dlatego szerokość nie ma tego samego problemu.
Lucent Fox
3

Zdaję sobie sprawę, że OP pyta, dlaczego specyfikacja CSS definiuje wartości procentowe górnego / dolnego marginesu jako% szerokości (a nie, jak można by przypuszczać, wysokości), ale pomyślałem, że może być również przydatne opublikowanie potencjalnego rozwiązania.

Większość nowoczesnych przeglądarek obsługuje teraz vw i vh, co pozwala na określenie wartości marginesów względem szerokości i wysokości widoku.

100 vw / 100 vh równa się odpowiednio 100% szerokości / 100% wysokości, jeśli nie ma paska przewijania; jeśli jest pasek przewijania, numery rzutni nie uwzględniają tego (podczas gdy% liczb tak). Na szczęście prawie wszystkie przeglądarki używają paska przewijania o rozmiarze 17 pikseli ( patrz tutaj ), więc możesz to uwzględnić przy użyciu funkcji css calc. Jeśli nie wiesz, czy pojawi się pasek przewijania, czy nie, to rozwiązanie nie zadziała.

Na przykład: Zakładając, że nie ma poziomego paska przewijania, górny margines o wysokości 50% można zdefiniować jako „margin-top: 50vh;”. W przypadku poziomego paska przewijania można to zdefiniować jako „margin-top: calc (0,5 * (100vh - 17px));” (pamiętaj, że operatory minus i plus w kalkulatorze wymagają spacji po obu stronach!).

VKK
źródło
1
W wielu przypadkach jest to przydatne obejście, ale istnieje wiele przykładów, w których to nie działa (np. Jeśli próbujesz dopasować zawartość proporcjonalnie do rozmiaru kontenera flexbox, który powiększa się, aby wypełnić dostępne miejsce, podczas gdy jest inne pudełko z zmienna zawartość).
Jules
1

Wiem, że to pytanie jest trochę stare, ale chciałbym je odświeżyć w CSS3. Chociaż prawdą jest, że specyfikacja CSS2.1 mówi, że procentowe wypełnienie i marginesy są definiowane w stosunku do szerokości bloku zawierającego, nie zawsze tak jest. To zależy od trybu pisania. Wynika to prosto ze specyfikacji CSS3 :

W konsekwencji wartości procentowe właściwości marginesu i wypełnienia, które są zawsze obliczane w odniesieniu do szerokości bloku zawierającego w CSS2.1, są obliczane w odniesieniu do rozmiaru wewnętrznego bloku zawierającego w CSS3.

Omówię to w moim samouczku dotyczącym współczynników proporcji w CSS .

W szczególności znajduje się sekcja dotycząca wypełnienia procentowego w trybach pisania poziomego i pionowego . Domyślnie element ma poziomy tryb pisania, w którym tekst przepływa poziomo (w kierunku „w wierszu”) od lewej do prawej. Jednak używając writing-modewłaściwości CSS, możesz w rzeczywistości ustawić tryb na pionowy (z tekstem przepływającym od prawej do lewej lub od lewej do prawej). Oto kilka diagramów poziomych i pionowych trybów pisania:

Poziomy tryb pisania, w którym tekst przepływa pionowo od góry do dołu.  Strzałka wskazuje od lewej do prawej u góry dokumentu i jest oznaczona jako kierunek w wierszu.  Kolejna strzałka wskazuje od góry do dołu i jest oznaczona jako kierunek bloku.

Pionowy tryb pisania z tekstem płynącym poziomo.  Oś pozioma jest oznaczona jako kierunek bloku, podczas gdy oś pionowa jest teraz oznaczona jako kierunek liniowy.  Tekst jest renderowany bokiem.

Są one zaczerpnięte z dokumentacji MDN dotyczącej trybów pisania .

W pionowych trybach pisania procentowe wypełnienie będzie zależało od wysokości bloku zawierającego, a nie szerokości.

Oto dowód:

.document {
  writing-mode: vertical-rl;
  width: 100%;
  height: 100vh;
}

.parent {
   width: 100%;
   height: 200px;
   background-color: black;
   color: white;
}

.child {
  padding: 10%;
  background-color: white;
  color: black;
  border: solid 1px;
}
<div class="document">
  <div class="parent">
    <div class="child">
      Child
    </div>
  </div>
</div>

Dziecko otrzymuje 20 pikseli wypełnienia, co stanowi 10% wysokości zawierającego blok (200 pikseli).

Jeśli chodzi o powody, dla których w tym pytaniu, zostało to dobrze omówione w innych postach tutaj.

AleksandrH
źródło