<zestaw pól> źle zmienia rozmiar; wydaje się mieć nieusuwalny „min-width: min-content”

221

Problem

Mam <select>gdzie jedna z jego <option>wartości tekstowych jest bardzo długa. Chcę <select>zmienić rozmiar, aby nigdy nie był szerszy niż element nadrzędny, nawet jeśli musi odciąć wyświetlany tekst. max-width: 100%powinien to zrobić.

Przed zmianą rozmiaru:

strona przed zmianą rozmiaru, pokazująca całe <code> wybierz </code>

Co chcę po zmianie rozmiaru:

moja żądana strona po zmianie rozmiaru, z <code> wybierz </code> obcinaniem jej zawartości

Ale jeśli załadujesz ten przykład jsFiddle i zmienisz szerokość panelu Wynik na mniejszą niż szerokość <select>, możesz zobaczyć, że zaznaczenie wewnątrz <fieldset>nie przeskalowuje jego szerokości w dół.

Co właściwie widzę po zmianie rozmiaru:

moja bieżąca strona po zmianie rozmiaru, z paskiem przewijania

Jednak odpowiednik strony z <div>zamiast zamiast<fieldset> nie skaluje się poprawnie. Możesz to zobaczyć i przetestować zmiany łatwiej, jeśli masz jeden <fieldset>i jeden <div>obok siebie na jednej stronie . A jeśli usuniesz otaczające <fieldset>tagi , zmiana rozmiaru będzie działać. <fieldset>Znacznik jest w jakiś sposób powoduje poziome skalowanie do złamania.

Te <fieldset>akty jakby istnieje reguła CSS fieldset { min-width: min-content; }. ( min-contentŚrodki, z grubsza, najmniejsza szerokość, która nie powoduje dziecka do przelewu). Jeśli mogę wymienić <fieldset>z <div>omin-width: min-content , wygląda dokładnie tak samo. Jednak min-contentw moich stylach, w domyślnym arkuszu stylów przeglądarki lub w Inspektorze CSS Firebug nie ma żadnej reguły . Próbowałem zastąpić każdy styl widoczny <fieldset>w Inspektorze CSS Firebuga i domyślnym arkuszu stylów forms.css Firefoksa , ale to nie pomogło. Szczególnie nadpisujące min-widthi też widthnic nie zrobiły.

Kod

HTML zestawu pól:

<fieldset>
    <div class="wrapper">
        <select id="section" name="section">
            <option value="-1"></option>
            <option value="1501" selected="selected">Sphinx of black quartz, judge my vow. The quick brown fox jumps over the lazy dog.</option>
            <option value="1480">Subcontractor</option>
            <option value="3181">Valley</option>
            <option value="3180">Ventura</option>
            <option value="3220">Very Newest Section</option>
            <option value="1481">Visitor</option>
            <option value="3200">N/A</option>
        </select>
    </div>
</fieldset>

Mój CSS, który powinien działać, ale nie jest:

fieldset {
    /* hide fieldset-specific visual features: */
    margin: 0;
    padding: 0;
    border: none;
}

select {
    max-width: 100%;
}

Resetowanie widthwłaściwości do wartości domyślnych nic nie robi:

fieldset {
    width: auto;
    min-width: 0;
    max-width: none;
}

Dalszy CSS, w którym próbuję rozwiązać problem :

/* try lots of things to fix the width, with no success: */
fieldset {
    display: block;
    min-width: 0;
    max-width: 100%;
    width: 100%;
    text-overflow: clip;
}

div.wrapper {
    width: 100%;
}

select {
    overflow: hidden;
}

Więcej szczegółów

Problem występuje również w tym bardziej kompleksowym, bardziej skomplikowanym przykładzie jsFiddle , który jest bardziej podobny do strony internetowej, którą próbuję naprawić. Widać z tego, że <select>to nie jest problem - blok wbudowany divrównież nie zmienia rozmiaru. Chociaż ten przykład jest bardziej skomplikowany, zakładam, że poprawka do powyższego prostego przypadku również naprawi ten bardziej skomplikowany przypadek.

[Edycja: zobacz szczegóły wsparcia przeglądarki poniżej.]

