<fieldset> ridimensiona in modo errato; sembra avere "larghezza minima: contenuto minimo" inamovibile


221

Problema

Ho un punto in <select>cui uno dei suoi <option>valori di testo è molto lungo. Voglio <select>ridimensionare in modo che non sia mai più largo del suo genitore, anche se deve tagliare il testo visualizzato. max-width: 100%dovrebbe farlo.

Prima di ridimensionare:

la pagina prima del ridimensionamento, mostrando l'intero <code> seleziona </code>

Cosa voglio dopo il ridimensionamento:

la mia pagina desiderata dopo il ridimensionamento, con <code> seleziona </code> ritagliandone il contenuto

Ma se carichi questo esempio di jsFiddle e ridimensioni la larghezza del pannello dei risultati in modo che sia più piccola di quella di <select>, puoi vedere che la selezione all'interno della <fieldset>scala non riesce a ridimensionarla.

Quello che vedo dopo il ridimensionamento:

la mia pagina corrente dopo il ridimensionamento, con una barra di scorrimento

Tuttavia, la pagina equivalente con a <div>anziché a<fieldset> viene ridimensionata correttamente. Puoi vederlo e testare le tue modifiche più facilmente se hai una <fieldset>e una <div>accanto all'altra su una pagina . E se elimini i <fieldset>tag circostanti , il ridimensionamento funziona. Il <fieldset>tag sta causando l'interruzione del ridimensionamento orizzontale.

