Flex-Übergang: Dehnen (oder verkleinern), um den Inhalt anzupassen

9

Ich habe ein Skript (mit Hilfe eines Benutzers hier) codiert, mit dem ich ein ausgewähltes Div erweitern und die anderen Divs entsprechend verhalten kann, indem ich es gleichmäßig strecke, um es an den verbleibenden Platz anzupassen (mit Ausnahme des ersten, dessen Breite festgelegt ist).

Und hier ist ein Bild von dem, was ich erreichen möchte:

Geben Sie hier die Bildbeschreibung ein

Dafür benutze ich Flex und Übergänge.

Es funktioniert gut, aber das jQuery-Skript gibt einen Dehnungswert von "400%" an (der sich hervorragend zum Testen eignet).

Jetzt möchte ich, dass das ausgewählte div erweitert / verkleinert wird, um genau zum Inhalt zu passen, anstatt zum festen Wert "400%".

Ich habe keine Ahnung, wie ich das machen könnte.

Ist es möglich ?

Ich habe versucht, das div zu klonen, es an den Inhalt anzupassen, seinen Wert abzurufen und diesen Wert dann für den Übergang zu verwenden, ABER dies bedeutet, dass ich eine anfängliche Breite in Prozent, aber einen Zielwert in Pixel habe. Das geht nicht

Und wenn ich den Pixelwert in Prozent umrechne, passt das Ergebnis aus irgendeinem Grund nicht genau zum Inhalt.

In allen Fällen scheint dies ein etwas komplizierter Weg zu sein, um das zu erreichen, was ich sowieso will.

Gibt es keine Flex-Eigenschaft, die geändert werden könnte, um dem Inhalt eines ausgewählten Divs zu entsprechen?

Hier ist der Code ( bearbeitet / vereinfacht, da zum besseren Lesen ):

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>

Hier ist die jsfiddle:

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

BEARBEITEN: Hier ist meine Arbeitslösung mit Hilfe von Ihnen allen (Größen, die beim Ändern der Fenstergröße aktualisiert wurden + Anzahl der Divs und dynamisch berechnete Breite der ersten Spalte):

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>

Und hier ist ein vollwertiges Beispiel bei der Arbeit (Stile + Auffüllen + mehr Daten):

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

Danke euch allen !