Ciekawą rzeczą w tym problemie jest to, że jeśli ustawiszdiv.wrapper { width: 50%; } , <fieldset>zatrzyma się zmiana rozmiaru w punkcie, a następnie pełny rozmiar <select>uderzy o krawędź rzutni. Zmiana rozmiaru odbywa się tak, jakby <select>miała width: 100%, mimo że <select>wygląda tak width: 50%.

po zmianie rozmiaru za pomocą div otoki 50% szerokości

Jeśli dasz z <select>siebiewidth: 50% , takie zachowanie nie występuje; szerokość jest po prostu poprawnie ustawiona.

po zmianie rozmiaru przy 50% szerokości wybierz

Nie rozumiem przyczyny tej różnicy. Ale to może nie mieć znaczenia.

Znalazłem również bardzo podobne pytanie zestaw pól HTML pozwala dzieciom rozwijać się w nieskończoność . Pytający nie mógł znaleźć rozwiązania i zgaduje, że nie ma innego rozwiązania oprócz usunięcia <fieldset>. Ale zastanawiam się, czy naprawdę nie jest możliwe <fieldset>poprawne wyświetlanie, dlaczego? Co w <fieldset>„s specyfikacji lub domyślnego CSS ( w tej kwestii ) powoduje takie zachowanie? To specjalne zachowanie jest prawdopodobnie gdzieś udokumentowane, ponieważ wiele przeglądarek działa w ten sposób.

Cel i wymagania w tle

Powodem, dla którego próbuję to zrobić, jest pisanie stylów mobilnych dla istniejącej strony o dużej formie. Formularz ma wiele sekcji, a jedna jego część jest owinięta w <fieldset>. Na smartfonie (lub jeśli okno przeglądarki jest małe) część strony z rozszerzeniem <fieldset>jest znacznie szersza niż reszta formularza. Większość postaci ogranicza swoją szerokość, ale sekcja z <fieldset>nie, co zmusza użytkownika do pomniejszenia lub przewinięcia w prawo, aby zobaczyć całą tę sekcję.

Obawiam się po prostu usunąć <fieldset>, ponieważ jest generowany na wielu stronach w dużej aplikacji, i nie jestem pewien, od czego mogą zależeć selektory w CSS lub JavaScript.

W razie potrzeby mogę używać JavaScript, a rozwiązanie JavaScript jest lepsze niż nic. Ale jeśli JavaScript jest jedynym sposobem, aby to zrobić, byłbym ciekawy wyjaśnienia, dlaczego nie jest to możliwe przy użyciu tylko CSS i HTML.


Edycja: obsługa przeglądarki

Na stronie muszę obsługiwać Internet Explorera 8 i nowsze (właśnie zrezygnowaliśmy z obsługi IE7), najnowszego Firefoksa i najnowszego Chrome. Ta konkretna strona powinna również działać na smartfonach z systemem iOS i Android. Nieco obniżone, ale nadal użyteczne zachowanie jest dopuszczalne w Internet Explorerze 8.

Ponownie przetestowałem mój zepsuty fieldsetprzykład w różnych przeglądarkach. W rzeczywistości działa już w tych przeglądarkach:

  • Internet Explorer 8, 9 i 10
  • Chrom
  • Chrome na Androida

Uszkadza się w tych przeglądarkach:

  • Firefox
  • Firefox na Androida
  • Internet Explorer 7

Tak więc jedyną przeglądarką, na której mi zależy, że obecny kod się włamuje, jest Firefox (zarówno na komputerze, jak i telefonie komórkowym). Jeśli kod zostałby naprawiony, aby działał w przeglądarce Firefox bez uszkodzenia go w innych przeglądarkach, rozwiązałoby to mój problem.

Szablon strona HTML używa Internet Explorer warunkowe komentarze dodać takie zajęcia .ie8i .oldiedo <html>elementu. Możesz użyć tych klas w swoim CSS, jeśli chcesz obejść różnice stylów w IE. Dodane klasy są takie same jak w starej wersji HTML5 Boilerplate .