Gli <fieldset>atti è come se ci fosse una regola CSS fieldset { min-width: min-content; }. ( min-contentsignifica, all'incirca, la larghezza più piccola che non fa traboccare un bambino.) Se lo sostituisco <fieldset>con un <div>conmin-width: min-content , sembra esattamente lo stesso. Eppure non esiste alcuna regola min-contentnei miei stili, nel foglio di stile predefinito del browser o visibile in Inspector CSS di Firebug. Ho provato a sovrascrivere tutti gli stili visibili <fieldset>in CSS Inspector di Firebug e nel foglio di stile di Firefox Form.css predefinito , ma questo non ha aiutato. Soprattutto scavalcando min-widthe widthnon ha fatto nulla.

Codice

HTML del fieldset:

<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>

Il mio CSS che dovrebbe funzionare ma non è:

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

select {
    max-width: 100%;
}

Il ripristino delle widthproprietà ai valori predefiniti non fa nulla:

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

Ulteriori CSS in cui provo e non riesco a risolvere il problema :

/* 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;
}

Più dettagli

Il problema si verifica anche in questo esempio jsFiddle più completo e complicato , che è più simile alla pagina Web che sto effettivamente cercando di risolvere. Da ciò si può vedere che <select>non è questo il problema: anche un blocco inline divnon riesce a ridimensionare. Sebbene questo esempio sia più complicato, suppongo che la correzione per il caso semplice sopra risolverà anche questo caso più complicato.

[Modifica: vedi i dettagli del supporto del browser di seguito.]

Una cosa curiosa di questo problema è che se si impostadiv.wrapper { width: 50%; } , gli <fieldset>arresti si ridimensionano automaticamente nel punto in cui il full-size <select>avrebbe colpito il bordo della finestra. Il ridimensionamento avviene come se <select>fosse width: 100%, anche se <select>sembra che abbia width: 50%.

dopo il ridimensionamento con div wrapper con larghezza del 50%

Se dai lo <select>stessowidth: 50% , quel comportamento non si verifica; la larghezza è semplicemente impostata correttamente.

dopo il ridimensionamento con 50% di larghezza selezionare

Non capisco il motivo di quella differenza. Ma potrebbe non essere pertinente.

Ho anche trovato la domanda molto simile HTML fieldset consente ai bambini di espandersi indefinitamente . Il richiedente non è stato in grado di trovare una soluzione e indovina che non esiste una soluzione oltre alla rimozione di <fieldset>. Ma mi chiedo, se è davvero impossibile rendere <fieldset>giusto il display, perché? Ciò che in <fieldset>'s spec o CSS predefinito ( a partire da questa domanda ) fa sì che questo comportamento? Questo comportamento speciale è probabilmente documentato da qualche parte, poiché più browser funzionano in questo modo.

Obiettivo e requisiti di base

Il motivo per cui sto provando a farlo è parte della scrittura di stili mobili per una pagina esistente con una grande forma. Il modulo ha più sezioni e una parte è racchiusa in a <fieldset>. Su uno smartphone (o se si riduce la finestra del browser), la parte della pagina con il <fieldset>è molto più ampia rispetto al resto del modulo. La maggior parte del modulo ne limita la larghezza, ma la sezione <fieldset>non lo fa, costringendo l'utente a ridurre o scorrere verso destra per vedere tutta quella sezione.

Sono cauto nel rimuovere semplicemente il <fieldset>, poiché viene generato su molte pagine in una grande app, e non sono sicuro da quali selettori in CSS o JavaScript potrebbero dipendere da esso.

Posso usare JavaScript se necessario, e una soluzione JavaScript è meglio di niente. Ma se JavaScript è l'unico modo per farlo, sarei curioso di sentire una spiegazione del perché questo non è possibile usando solo CSS e HTML.


Modifica: supporto browser

Sul sito, devo supportare Internet Explorer 8 e versioni successive (abbiamo appena abbandonato il supporto per IE7), l'ultima versione di Firefox e l'ultima versione di Chrome. Questa pagina particolare dovrebbe funzionare anche su smartphone iOS e Android. Un comportamento leggermente degradato ma comunque utilizzabile è accettabile per Internet Explorer 8.

Ho riprovato il mio fieldsetesempio rotto su diversi browser. In realtà funziona già in questi browser:

  • Internet Explorer 8, 9 e 10
  • Cromo
  • Chrome per Android

Si rompe in questi browser:

  • Firefox
  • Firefox per Android
  • Internet Explorer 7

Pertanto, l'unico browser a cui tengo che il codice corrente si interrompa è Firefox (sia su desktop che su dispositivo mobile). Se il codice fosse corretto in modo che funzionasse in Firefox senza romperlo in nessun altro browser, ciò avrebbe risolto il mio problema.

Il modello di sito HTML utilizza Internet Explorer commenti condizionali per aggiungere classi, .ie8e .oldieal <html>elemento. Puoi usare quelle classi nel tuo CSS se hai bisogno di aggirare le differenze di stile in IE. Le classi aggiunte sono le stesse di questa vecchia versione di HTML5 Boilerplate .


41
congratulazioni per questo eccellente resoconto
Alp

Il tuo semplice esempio: dare <fieldset>(o div.wrapper) la larghezza necessaria e select { width:100% }. max-widthsembra dare all'elemento una percentuale della larghezza originale del suo genitore , quindi non si ridimensiona, mentre widthsi ridimensiona con il suo genitore. Penso che farà quello che vuoi (ma avrei potuto fraintendere). Nel violino più complesso, prova a dare ai campi nella colonna di sinistra una larghezza% e una larghezza minima del pixel. Quindi dai i campi a destra 100 - (campi a sinistra -% - larghezza)% larghezza.
Trojan,

Ho appena avuto un altro pensiero: cosa succede se si imposta la larghezza minima del fieldset su 0? Che di per sé probabilmente non risolverà il problema, ma forse vale la pena provare a risolverlo. Sto seguendo un percorso diverso ora, ma se non funziona lo proverò anche io
Trojan

Hai bisogno dell'etichetta e del campo per rimanere sulla stessa linea? Per i viewport piccoli, si sovrappongono. Perché non renderlo in qualche modo reattivo e passare a una nuova linea se necessario?
Trojan,

Risposte:


346

Aggiornamento (25 settembre 2017)

Il bug di Firefox descritto di seguito è stato corretto a partire da Firefox 53 e il collegamento a questa risposta è stato finalmente rimosso dalla documentazione di Bootstrap .

Inoltre, mi scuso sinceramente con i contributori di Mozilla che hanno dovuto bloccare la rimozione del supporto in -moz-documentparte a causa di questa risposta.

La correzione

In WebKit e Firefox 53+, è sufficiente impostare min-width: 0;sul set di min-contentcampi per sovrascrivere il valore predefinito di .¹

Tuttavia, Firefox è un po '... strano quando si tratta di set di campi. Per farlo funzionare nelle versioni precedenti, è necessario modificare la displayproprietà del fieldset su uno dei seguenti valori:

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

Di questi, lo consigliotable-cell . Sia table-rowe table-row-groupsi impedisce di modificare la larghezza, mentre table-columne table-column-groupvi impedisce di cambiare altezza.

Questo (abbastanza ragionevolmente) interromperà il rendering in IE. Dato che solo Gecko ne ha bisogno, puoi legittimamente usare @-moz-document—una delle estensioni CSS proprietarie di Mozilla — per nasconderlo dagli altri browser:

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

(Ecco una demo di jsFiddle .)


Ciò risolve le cose, ma se sei qualcosa come me la tua reazione è stata qualcosa come ...

Che cosa.

V'è una ragione, ma non è abbastanza.

La presentazione predefinita dell'elemento fieldset è assurda ed essenzialmente impossibile da specificare nei CSS. Pensaci: il bordo del fieldset scompare dove è sovrapposto da un elemento legenda, ma lo sfondo rimane visibile! Non c'è modo di riprodurlo con qualsiasi altra combinazione di elementi.

Per finire, le implementazioni sono piene di concessioni al comportamento legacy. Uno di questi è che la larghezza minima di un fieldset non è mai inferiore alla larghezza intrinseca del suo contenuto. WebKit offre un modo per ignorare questo comportamento specificandolo nel foglio di stile predefinito, ma Gecko² fa un ulteriore passo avanti e lo applica nel motore di rendering .

Tuttavia, gli elementi della tabella interna costituiscono un tipo di frame speciale in Gecko. I vincoli dimensionali per gli elementi con questi displayvalori impostati vengono calcolati in un percorso di codice separato , aggirando completamente la larghezza minima imposta imposta sui set di campi.

Ancora una volta, il bug per questo è stato corretto a partire da Firefox 53, quindi non è necessario questo hack se si sta prendendo di mira solo le versioni più recenti.

L'utilizzo è @-moz-documentsicuro?

Per questo problema, sì. @-moz-documentfunziona come previsto in tutte le versioni di Firefox fino alla 53, dove questo bug è stato corretto.

Questo non è un caso. In parte a causa di questa risposta, il bug da limitare @-moz-documentai fogli di stile utente / UA è stato reso dipendente dal bug del fieldset sottostante che veniva corretto per primo.

Oltre a ciò, non utilizzare @-moz-documentper indirizzare Firefox nel tuo CSS , nonostante altre risorse.³


¹ Il valore può essere prefissato. Secondo un lettore, ciò non ha alcun effetto nel browser stock di Android 4.1.2 e probabilmente in altre versioni precedenti; Non ho avuto il tempo di verificarlo.

² Tutti i collegamenti alla fonte Gecko in questa risposta si riferiscono al changeset 5065fdc12408 , impegnato il 29ᵗʰ luglio 2013; potresti voler confrontare le note con la revisione più recente di Mozilla Central .

³ Vedi ad es. SO # 953491: Targeting solo per Firefox con trucchi CSS e CSS: hack CSS mirati a Firefox per articoli ampiamente citati su siti di alto profilo.


5
Avevo appena notato che la correzione si è rotta in IE, solo per venire qui e scoprire che hai già modificato la soluzione. Grazie! La soluzione di hacking Gecko funziona bene per me in ogni browser.
Rory O'Kane,

Mi stavo togliendo i capelli per questo problema, ma la tua soluzione è perfetta - tranne che sembra funzionare solo su Firefox. Qualche suggerimento per altri browser, in particolare i browser mobili? Il mio problema più grande è il ridimensionamento a causa del cambio di orientamento sui dispositivi mobili ...
Adriaan Nel

30
Questa è una risposta assolutamente eccezionale: approfondita, ricercata e concisa. Grazie.
iamkeir,

16
Ovviamente è una risposta assolutamente superba! La documentazione ufficiale di Bootstrap indica qui
Ranhiru Jude Cooray,

1
Bene, non l'ho mai saputo dei set di campi. Oggi ho imparato qualcosa qui.
superluminario

11

Safari su iOS problema con la risposta selezionata

Ho trovato particolarmente utile la risposta di Jordan Grey. Tuttavia non mi è sembrato di risolvere questo problema su Safari iOS.

Il problema per me è semplicemente che il fieldset non può avere una larghezza automatica se l'elemento all'interno ha una larghezza massima in% larghezza.

Risolto il problema

La semplice impostazione del fieldset per avere una larghezza del 100% del suo contenitore sembra aggirare questo problema.

Esempio

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

Fare riferimento a quanto segue per esempi di funzionamento: se si rimuove la larghezza% dal set di campi o lo si sostituisce con auto, non continuerà a funzionare.

JSFiddle | Codepen


1
Questa è un'ottima aggiunta al problema generale, non pensavo di provarlo su iOS. :) Penso quasi che valga la pena farti e rispondere autonomamente come nuova domanda. Proverò a fare alcuni test extra questa settimana e collegherò a questa risposta se funziona.
Jordan Grey,

3

Ho lottato per molte ore con questo, e fondamentalmente, il browser sta applicando uno stile calcolato che devi sovrascrivere nel tuo CSS. Dimentico l'esatta proprietà che viene impostata sugli fieldsetelementi rispetto a divs (forse min-width?).

Il mio miglior consiglio sarebbe quello di cambiare il tuo elemento in a div, copiare gli stili calcolati dal tuo ispettore, quindi cambiare l'elemento in fieldsete confrontare gli stili calcolati per trovare il colpevole.

Spero che aiuti.

Aggiornamento: l' aggiunta di display: table-cellaiuto nei browser non Chrome.


Ho confrontato tutte le proprietà CSS di fieldsete divin questa pagina in Firebug. Non ha aiutato In Firebug, le uniche proprietà con valori diversi erano widthe perspective-origin. L ' widthera numericamente diverso, ma l fieldset' widthera auto, come l div'. E perspective-originè solo una funzione del width- 50% di default.
Rory O'Kane,

Tuttavia, il confronto ha aiutato in qualche modo in Chrome Web Inspector. Ho scoperto subito dopo aver aperto il mio esempio che funziona in Chrome! Avevo aggiunto il min-width: 0;mio esempio dopo il test in Chrome, ma sembra che abbia risolto il problema in Chrome. Il Web Inspector mi dice che Chrome ha lo stile min-width: -webkit-min-content;, proprio come avevo ipotizzato. Quindi ora ho solo bisogno di risolvere il problema in Firefox e possibilmente altri browser in cui non ho ancora testato.
Rory O'Kane,

Sono contento che abbia aiutato un po '. Sono stato in grado di risolvere il mio problema in altri browser con "display: table-cell". Non penso che non sia la soluzione migliore, ma spero che funzioni per te.
phdj,

2

.fake-select { white-space:nowrap; }ha fatto sì che il fieldset interpretasse l' .fake-selectelemento in base alla sua larghezza originale, piuttosto che alla sua larghezza forzata (anche quando l'overflow è nascosto).

Rimuovere questa regola, e il cambiamento .fake-select's max-width:100%ad appena width:100%e si adatta tutto. L'avvertenza è che vedi tutto il contenuto di fake-select, ma non penso che sia così male, e ora si adatta orizzontalmente.

Aggiornamento: con le regole correnti nel seguente violino (che contiene solo selezioni reali), i figli del fieldset sono vincolati alla larghezza corretta. Oltre a rimuovere le regole .fake-selecte correggere i commenti (da // commenta /* comment */, ho notato cambiamenti nel CSS del violino.

Ora capisco meglio il tuo problema e il violino riflette alcuni progressi. Ho impostato le regole predefinite per tutti <select>i, e mi riservo .xxlargeper quelli che sai saranno più larghi di 480 px (e questo funziona solo perché conosci la larghezza di #viewport, e posso aggiungere manualmente la classe a quelli troppo larghi. Richiede solo un po 'di test )

Prova


Qualsiasi correzione tu usi deve funzionare su un reale <select>. Il sito attuale ha solo <select>s. Il punto fake-selectera di avere le regole di layout di a <select>senza essere effettivamente a <select>, solo per dimostrare che questo comportamento non è un bug del browser che si verifica solo con <select>elementi. Quindi cambiando gli stili di .fake-selectessere meno come una <select>sconfitta lo scopo.
Rory O'Kane,

Ho sostituito la .fake-selectscatola con una <select>(identica a quella già presente) per dimostrare. Gli elementi sono tutti vincolati alla larghezza del genitore (tranne quando si fa clic su di esso, facendo cadere le opzioni, che sono visualizzate su una sola riga, ma non penso che tu possa controllarlo). Ho anche rimosso tutte le regole per .fake-select. Il violino attuale fa quello che ti serve?
Trojan,

Non è così. width: 100%funziona con i menu lunghi nella pagina, ma non funziona correttamente con i menu brevi. Espande i menu brevi per l'intera larghezza della pagina, ma voglio che i menu brevi mantengano la larghezza ridotta. I menu dovrebbero essere la loro larghezza naturale se c'è spazio, ma la larghezza del 100% se sono troppo grandi per i loro genitori. Questo è ciò che max-widthdovrebbe fare, ma non farlo in questo caso.
Rory O'Kane,

Ho apportato ulteriori modifiche, separando "tutte le <select>s" da "lunghe <select>" e ora sto lavorando solo sulla larghezza dello sfondo.
Trojan,

#viewportnon ha nemmeno una larghezza fissa. Ecco perché ho messo il pulsante Ridimensiona finestra sulla pagina, in modo da poter verificare che tutto funzioni indipendentemente dalla larghezza della finestra. Proprio come .fake-selectè un sostituto per un vero <select>test, #viewportè un sostituto per un vero viewport. (Una "finestra" è sostanzialmente una finestra del browser). Ho incluso #viewportnella pagina in modo da poter vedere facilmente gli elementi che sporgono dal viewport, piuttosto che dover scorrere per vederli.
Rory O'Kane,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.