Bachir Messaouri
quelle
1
Bitte seien Sie so freundlich , Ihrer Frage ein Endergebnis hinzuzufügen , das zeigt, was Sie nach all dem erstellt haben. Ich bin wirklich interessiert daran, wie es aussieht. Vielen Dank! (... vielleicht ein Einblick, wofür es verwendet wird? ...)
Rene van der Lende
Ok, ich mache das, sobald ich mein Skript von allen unnötigen Elementen befreit habe (in ein paar Minuten, denke ich). Das heißt, wie mache ich das? Bearbeite ich nur den Originalbeitrag und füge meine Meinung dazu hinzu, obwohl die Antwort von Azametzin vollkommen in Ordnung ist? Ich ändere ein paar Dinge, aber es ist zu 90% gleich (ich würde es gerne zeigen).
Bachir Messaouri
Hier ist der Punkt: Ich emuliere eine Art Excel-Tabelle und möchte, dass die Benutzer auf eine bestimmte Spalte klicken können, um sie an den Inhalt anzupassen. Normalerweise würde eine Tabelle funktionieren, aber Spalten verhalten sich nicht richtig, wenn es darum geht, die Größe so zu ändern, wie ich es möchte (es ist einfach, jeweils eine Spalte zu erweitern, aber es ist schwierig, die anderen in Echtzeit zu verhalten, um den verbleibenden Platz so zu belegen Flex würde für Divs reichen. Sie dehnen sich nur unregelmäßig aus. Ich habe einige Nachforschungen angestellt und was ich will, ist mit Tabellen unmöglich. Es gibt andere Gründe, warum ich keine Tabelle oder Datentabelle verwende, aber dies liegt außerhalb des Bereichs dieses Beitrags.
Bachir Messaouri
1
Einfach genug, klicken Sie einfach auf "Bearbeiten", gehen Sie zum Ende der Frage und fügen Sie einige prozaische Überlegungen hinzu, einschließlich eines neuen Snippets (alles). Stellen Sie sicher, dass das Snippet standardmäßig ausgeblendet ist (Kontrollkästchen unten links), damit es für neue Leser weniger aufdringlich / ablenkend ist. Mein erster Gedanke war, dass Sie es für ein ausgefallenes 'Mundharmonika'-ähnliches Widget, Symbole und alles verwenden würden. Tabellenkalkulation huh, ich habe einen Vollbildmodus, der sich überall nach oben / unten / links / rechts erstreckt und nach den gleichen Prinzipien herumliegt, die hier besprochen wurden (wenn Sie interessiert sind).
Rene van der Lende
2
Großartig, dass Sie die Extrameile gegangen sind, um das Endergebnis zu veröffentlichen. Macht das Beantworten von Fragen zu SO viel lohnender! Cudos ...
Rene van der Lende

Antworten:

4

Es ist möglich, es mit max-widthund zu lösen calc().

Erstens ersetzen width: 100%mit flex: 1der divs in CSS, so dass sie wachsen wird, was in diesem Fall besser ist. Verwenden Sie außerdem den Übergang für max-width.

Jetzt müssen wir einige relevante Werte speichern:

  • Die Anzahl der zu animierenden Divs ( divsLengthvariabel) - 3 in diesem Fall.
  • Die Gesamtbreite für das feste Div und die Ränder ( extraSpacevariabel) - in diesem Fall 39px.

Mit diesen beiden Variablen können wir einen Standardwert max-width( defaultMaxWidthVariable) für alle Divs festlegen und diese später verwenden. Deshalb werden sie global gespeichert.

Das defaultMaxWidthist calc((100% - extraSpace)/divsLength).

Geben Sie nun die Klickfunktion ein:

Um die div zu erweitern, wird die Breite des Zieltextes in einer Variablen aufgerufen werden gespeichert , textWidthund es wird auf die div angewandt werden , wie max-width.es verwendet .getBoundingClientRect().width(da sie den Fließkommawert zurück).

Für die verbleibenden Divs wird ein calc()For erstellt max-width, das auf sie angewendet wird.
Es ist : calc(100% - textWidth - extraScape)/(divsLength - 1).
Das berechnete Ergebnis ist die Breite, die jedes verbleibende Div haben sollte.

Wenn Sie auf das erweiterte div klicken, dh zur Normalität zurückkehren, wird die Standardeinstellung max-widtherneut auf alle .divElemente angewendet .

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>

Dieser Ansatz verhält sich dynamisch und sollte bei jeder Auflösung funktionieren.
Der einzige Wert, den Sie für den Hardcode benötigen, ist die extraSpaceVariable.

Azametzin
quelle
1
Ja, dies ist ein Problem, das eine genaue Berechnung und ein besseres Verständnis des Flexbox-Schrumpfeffekts erfordert. Ich habe noch keine "perfekte" Lösung, möchte sie aber in Zukunft erneut versuchen. Deshalb habe ich Ihre Frage ein weiteres Mal mit einem Lesezeichen versehen, um herauszufinden, wie Sie es richtig machen können. Es ist eine gute Herausforderung.
Azametzin
1
Nur zu Ihrer Information, ich habe den Code seitdem bearbeitet, um das Lesen und Testen zu vereinfachen. Ich habe die Spannweiten hinzugefügt, die eine gute Ergänzung sind. Ich habe die Übergänge mit maximaler Breite noch nicht hinzugefügt, aber sie haben bisher nicht besonders gut funktioniert.
Bachir Messaouri
1
Cool. Ich habe es gerade gelöst. Ich bearbeite die Antwort.
Azametzin
1
Nett! Nehmen Sie sich Zeit, da ich sowieso ein paar Stunden
unterwegs
1
Oooh, ich habe gerade Ihren bearbeiteten Kommentar gesehen (hatte einen Update-Kommentar erwartet). Die Verwendung von getBoundingClientRect () ist sehr clever und ist mir nicht in den Sinn gekommen (dasselbe gilt für die Berücksichtigung der festen Breite von 39 px). Ich werde ein bisschen mehr testen, um zu sehen, ob es für jede Fenstergröße und jede Anzahl von Fluid-Divs funktioniert. Ich melde mich bei dir. Vielen Dank! PS: Ihre Lösung funktioniert mit jQuery 3.3.1, aber NICHT mit 3.4.1, aus welchem ​​Grund auch immer.
Bachir Messaouri
3

Sie müssen sich mit den Breiten- oder Berechnungsfunktionen befassen. Flexbox hätte eine Lösung.

Um alle Divs gleich zu machen (nicht die erste), verwenden wir 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>

Definieren Sie Flex-Regeln für Ihr normales Div und das ausgewählte Div. transition: flex 1s;ist dein Freund. Für ausgewählte benötigen wir kein Flex Grow, also verwenden wir 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;
}

