Układy Flex / Grid nie działają na przyciskach lub elementach zestawu pól

91

Próbuję wyśrodkować wewnętrzne elementy <button>-tagu za pomocą flexboxów justify-content: center. Ale Safari ich nie wyśrodkowuje. Mogę zastosować ten sam styl do innych tagów i działa zgodnie z przeznaczeniem (zobacz <p>-tag). Tylko przycisk jest wyrównany do lewej.

Wypróbuj Firefox lub Chrome, a zobaczysz różnicę.

Czy istnieje styl klienta użytkownika, który muszę nadpisać? Lub inne rozwiązanie tego problemu?

div {
  display: flex;
  flex-direction: column;
  width: 100%;
}
button, p {
  display: flex;
  flex-direction: row;
  justify-content: center;
}
<div>
  <button>
    <span>Test</span>
    <span>Test</span>
  </button>
  <p>
    <span>Test</span>
    <span>Test</span>
  </p>
</div>

Oraz jsfiddle: http://jsfiddle.net/z3sfwtn2/2/

Ingemar
źródło

Odpowiedzi:

132

Problem

W niektórych przeglądarkach <button>element nie akceptuje zmian displaywartości, poza przełączaniem między blocki inline-block. Oznacza to, że <button>element nie może być kontenerem flex ani grid, ani <table>też.

Oprócz <button>elementów, może znaleźć zastosowanie do tego ograniczenia <fieldset>i <legend>elementy, jak również.

Aby uzyskać więcej informacji, zobacz poniższe raporty o błędach.

Uwaga: Chociaż nie mogą to być pojemniki elastyczne, <button>elementy mogą być elementami elastycznymi.


Rozwiązanie

Istnieje proste i łatwe obejście tego problemu w różnych przeglądarkach:

Zawiń zawartość buttonw a spani utwórz spanpojemnik elastyczny.

Dostosowany HTML ( buttonzawartość opakowana w a span)

<div>
    <button>
        <span><!-- using a div also works but is not valid HTML -->
            <span>Test</span>
            <span>Test</span>
        </span>
    </button>
    <p>
        <span>Test</span>
        <span>Test</span>
    </p>
</div>

Dostosowany CSS (docelowy span)

button > span, p {
    display: flex;
    flex-direction: row;
    justify-content: center;
}

Zmienione demo


Referencje / raporty o błędach

Flexbox na <button>blokuje zawartość, ale nie ustanawia kontekstu formatowania flex

Użytkownik (Oriol Brufau): elementy <button>podrzędne są zablokowane, zgodnie ze specyfikacją flexbox. Jednak <button>wydaje się, że ustanawia kontekst formatowania blokowego zamiast elastycznego.

Użytkownik (Daniel Holbert): Właśnie tego wymaga specyfikacja HTML. Kilka elementów kontenera HTML jest „specjalnych” i skutecznie ignoruje ich displaywartości CSS w Gecko [poza tym, czy jest to na poziomie liniowym czy na poziomie bloku]. <button>jest jednym z nich. <fieldset>i też <legend>są.

Dodaj obsługę wyświetlania: flex / grid i układ kolumn wewnątrz <button>elementów

Użytkownik (Daniel Holbert):

<button>nie są możliwe do zaimplementowania (przez przeglądarki) w czystym CSS, więc są trochę czarną skrzynką z punktu widzenia CSS. Oznacza to, że niekoniecznie reagują w taki sam sposób, jak np <div> .

Nie jest to specyficzne dla flexboksa - np. Nie renderujemy pasków przewijania, jeśli umieścisz overflow:scrollprzycisk, i nie renderujemy go jako tabeli, jeśli go umieścisz display:table.

Cofając się jeszcze dalej, to nie jest specyficzne dla <button>. Rozważ <fieldset>i <table>które również mają specjalne zachowanie podczas renderowania.

I stary-timey elementy HTML, jak <button>i <table>, a <fieldset>po prostu nie obsługują niestandardowe displaywartości, inne niż dla celów odpowiedzi na pytanie bardzo wysokim poziomie „Jest to element blokowy lub inline-level”, na płynący inną zawartość wokół elementu .

Zobacz także:

Michael Benjamin
źródło
3
Rozumiem, dlaczego to może nie działać w przypadku <button>s, ale dlaczego, u licha, nie działa też w przypadku <fieldset>? Czy ten element nie powinien być uważany za zwykły element na poziomie bloku, który jest ważny dla kontenera elastycznego?
fritzmg
3
@fritzmg. Zgoda. Reguła prawdopodobnie istniała już wcześniej w technologiach CSS3, takich jak Flex i Grid, i powinna zostać ponownie oceniona.
Michael Benjamin,
Chcielibyśmy więc zapobiec niewłaściwemu użyciu przycisku ... ale nadal moglibyśmy niewłaściwie użyć czegoś innego i umieścić go wewnątrz przycisku. Brzmi równie logicznie, jak reszta sieci ...
Romain Vincent
1
@Michael_B Rozważ dodanie odniesienia do faktycznego tekstu, w którym dokonujesz asercji. Nie musisz odpowiadać na mój komentarz - jeśli dodasz go do tekstu, w którym dokonasz asercji, dam ci głos za.
mikemaccana
2
A divnie może być dzieckiem a, buttonponieważ jeśli pomyślisz o tym w starej szkole HTML, element blokowy nie może być elementem podrzędnym elementu wbudowanego. Ale dziś, z HTML5, technicznie poprawna odpowiedź jest taka, że element może przyjąć tylko frazowania zawartość . A reprezentuje treść wyrażenia , ale nie. A oznacza zawartość przepływu . Więcej szczegółówbuttonspandivdiv
Michael Benjamin
12

Oto mój najprostszy hack.

button::before,
button::after {
    content: '';
    flex: 1 0 auto;
}
Igari Takeharu
źródło
Nie działa na mnie. Jak byś to naprawić: codepen.io/nuthinking/pen/…
Nuthinking
1

Począwszy od Chrome 83, buttonteraz działa tak inline-grid/grid/inline-flex/flex, jak możesz dowiedzieć się więcej o tej nowej funkcji tutaj

Oto fragment (dla osób korzystających z Chrome 83 i nowszych)

button {
  display: inline-flex;
  height: 2rem;
  align-items: flex-end;
  width: 4rem;
  -webkit-appearance: none;
  justify-content: flex-end;
}
<!-- 

The align-items keyword should fail in Chrome 81 or earlier, but work in Chrome 83 or later. To see the error, the button needs styles that make it more of an extrinsic container. In other words, it needs a height or width set. 
 
-->
<button>Hi</button>
<input type="button" value="Hi">

dippas
źródło