Rory O'Kane
źródło
41
gratuluję tego znakomitego podsumowania
Alp
Twój prosty przykład: podaj <fieldset>(lub div.wrapper) dowolną potrzebną szerokość oraz select { width:100% }. max-widthwydaje się dawać elementowi pewną% pierwotnej szerokości elementu nadrzędnego , a następnie nie jest skalowany, podczas widthgdy skaluje się z rodzicem. Myślę, że zrobi to, co chcesz (ale mogłem źle zrozumieć). W bardziej złożonym skrzypciu spróbuj nadać polom w lewej kolumnie% szerokości i szerokość min. Piksela. Następnie podaj pola po prawej stronie 100 - (lewe pola -% - szerokość)% szerokości.
Trojan
Pomyślałem jeszcze raz: co, jeśli ustawisz minimalną szerokość zestawu pól na 0? To samo w sobie prawdopodobnie nie rozwiąże problemu, ale być może warto spróbować je rozwiązać. Pracuję teraz inną ścieżką, ale jeśli to nie zadziała, wypróbuję to również
Trojan
Czy potrzebujesz etykiety i pola, aby pozostać na tej samej linii? W przypadku małych rzutni nakładają się na siebie. Dlaczego nie sprawić, by był nieco responsywny i w razie potrzeby przejść do nowej linii?
Trojan

Odpowiedzi:

346

Aktualizacja (25 września 2017 r.)

Firefox bug opisany poniżej jest ustalana jako Firefoksa 53 i link do tej odpowiedzi został ostatecznie usunięty z dokumentacją Bootstrap za .

Również moje szczere przeprosiny dla twórców Mozilli, którzy musieli -moz-documentczęściowo zablokować usuwanie wsparcia z powodu tej odpowiedzi.

Poprawka

W WebKit i Firefox 53+ po prostu ustawiłeś zestaw min-width: 0;pól, aby zastąpić domyślną wartość min-content

Mimo to Firefox jest trochę… dziwny, jeśli chodzi o zestawy terenowe. Aby działało to we wcześniejszych wersjach, należy zmienić displaywłaściwość zestawu pól na jedną z następujących wartości:

  • table-cell (Zalecana)
  • table-column
  • table-column-group
  • table-footer-group
  • table-header-group
  • table-row
  • table-row-group

Spośród nich polecam table-cell . Zarówno table-rowi table-row-groupuniemożliwić zmianę szerokości, podczas gdy table-columni table-column-groupuniemożliwić zmianę wysokości.

Spowoduje to (w pewnym sensie) przerwanie renderowania w IE. Ponieważ tylko Gecko tego potrzebuje, możesz w uzasadniony sposób użyć@-moz-document jednego z zastrzeżonych rozszerzeń CSS Mozilli, aby ukryć go przed innymi przeglądarkami:

@-moz-document url-prefix() {
    fieldset {
        display: table-cell;
    }
}

(Oto demo jsFiddle .)


To naprawia rzeczy, ale jeśli jesteś podobny do mnie, twoja reakcja była jak…

Co.

Tam jest to powód, ale nie całkiem.

Domyślna prezentacja elementu zestawu pól jest absurdalna i zasadniczo niemożliwa do określenia w CSS. Pomyśl o tym: granica zestawu pól znika, gdy nakłada się na niego element legendy, ale tło pozostaje widoczne! Nie da się tego odtworzyć za pomocą żadnej innej kombinacji elementów.

Reasumując, implementacje są pełne ustępstw wobec starszych zachowań. Jednym z nich jest to, że minimalna szerokość zestawu pól nigdy nie jest mniejsza niż wewnętrzna szerokość jego zawartości. WebKit umożliwia obejście tego zachowania poprzez określenie go w domyślnym arkuszu stylów, ale Gecko² idzie o krok dalej i wymusza to w silniku renderowania .

Jednak wewnętrzne elementy stołu stanowią w Gecko specjalny rodzaj ramy . Ograniczenia wymiarowe dla elementów z displayustawionymi wartościami są obliczane w osobnej ścieżce kodu , całkowicie omijając wymuszoną minimalną szerokość nałożoną na zestawy pól.

Znowu - błąd został naprawiony w Firefoksie 53, więc nie potrzebujesz tego hacka, jeśli atakujesz tylko nowsze wersje.

Czy używanie jest @-moz-documentbezpieczne?

W przypadku tego jednego problemu tak. @-moz-documentdziała zgodnie z przeznaczeniem we wszystkich wersjach Firefoksa do 53, w których ten błąd został naprawiony.

To nie jest przypadek. Częściowo z powodu tej odpowiedzi błąd polegający na ograniczeniu się @-moz-documentdo arkuszy stylów użytkownika / UA został uzależniony od tego, czy najpierw naprawiony zostanie podstawowy błąd zestawu pól.