Fügen Sie jedes Mal eine ausgewählte Klasse hinzu, wenn der Benutzer auf ein Div klickt. Sie können auch den Umschalter für den zweiten Klick verwenden, um ausgewählte Elemente in einer Karte zu speichern und mehrere ausgewählte Elemente anzuzeigen (natürlich nicht mit diesem Codebeispiel).

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

https://jsfiddle.net/f3ao8xcj/

Hurrikan
quelle
Vielen Dank. Ich muss Ihren Fall untersuchen, weil er das Gegenteil von dem bewirkt, was ich will, und ich bin mir noch nicht sicher, wie ich das umkehren soll (vielleicht ist es trivial). Ich möchte standardmäßig, dass alle Fluiddivs gleich groß sind. Nur wenn ich auf eines klicke, passt dieses zu seinem Inhalt (und die anderen flüssigen Divs teilen sich den verbleibenden Platz zu gleichen Teilen). Und wenn ich ein zweites Mal auf dasselbe Div klicke, werden alle Fluid-Divs zurückgesetzt und teilen sich den Speicherplatz wieder gleichmäßig, ohne sich um ihren Inhalt zu kümmern. Wenn ich mich nicht irre, ist es genau das Gegenteil von dem, was Sie getan haben. Ich bin mir noch nicht sicher, wie ich das umkehren soll.
Bachir Messaouri
Wenn dies jedoch umgekehrt werden kann, gefällt mir sehr, wie elegant und einfach Ihre Lösung ist, da Sie nicht mit dem Attribut css width herumspielen müssen. Flex scheint dies nur intern zu erledigen. Trotzdem habe ich eine Ahnung, dass es schwieriger sein wird, den umgekehrten Weg zu finden. Warten wir es ab. Vielen Dank für das Follow-up.
Bachir Messaouri
1
@ BachirMessaouri es wieder ziemlich einfach. Bitte beachten Sie die aktualisierte Version.
Hurrikan
Ich liebe die Eleganz Ihres Skripts, aber Ihre Lösungen berücksichtigen nur Teile meiner Anfrage. Bei meiner Anfrage geht es nicht nur darum, den Inhalt anzupassen, sondern auch darum, den verbleibenden Speicherplatz vor, während und nach dem Klicken auf die Fluid Divs gleichmäßig zu teilen. Ihr Skript spricht das nicht sehr gut an, wenn überhaupt nicht. Und das macht das Ganze zu einem Brain Burner. Ich habe dem ursprünglichen Beitrag ein Bild hinzugefügt, um meine Bedürfnisse sehr deutlich zu machen. Das andere Skript oben beginnt genau dort, wo Ihr Skript nicht geliefert werden kann. Es scheint seltsam, dass eine Mischung aus beiden Skripten funktionieren könnte. Ich teste und wenn ich fertig bin, melde ich mich wieder bei Ihnen.
Bachir Messaouri
@BachirMessaouri Was Sie hier vermissen, ist, dass niemand eine perfekte Lösung für Ihren Fall anbieten muss. Flex transition: Stretch (or shrink) to fit contentist Ihr Fragentitel und diese Antwort ist das einfache Beispiel dafür. Es liegt in Ihrer Verantwortung, die Lösung für Ihren Fall zu finden.
Hurrikan
2

