Transizione flessibile: allunga (o riduci) per adattarla al contenuto


9

Ho codificato uno script (con l'aiuto di un utente qui) che mi consente di espandere un div selezionato e di fare in modo che gli altri div si comportino di conseguenza allungando allo stesso modo per adattarsi allo spazio rimanente (tranne il primo a cui la larghezza è fissa).

Ed ecco una foto di ciò che voglio ottenere:

inserisci qui la descrizione dell'immagine

Per questo uso flex e transizioni.

Funziona bene, ma lo script jQuery specifica un valore di allungamento "400%" (che è ottimo per i test).

Ora vorrei che il div selezionato si espandesse / restringesse per adattarsi esattamente al contenuto anziché al valore fisso "400%".

Non ho idea di come potrei farlo.

È possibile ?

Ho cercato di clonare il div, adattarlo al contenuto, ottenere il suo valore e quindi utilizzare questo valore per la transizione MA questo significa che ho una larghezza iniziale in percentuale ma un valore target in pixel. Non funziona

E se converto il valore in pixel in percentuale, il risultato non si adatta esattamente al contenuto per qualsiasi motivo.

In tutti i casi, questo sembra un po 'un modo complicato per ottenere ciò che voglio comunque.

Non esiste alcuna proprietà flessibile che potrebbe essere trasferita per adattarsi al contenuto di un div selezionato?

Ecco il codice ( modificato / semplificato per una lettura migliore ):

var expanded = '';

$(document).on("click", ".div:not(:first-child)", function(e) {

  var thisInd =$(this).index();
  
  if(expanded != thisInd) {
    //fit clicked fluid div to its content and reset the other fluid divs 
    $(this).css("width", "400%");
    $('.div').not(':first').not(this).css("width", "100%");
    expanded = thisInd;
  } else {
    //reset all fluid divs
    $('.div').not(':first').css("width", "100%");
    expanded = '';
  }
});
.wrapper {
  overflow: hidden;
  width: 100%;
  margin-top: 20px;
  border: 1px solid black;
  display: flex;
  justify-content: flex-start;
}

.div {
  overflow: hidden;
  white-space: nowrap;
  border-right: 1px solid black;
  text-align:center;
}

.div:first-child {
  min-width: 36px;
  background: #999;
}

.div:not(:first-child) {
  width: 100%;
  transition: width 1s;
}

.div:not(:first-child) span {
  background: #ddd;
}

.div:last-child {
  border-right: 0px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Click on the div you want to fit/reset (except the first div)

<div class="wrapper">

  <div class="div"><span>Fixed</span></div>
  <div class="div"><span>Fluid (long long long long long text)</span></div>
  <div class="div"><span>Fluid</span></div>
  <div class="div"><span>Fluid</span></div>

</div>

Ecco il jsfiddle:

https://jsfiddle.net/zajsLrxp/1/

EDIT: Ecco la mia soluzione di lavoro con l'aiuto di tutti voi (dimensioni aggiornate sul ridimensionamento della finestra + numero di div e larghezza della prima colonna calcolata dinamicamente):

var tableWidth;
var expanded	   = '';
var fixedDivWidth  = 0;
var flexPercentage = 100/($('.column').length-1);

$(document).ready(function() {

    // Set width of first fixed column
    $('.column:first-child .cell .fit').each(function() {
        var tempFixedDivWidth = $(this)[0].getBoundingClientRect().width;
        if( tempFixedDivWidth > fixedDivWidth ){fixedDivWidth = tempFixedDivWidth;}
    });
    $('.column:first-child'  ).css('min-width',fixedDivWidth+'px')
	
    //Reset all fluid columns
    $('.column').not(':first').css('flex','1 1 '+flexPercentage+'%')
})

$(window).resize( function() {

    //Reset all fluid columns
    $('.column').not(':first').css('flex','1 1 '+flexPercentage+'%')	
    expanded   = '';
})

$(document).on("click", ".column:not(:first-child)", function(e) {
	
    var thisInd =$(this).index();	
	
    // if first click on a fluid column
    if(expanded != thisInd) 
    {
        var fitDivWidth=0;
    
        // Set width of selected fluid column
        $(this).find('.fit').each(function() {
            var c = $(this)[0].getBoundingClientRect().width;
            if( c > fitDivWidth ){fitDivWidth = c;}
        });
        tableWidth = $('.mainTable')[0].getBoundingClientRect().width; 
        $(this).css('flex','0 0 '+ 100/(tableWidth/fitDivWidth) +'%')
		
        // Use remaining space equally for all other fluid column
        $('.column').not(':first').not(this).css('flex','1 1 '+flexPercentage+'%')
		
        expanded = thisInd;
    }

    // if second click on a fluid column
    else
    {
        //Reset all fluid columns
        $('.column').not(':first').css('flex','1 1 '+flexPercentage+'%')

        expanded = '';
    }
  
});
body{
    font-family: 'Arial';
    font-size: 12px;
    padding: 20px;
}

.mainTable {
    overflow: hidden;
    width: 100%;
    border: 1px solid black;
    display: flex;
    margin-top : 20px;
}

.cell{
    height: 32px;
    border-top: 1px solid black;
    white-space: nowrap;
}

.cell:first-child{
    background: #ccc;
    border-top: none;
}

.column {
    border-right: 1px solid black;
    transition: flex 0.4s;
    overflow: hidden;
    line-height: 32px;
    text-align: center;
}

.column:first-child {
    background: #ccc;
}

.column:last-child {
  border-right: 0px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<span class="text">Click on the header div you want to fit/reset (except the first one which is fixed)</span>

<div class="mainTable">
    <div class="column">
        <div class="cell"><span class="fit">Propriété</span></div>
        <div class="cell"><span class="fit">Artisan 45</span></div>
        <div class="cell"><span class="fit">Waterloo 528</span></div>	    
    </div>
		  
    <div class="column">	    
        <div class="cell"><span class="fit">Adresse</span></div>
        <div class="cell"><span class="fit">Rue du puit n° 45 (E2)</span></div>
        <div class="cell"><span class="fit">Chaussée de Waterloo n° 528 (E1)</span></div>	    
    </div>
		
    <div class="column">	    
        <div class="cell"><span class="fit">Commune</span></div>
        <div class="cell"><span class="fit">Ixelles</span></div>
        <div class="cell"><span class="fit">Watermael-Boitsfort</span></div>	    
    </div>
		    
    <div class="column">	    
        <div class="cell"><span class="fit">Ville</span></div>
        <div class="cell"><span class="fit">Marche-en-Famenne</span></div>
        <div class="cell"><span class="fit">Bruxelles</span></div>	    
    </div>
		  
    <div class="column">
        <div class="cell"><span class="fit">Surface</span></div>
        <div class="cell"><span class="fit">120 m<sup>2</sup></span></div>
        <div class="cell"><span class="fit">350 m<sup>2</sup></span></div>	    
    </div>
</div>

Ed ecco un esempio completo al lavoro (stili + imbottitura + più dati):

https://jsfiddle.net/zrqLowx0/2/

Grazie a tutti !


1
Sii gentile abbastanza da aggiungere un risultato finale alla tua domanda mostrando ciò che hai creato dopo tutto questo. Sono sinceramente interessato a come appare. Grazie! (... forse qualche intuizione su a cosa serve? ...)
Rene van der Lende,

Ok, lo farò non appena avrò pulito la mia sceneggiatura da tutti gli elementi non necessari (credo che tra qualche minuto). Detto questo, come posso farlo? Devo solo modificare il post originale e aggiungere la mia opinione su questo anche se la risposta di Azametzin va benissimo? Ho cambiato alcune cose ma è uguale al 90% (sarei felice di mostrarlo però) ..
Bachir Messaouri

Ecco il punto: emulo una specie di foglio di calcolo Excel e voglio che le persone siano in grado di fare clic su una determinata colonna per adattarsi al contenuto. Normalmente una tabella farebbe ma le colonne non si comportano correttamente quando si tratta di ridimensionarle nel modo desiderato (facile espandere una colonna alla volta ma è difficile far sì che le altre si comportino in tempo reale per occupare lo spazio rimanente nel modo flex farebbe per i div. Si espandono in modo irregolare. Ho fatto qualche ricerca e ciò che voglio è impossibile con le tabelle). Ci sono altri motivi per cui non utilizzo una tabella o una tabella dei dati, ma non rientra nell'ambito di questo post.
Bachir Messaouri,

1
Abbastanza facile, basta premere "modifica", andare alla fine della domanda e iniziare ad aggiungere alcune ponderazioni prozaiche, incluso un nuovo frammento (tutto). Assicurati di "nascondere lo snippet" per impostazione predefinita (casella di controllo in basso a sinistra), meno invadente / che distrae per i "nuovi lettori". Il mio primo pensiero è stato che lo avresti usato per alcuni widget, icone e icone simili a "armoniche". Foglio di calcolo eh, ho un po 'di schermo intero, che si estende in tutto il luogo su / giù / a sinistra / a destra in giro usando gli stessi principi discussi qui (se sei interessato).
Rene van der Lende

2
Fantastico hai fatto il possibile per pubblicare il risultato finale. Rende le risposte alle domande su COSÌ molto più gratificanti! Cudos ...
Rene van der Lende,

Risposte:


4

È possibile risolverlo usando max-widthe calc().

Innanzitutto, sostituisci width: 100%con flex: 1per i div in CSS, quindi cresceranno, il che è meglio in questo caso. Inoltre, utilizzare la transizione per max-width.

Ora, dobbiamo memorizzare alcuni valori rilevanti:

  • La quantità di div che saranno animati ( divsLengthvariabile) - 3 in questo caso.
  • La larghezza totale utilizzata per il div fisso e i bordi ( extraSpacevariabile) - 39px in questo caso.

Con queste 2 variabili, possiamo impostare un valore predefinito max-width( defaultMaxWidthvariabile) su tutti i div, oltre a usarli in seguito. Ecco perché vengono archiviati a livello globale.

Il defaultMaxWidth è calc((100% - extraSpace)/divsLength).

Ora, entriamo nella funzione clic:

Per espandere il div, la larghezza del testo di destinazione verrà memorizzata in una variabile chiamata textWidthe verrà applicata al div comemax-width. utilizzo .getBoundingClientRect().width(poiché restituisce il valore in virgola mobile).

Per i restanti div, viene creato un calc()per max-widthche verrà applicato a loro.
È:calc(100% - textWidth - extraScape)/(divsLength - 1) .
Il risultato calcolato è la larghezza che dovrebbe essere ogni div rimanente.

Quando si fa clic sul div espanso, ovvero per tornare alla normalità, il valore predefinito max-widthviene nuovamente applicato a tutti gli .divelementi.

var expanded = false,
    divs = $(".div:not(:first-child)"),
    divsLength = divs.length,
    extraSpace = 39, //fixed width + border-right widths 
    defaultMaxWidth = "calc((100% - " + extraSpace + "px)/" + divsLength + ")";

    divs.css("max-width", defaultMaxWidth);

$(document).on("click", ".div:not(:first-child)", function (e) {
  var thisInd = $(this).index();

  if (expanded !== thisInd) {
    
    var textWidth = $(this).find('span')[0].getBoundingClientRect().width;  
    var restWidth = "calc((100% - " + textWidth + "px - " + extraSpace + "px)/" + (divsLength - 1) + ")";
    
    //fit clicked fluid div to its content and reset the other fluid divs 
    $(this).css({ "max-width": textWidth });
    $('.div').not(':first').not(this).css({ "max-width": restWidth });
    expanded = thisInd;
  } else {
    //reset all fluid divs
    $('.div').not(':first').css("max-width", defaultMaxWidth);
    expanded = false;
  }
});
.wrapper {
  overflow: hidden;
  width: 100%;
  margin-top: 20px;
  border: 1px solid black;
  display: flex;
  justify-content: flex-start;
}

.div {
  overflow: hidden;
  white-space: nowrap;
  border-right: 1px solid black;
  text-align:center;
}

.div:first-child {
  min-width: 36px;
  background: #999;
}

.div:not(:first-child) {
  flex: 1;
  transition: max-width 1s;
}

.div:not(:first-child) span {
  background: #ddd;
}

.div:last-child {
  border-right: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

Click on the div you want to fit/reset (except the first div)

<div class="wrapper">

  <div class="div"><span>Fixed</span></div>
  <div class="div"><span>Fluid (long long long long text)</span></div>
  <div class="div"><span>Fluid</span></div>
  <div class="div"><span>Fluid</span></div>

</div>

Questo approccio si comporta in modo dinamico e dovrebbe funzionare su qualsiasi risoluzione.
L'unico valore necessario per codificare è la extraSpacevariabile.


1
Sì, questo è un problema che richiede un calcolo preciso e una maggiore comprensione dell'effetto di riduzione del flexbox. Non ho ancora una soluzione "perfetta" ma vorrei riprovare in futuro, quindi ho aggiunto la tua domanda ai segnalibri per un'altra volta per cercare di capire come farlo nel modo giusto. È una bella sfida
Azametzin,

1
Cordiali saluti, ho modificato il codice da allora per semplificare la lettura e il test. Ho aggiunto gli span che sono una buona aggiunta. Non ho ancora aggiunto le transizioni di larghezza massima, ma finora non hanno funzionato alla grande.
Bachir Messaouri,

1
Freddo. L'ho appena risolto. Sto modificando la risposta.
Azametzin,

1
Bello! Prenditi il ​​tuo tempo mentre sono fuori per un paio d'ore comunque ;-)
Bachir Messaouri

1
Oooh, ho appena visto il tuo commento modificato (mi aspettavo un commento di aggiornamento). L'uso di getBoundingClientRect () è molto intelligente e non mi è venuto in mente (lo stesso per tenere conto della larghezza fissa di 39 px). Proverò un po 'di più per vedere se funziona per qualsiasi dimensione di Windows e qualsiasi numero di div Fluid. Tornerò da te. Grazie mille! PS: la tua soluzione funziona con jQuery 3.3.1 ma NON con 3.4.1, per qualsiasi motivo.
Bachir Messaouri,

3

È necessario gestire le funzioni di larghezza o calc. Flexbox avrebbe una soluzione.

Per rendere uguali tutti i div (non il primo) che usiamo flex: 1 1 auto.

<div class="wrapper">
  <div class="div"><span>Fixed</span></div>
  <div class="div"><span>Fluid (long long long long text)</span></div>
  <div class="div"><span>Fluid</span></div>
  <div class="div"><span>Fluid</span></div>
</div>

Definisci le regole di flessione per il tuo div normale e div selezionato. transition: flex 1s;È tuo amico. Per quello selezionato non abbiamo bisogno di flex flex, quindi usiamo flex: 0 0 auto;

.wrapper {
  width: 100%;
  margin-top: 20px;
  border: 1px solid black;
  display: flex;
}

.div {
  white-space: nowrap;
  border-right: 1px solid black;
  transition: flex 1s;
  flex: 1 1 auto;
}

.div.selected{
  flex: 0 0 auto;
}

.div:first-child {
  min-width: 50px;
  background: #999;
  text-align: center;
}

.div:not(:first-child) {
  text-align: center;
}

.div:last-child {
  border-right: 0px;
}

div:not(:first-child) span {
  background: #ddd;
}

Aggiungi la classe selezionata ogni volta che l'utente fa clic su un div. Puoi anche usare l'interruttore per il secondo clic in modo da poter salvare gli elementi selezionati in una mappa e mostrare più elementi selezionati (ovviamente non con questo esempio di codice).

$(document).on("click", ".div:not(:first-child)", function(e) {
  const expanded = $('.selected');
  $(this).addClass("selected");
  if (expanded) {
    expanded.removeClass("selected");
  }
});

https://jsfiddle.net/f3ao8xcj/


Grazie. Devo studiare il tuo caso perché fa il contrario di quello che voglio e non sono ancora sicuro di come annullarlo (forse è banale). Per impostazione predefinita, voglio che tutti i div fluidi siano ugualmente grandi. è solo quando faccio clic su uno che si adatta al suo contenuto (e gli altri div fluidi condividono equamente lo spazio rimanente). E se clicco una seconda volta sullo stesso div, tutti i div Fluid si ripristinano, condividendo di nuovo ugualmente lo spazio senza preoccuparsi del loro contenuto. Quindi, se non sbaglio, è l'esatto contrario di quello che hai fatto. Non sono ancora sicuro di come invertire ciò.
Bachir Messaouri,

Tuttavia, se questo può essere invertito, mi piace davvero quanto sia elegante e semplice la tua soluzione in quanto non è necessario scherzare con l'attributo css width. Flex sembra gestirlo solo internamente. Detto questo, ho il sospetto che sarà più difficile ottenere il contrario. Aspetta e vedi. Grazie per il seguito.
Bachir Messaouri,

1
@BachirMessaouri è di nuovo abbastanza semplice. Si prega di consultare la versione aggiornata.
uragano,

Adoro l'eleganza della tua sceneggiatura, ma le tue soluzioni considerano solo parti della mia richiesta. La mia richiesta non riguarda solo l'adattamento del contenuto, ma anche la condivisione dello spazio rimanente equamente prima, durante e dopo aver fatto clic sui div fluid. La tua sceneggiatura non si rivolge molto bene, se non del tutto. Ed è ciò che rende il tutto un bruciore al cervello. Ho aggiunto un'immagine nel post originale per chiarire le mie esigenze. L'altro script sopra inizia esattamente dove il tuo non riesce a consegnare. Stranamente sembra che un mix di entrambi i tuoi script potrebbe funzionare. Sto testando e una volta fatto torno da te.
Bachir Messaouri,

@BachirMessaouri Quello che ti manca qui è che nessuno ha bisogno di fornire una soluzione perfetta per il tuo caso. Flex transition: Stretch (or shrink) to fit contentè il titolo della tua domanda e questa risposta è il semplice esempio di come farlo. È tua responsabilità trovare la soluzione per il tuo caso.
uragano,

2

Dopo alcune versioni di prova, questa sembra essere la mia soluzione più breve e diretta.

Tutto ciò che essenzialmente deve essere fatto è che Flexbox allunghi gli <div>elementi ai loro limiti per impostazione predefinita, ma quando si fa <span>clic, vincoli l'allungamento <div>alla <span>larghezza ...

pseudo codice:

when <span> clicked and already toggled then <div> max-width = 100%, reset <span> toggle state

otherwise <div> max-width = <span> width, set <span> toggle state

Ho diviso il CSS in un "meccanismo pertinente" e nella sezione "solo accattivanti" per una facile lettura (e ricodifica del codice).

Il codice è fortemente commentato, quindi non c'è molto testo qui ...

Quirk In qualche modo c'è un ulteriore ritardo nel transitionpassaggio divda da max-width: 100%a max-width = span width. Ho verificato questo comportamento in Chrome, Edge, IE11 e Firefox (tutti W10) e sembra che tutti abbiano questa stranezza. È in corso un ricalcolo interno del browser o forse il transitiontempo viene utilizzato due volte ("sembra"). Vice Versa, stranamente, non c'è ritardo extra.

Tuttavia, con un breve tempo di transizione (ad es. 150 ms, come sto usando ora) questo ritardo aggiuntivo non è / appena percettibile. (Bello per un'altra domanda SO ...)

AGGIORNARE

Nuova versione che reimposta tutte le altre <div>. Odio davvero il nervosismo, ma ciò è dovuto allo stretching Flexbox e al transitionvalore. Senza transitionsalti visibili. Devi provare cosa funziona per te.

Ho solo aggiunto document.querySelectorAll()al codice javascript.


Grazie mille per la tua proposta Non soddisfa i miei bisogni (ma è un ottimo punto di partenza) poiché quando facciamo clic su un div fluido, si adatta bene, ma le altre due div non si espandono allo stesso modo per occupare lo spazio rimanente. Qual è il 50% del problema. E sì, c'è anche questa stranezza. La sceneggiatura di Azametzin fa il lavoro al 100% senza eccentricità .. Ho finito per mescolare sia la sua sceneggiatura che l'uso della transizione flessibile invece della larghezza massima e funziona come un incantesimo. Grazie mille per il tuo aiuto !
Bachir Messaouri,

Secondo il commento di @Azametzin: Sono stati molti giorni divertenti in questa saga. :). Forse la prossima volta inizi con l'immagine?
Rene van der Lende,

Ok. Pensavo che la spiegazione fosse sufficiente, ma ovviamente mi sbagliavo. L'immagine è lì da ieri però. Ma ho capito il tuo punto. Modificherò il mio messaggio originale per mostrare il mio script personalizzato in pochi minuti. Grazie mille !
Bachir Messaouri,

Post originale aggiornato con la soluzione personalizzata!
Bachir Messaouri,
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.