Poza tym nie używaj @-moz-documentdo kierowania Firefoksa w CSS , niezależnie od innych zasobów .³


¹ Wartość może być przedrostkiem. Według jednego z czytelników nie ma to wpływu na przeglądarkę zapasową Androida 4.1.2 i prawdopodobnie na inne stare wersje; Nie miałem czasu tego zweryfikować.

² Wszystkie linki do źródła Gecko w tej odpowiedzi odnoszą się do zestawu zmian 5065fdc12408 , zatwierdzonego 29ᵗʰ lipca 2013 r .; możesz porównać notatki z najnowszą wersją Mozilla Central .

³ Patrz np. SO # 953491: Skierowanie tylko do Firefoksa za pomocą CSS i sztuczek CSS: hacki CSS atakujące Firefoksa w przypadku artykułów, do których często się odwołuje, na popularnych stronach.

Jordan Gray
źródło
5
Właśnie zauważyłem, że poprawka zepsuła się w IE, tylko po to, aby przyjść tutaj i dowiedzieć się, że już edytowałeś w tym rozwiązaniu. Dzięki! Rozwiązanie hakowania Gecko działa dla mnie dobrze w każdej przeglądarce.
Rory O'Kane
Wyciągałem włosy z tego problemu, ale twoja poprawka jest idealna - tyle że wydaje się, że działa tylko w Firefox. Wszelkie sugestie dotyczące innych przeglądarek, zwłaszcza przeglądarek mobilnych? Moim największym problemem jest zmiana rozmiaru z powodu zmiany orientacji na urządzeniach mobilnych ...
Adriaan Nel,
30
To absolutnie znakomita odpowiedź - dokładna, zbadana i zwięzła. Dzięki.
iamkeir,
16
Oczywiście jest to absolutnie znakomita odpowiedź! Oficjalna dokumentacja Bootstrap wskazuje tutaj
Ranhiru Jude Cooray
1
Cóż, nigdy nie wiedziałem tego o zestawach pól. Nauczyłem się tu dzisiaj czegoś.
superluminarny
11

Problem z Safari na iOS z wybraną odpowiedzią

Odpowiedź Jordana Graya była szczególnie pomocna. Wydaje mi się jednak, że nie rozwiązało to problemu w Safari iOS.

Problemem jest dla mnie po prostu to, że zestaw pól nie może mieć automatycznej szerokości, jeśli element wewnątrz ma maksymalną szerokość jako% szerokości.

Napraw problem

Wydaje się, że po prostu ustawienie zestawu pól tak, aby miał kontener o 100% szerokości, pozwala obejść ten problem.

Przykład

fieldset {
    min-width: 0; 
    width: 100%; 
}

Przykłady robocze znajdują się poniżej - jeśli usuniesz% szerokości z zestawu pól lub zastąpisz go auto, przestanie on działać.

JSFiddle | Codepen

dziki
źródło
1
To świetny dodatek do ogólnego problemu, nie pomyślałem o przetestowaniu go w iOS. :) Wydaje mi się, że warto zadać to samo pytanie i odpowiedzieć jako nowe pytanie. Spróbuję wykonać dodatkowe testy w tym tygodniu i link do tej odpowiedzi, jeśli zadziała.
Jordan Gray
3

Walczyłem z tym przez wiele godzin i zasadniczo przeglądarka stosuje styl komputerowy, który należy zastąpić w CSS. Zapominam dokładną właściwość ustawianą dla fieldsetelementów w porównaniu do divs (być może min-width?).

Moja najlepsza rada to zmienić element na a div, skopiować obliczone style z inspektora, a następnie zmienić element z powrotem nafieldset i porównaj obliczone style, aby znaleźć winowajcę.

Mam nadzieję, że to pomaga.

Aktualizacja: dodawanie display: table-cellpomaga w przeglądarkach innych niż Chrome.