Nach einigen Testversionen scheint dies meine kürzeste und einfachste Lösung zu sein.

Alles , was sein muss , im Wesentlichen hat getan Flexbox die <div>Dehnungselemente standardmäßig an ihre Grenzen, aber wenn <span>die Strecke von der angeklickt wird , Constraint <div>zu <span>Breite ...

Pseudocode:

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

Ich habe das CSS in einen Abschnitt "Relevanter Mechanismus" und "Nur Augenweide" unterteilt, um das Lesen (und das erneute Codieren von Code) zu erleichtern.

Der Code ist stark kommentiert, daher hier nicht viel Text ...

Quirk Irgendwie gibt es eine zusätzliche Verzögerung transitionbeim Umschalten divvon von max-width: 100%auf max-width = span width. Ich habe dieses Verhalten in Chrome, Edge, IE11 und Firefox (alle W10) überprüft und alle scheinen diese Eigenart zu haben. Entweder wird eine interne Neuberechnung des Browsers durchgeführt, oder die transitionZeit wird zweimal verwendet ("fühlt sich an wie"). Umgekehrt gibt es seltsamerweise keine zusätzliche Verzögerung.

Bei einer kurzen Übergangszeit (z. B. 150 ms, wie ich sie jetzt verwende) ist diese zusätzliche Verzögerung jedoch nicht / kaum spürbar. (Schön für eine andere SO-Frage ...)

AKTUALISIEREN

Neue Version, die alle anderen zurücksetzt <div>. Ich hasse die Nervosität wirklich, aber das liegt an der Dehnung der Flexbox und dem transitionWert. Ohne transitionsichtbare Sprünge. Sie müssen ausprobieren, was für Sie funktioniert.

Ich habe nur document.querySelectorAll()den Javascript-Code hinzugefügt .

Rene van der Lende
quelle
Vielen Dank für Ihren Vorschlag. Es passt nicht zu meinen Bedürfnissen (aber es ist ein sehr guter Ausgangspunkt), denn wenn wir auf ein flüssiges Div klicken, passt es in Ordnung, aber die beiden anderen Divs dehnen sich nicht gleichmäßig aus, um den verbleibenden Platz einzunehmen. Welches ist 50% des Problems. Und ja, da ist auch diese Eigenart. Das Skript von Azametzin erledigt den Job zu 100% ohne Eigenart. Am Ende mischte ich sowohl sein Skript als auch die Verwendung des Flex-Übergangs anstelle der maximalen Breite und es funktioniert wie ein Zauber. Vielen Dank für Ihre Hilfe !
Bachir Messaouri
Die Bemerkung von @Azametzin: Es waren viele lustige Tage in dieser Saga. :). Vielleicht das nächste Mal mit dem Bild beginnen?
Rene van der Lende
OK. Ich dachte, die Erklärung wäre genug, aber offensichtlich habe ich mich geirrt. Das Bild ist aber seit gestern da. Aber ich verstehe deinen Standpunkt. Ich werde meine ursprüngliche Nachricht bearbeiten, um mein benutzerdefiniertes Skript in wenigen Minuten anzuzeigen. Vielen Dank !
Bachir Messaouri
Ursprünglicher Beitrag mit der benutzerdefinierten Lösung aktualisiert!
Bachir Messaouri