phdj
źródło
Porównałem każdą właściwość CSS fieldseti divna tej stronie w Firebug. To nie pomogło. W Firebug jedynymi właściwościami o różnych wartościach były widthi perspective-origin. widthLiczbowo była inna, ale fieldset„s widthbyło auto, podobnie jak div” s. I perspective-originjest to tylko funkcja width- 50% z tego domyślnie.
Rory O'Kane
Jednak porównanie pomogło w pewien sposób w Chrome Web Inspector. Po otwarciu mojego przykładu dowiedziałem się , że działa on w Chrome! Dodałem go min-width: 0;do mojego przykładu po przetestowaniu w Chrome, ale wygląda na to, że to rozwiązało problem w Chrome. Inspektor internetowy mówi mi, że Chrome ma styl min-width: -webkit-min-content;, tak jak to przypuszczałem. Teraz muszę tylko rozwiązać problem w przeglądarce Firefox i prawdopodobnie w innych przeglądarkach, w których jeszcze nie testowałem.
Rory O'Kane
Cieszę się, że to trochę pomogło. Udało mi się rozwiązać problem w innych przeglądarkach za pomocą „display: table-cell”. Nie sądzę, że to nie jest najlepsze rozwiązanie, ale mam nadzieję, że to ci odpowiada.
phdj
2

.fake-select { white-space:nowrap; }spowodowało, że zestaw pól zinterpretował .fake-selectelement na podstawie jego pierwotnej szerokości, a nie jego wymuszonej szerokości (nawet gdy przelew jest ukryty).

Usunąć tę regułę, a zmiana .fake-select„s max-width:100%do zaledwie width:100%i wszystko pasuje. Zastrzeżenie polega na tym, że widzisz całą zawartość fałszywego wyboru, ale nie sądzę, że jest tak źle, i teraz pasuje poziomo.

Aktualizacja: przy obecnych regułach w następującym skrzypcach (które zawierają tylko rzeczywiste selekcje), dzieci zestawu pól są ograniczone do poprawiania szerokości. Oprócz usuwania reguł .fake-selecti poprawiania komentarzy (od // commentdo /* comment */, zauważyłem zmiany w CSS skrzypce.

Teraz rozumiem twój problem, a skrzypce odzwierciedlają pewne postępy. Ustawiam domyślne reguły dla wszystkich <select>s i rezerwuję .xxlargedla tych, o których wiesz, że będą szersze niż 480px (i działa to tylko dlatego, że znasz szerokość #viewporti możesz ręcznie dodać klasę do tych zbyt szerokich. Wystarczy trochę przetestować )

Dowód

trojański
źródło
Jakakolwiek poprawka, której używasz, musi działać na rzeczywistej <select>. Rzeczywista strona ma tylko <select>s. Chodziło o fake-selectto, aby reguły układu <select>nie były w rzeczywistości <select>, aby pokazać, że to zachowanie nie jest błędem przeglądarki, który zdarza się tylko z <select>elementami. Zmiana stylu .fake-selectna mniejszą niż <select>pokonanie celu.
Rory O'Kane
Wymieniłem .fake-selectpudełko na <select>(identyczne z tym już obecnym), aby zademonstrować. Wszystkie elementy są ograniczone do szerokości elementu nadrzędnego (z wyjątkiem kliknięcia na nim, rozwijania opcji, które są wyświetlane w jednym wierszu - ale nie sądzę, że można to kontrolować). Usunąłem również wszystkie reguły dotyczące .fake-select. Czy obecne skrzypce robi to, czego potrzebujesz?
Trojan
To nie. width: 100%działa z długimi menu na stronie, ale nie działa poprawnie z krótkimi menu. Rozszerza krótkie menu do pełnej szerokości strony, ale chcę, aby krótkie menu zachowywały swoją krótką szerokość. Menu powinno mieć naturalną szerokość, jeśli jest miejsce, ale 100% szerokości, jeśli są zbyt duże dla rodzica. To właśnie max-widthnależy zrobić, ale nie zrobić w tym przypadku.
Rory O'Kane
Wprowadziłem jeszcze kilka zmian, oddzielając „wszystkie <select>s” od „długich <select>s” i pracuję teraz na szerokości tła.
Trojan
#viewportnie ma również stałej szerokości. Dlatego umieściłem przycisk Zmień rozmiar rzutni na stronie - abyś mógł sprawdzić, czy wszystko działa bez względu na szerokość rzutni. Podobnie jak .fake-selectsubstytut prawdziwego <select>testowania, tak samo jak #viewportsubstytut prawdziwej rzutni. („Rzutnia” to w zasadzie okno przeglądarki). Umieściłem #viewportna stronie, aby można było łatwo zobaczyć elementy wystające z okienka, zamiast przewijania, aby je zobaczyć.
Rory O'Kane