フレックストランジション:コンテンツに合わせてストレッチ(またはシュリンク)


9

選択したdivを展開し、残りのスペース(幅が固定されている最初のものを除く)に合わせて均等にストレッチすることで他のdivを適切に動作させるスクリプトを(ここでユーザーの助けを借りて)コード化しました。

そして、これが私が達成したいことの写真です:

ここに画像の説明を入力してください

そのために、フレックスとトランジションを使用します。

それはうまく機能しますが、jQueryスクリプトは「400%」のストレッチ値を指定します(これはテストに最適です)。

ここで、選択したdivを「400%」の固定値ではなく、コンテンツに正確に合わせて拡大/縮小したいと思います。

どうすればいいのかわかりません。

出来ますか ?

divを複製してコンテンツに適合させ、その値を取得し、この値を使用してトランジションを行おうとしましたが、これはパーセンテージでの初期幅ですが、ピクセルでのターゲット値であることを意味します。それはうまくいきません。

ピクセル値をパーセンテージに変換すると、何らかの理由で結果がコンテンツに正確に適合しません。

すべての場合において、これはとにかく私が欲しいものを達成するための少し複雑な方法のようです。

選択したdivのコンテンツに合わせるために移行できるflexプロパティはありませんか?

ここにコードがあります(より読みやすくするために編集/簡略化されています):

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>

ここにjsfiddleがあります:

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

編集:あなたの助けを借りて私の作業ソリューションがあります(ウィンドウのサイズ変更で更新されたサイズ+ divの数と動的に計算された最初の列の幅):

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>

そして、これは完全に機能している例です(スタイル+パディング+その他のデータ):

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

皆さん、ありがとうございました !


1
結局何を作成したかを示す最終結果を質問に追加してください。私はそれがどのように見えるかに本当に興味があります。ありがとう!(...たぶんそれが何に使われているのかについての洞察?...)
Rene van der Lende

わかりました。不要な要素をすべてスクリプトから削除したらすぐに実行します(たぶん数分で)。つまり、どうすればよいですか?Azametzinの回答が完全に問題なくても、元の投稿を編集して私の見解を追加するだけですか?いくつか変更しますが、90%は同じです(それを見せて
もらえれ

ここがポイントです。私は一種のExcelスプレッドシートをエミュレートし、特定の列をクリックしてコンテンツに合わせることができるようにしたいと考えています。通常、テーブルは機能しますが、列のサイズが希望どおりに変更されると列が正しく動作しません(一度に1列を拡張するのは簡単ですが、残りのスペースを占有するために他の列をリアルタイムで動作させることは困難です)フレックスはdivに対して行います。それらは不規則に拡張するだけです。私はいくつかの調査を行いましたが、私が望むものはテーブルでは不可能です。テーブルやdataTableを使用しない理由は他にもありますが、それはこの投稿の範囲外です。
Bachir Messaouri

1
非常に簡単です。「編集」を押し、質問の最後に移動して、新しいスニペット(すべて)を含む、おかしな考えを追加してください。デフォルト(左下のチェックボックス)で「スニペットを非表示」にし、「新しい読者」の邪魔にならないようにしてください。私の最初の考えは、あなたがそれをいくつかの派手な「ハーモニカ」のようなウィジェット、アイコンなどすべてに使用するつもりであるということでした。スプレッドシートええと、私はいくつかのフルスクリーンを使用しています。ここで説明した同じ原則を使用して、上下左右に配置します(興味がある場合)。
Rene van der Lende

2
最終結果を投稿するためにさらに1マイル進みました。非常にやりがいのある質問に答えることができます!Cudos ...
Rene van der

回答:


4

max-widthおよびを使用して解決することができcalc()ます。

まず、CSSのdivをに置き換えるwidth: 100%flex: 1、divが大きくなるため、この場合はより適しています。さらに、の遷移を使用しmax-widthます。

ここで、いくつかの関連する値を保存する必要があります。

  • アニメーション化されるdivの量(divsLength変数)-この場合は3。
  • 固定divとボーダーに使用される合計幅(extraSpace変数)に -この場合は39px。

これら2つの変数を使用して、デフォルトmax-widthdefaultMaxWidth変数)をすべてのdivに後で使用することもできます。それがグローバルに保存されている理由です。

defaultMaxWidthありますcalc((100% - extraSpace)/divsLength)

次に、クリック関数を入力します。

divを展開するために、ターゲットテキストの幅が呼び出される変数に格納され、それが使用textWidthするようにdivに適用さmax-width.れます.getBoundingClientRect().width(浮動小数点値を返すため)。

残りのdivには、それらに適用されるcalc()for が作成max-widthされます。
それは:calc(100% - textWidth - extraScape)/(divsLength - 1)です。
計算結果は、残りの各divの幅です。

展開されたdivをクリックすると、つまり通常に戻ると、デフォルトmax-widthがすべての.div要素に再度適用されます。

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>

このアプローチは動的に動作し、どの解像度でも機能します。
ハードコードする必要がある唯一の値はextraSpace変数です。


1
はい、これは正確な計算とフレックスボックスの縮小効果のより深い理解を必要とする問題です。「完全な」解決策はまだありませんが、将来もう一度やり直したいので、正しい方法を理解するためにもう一度質問にブックマークを付けました。それは良い挑戦です。
アザメジン

1
参考までに、コードを編集して、読みやすく、テストしやすくしました。良い追加であるスパンを追加しました。最大幅のトランジションを追加していませんが、これまでのところ、うまく機能しませんでした。
Bachir Messaouri

1
涼しい。解決しました。回答を編集しています。
アザメジン

1
いいね!とにかく私が数時間外に出るので、時間をかけてください;-)
バチル・メサウリ

1
おお、私はあなたの編集されたコメントを見ました(更新コメントを期待していました)。getBoundingClientRect()の使用は非常に賢く、私には発生しませんでした(39ピクセルの固定幅を考慮に入れても同じです)。任意のウィンドウサイズと任意の数のFluid divで機能するかどうかを確認するために、もう少しテストします。折り返しご連絡いたします。どうもありがとうございました!PS:ソリューションはjQuery 3.3.1で動作しますが、何らかの理由で3.4.1では動作しません。
Bachir Messaouri

3

幅または計算関数を処理する必要があります。Flexboxには解決策があります。

すべてのdivを(最初のdivではなく)等しくするには、を使用します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>

通常のdivと選択したdivのフレックスルールを定義します。transition: flex 1s;あなたの友だちです。選択したものについては、フレックスグローを必要としないため、以下を使用し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;
}

ユーザーがdivをクリックするたびに、選択したクラスを追加します。2回目のクリックでトグルを使用して、選択したアイテムをマップに保存し、選択した複数のアイテムを表示することもできます(もちろん、このコード例では使用できません)。

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

https://jsfiddle.net/f3ao8xcj/


ありがとうございました。私はあなたのケースを研究しなければなりません。なぜなら、それは私が望むものの反対であり、まだそれを逆にする方法がわからないからです(多分それは些細なことかもしれません)。デフォルトでは、すべての流動divを同じ大きさにしたいと思います。これをクリックすると、これがコンテンツに適合します(他の流体のdivが残りのスペースを等しく共有します)。そして、同じdivをもう一度クリックすると、すべてのFluid divがリセットされ、コンテンツをまったく気にすることなく、再びスペースを均等に共有します。だから、私が間違っていないのであれば、それはあなたがしたことの正反対です。それを逆にする方法はまだわかりません。
Bachir Messaouri

ただし、これを逆にできる場合は、cssのwidth属性をいじる必要がないため、ソリューションがいかにエレガントでシンプルかが本当に気に入っています。Flexはこれを内部的に処理するようです。とはいえ、逆の方法を実現するのはもっと難しいだろうと私は思います。成り行きを見守る。フォローアップありがとうございます。
Bachir Messaouri

1
@BachirMessaouriそれは再びかなり簡単です。更新されたバージョンをご覧ください。
ハリケーン

私はあなたのスクリプトの優雅さが大好きですが、あなたの解決策は私の要求の一部しか考慮していません。私のリクエストは、コンテンツを合わせるだけでなく、fluid divをクリックする前、間、後に均等に残りのスペースを共有することについてもです。あなたのスクリプトは、まったくそうではないにしても、それをうまく処理しません。そして、それが全体を脳のバーナーにするものです。元の投稿に写真を追加して、自分のニーズを明確にしました。上記の他のスクリプトは、あなたのスクリプトが配信に失敗したところから始まります。奇妙なことに、両方のスクリプトの組み合わせが機能する可能性があります。テスト中です。完了したら、折り返しご連絡いたします。
Bachir Messaouri

@BachirMessaouriあなたがここで欠けているのは、あなたのケースに完璧なソリューションを提供する必要がある人がいないことです。Flex transition: Stretch (or shrink) to fit contentはあなたの質問のタイトルであり、この答えはそれを行う方法の簡単な例です。あなたのケースの解決策を見つけるのはあなたの責任です。
ハリケーン

2

いくつかの試用版の後、これは私の最短かつ最も簡単な解決策のようです。

基本的に行う必要があるのは、Flexboxで<div>デフォルトで要素を限界まで伸ばすことですが、<span>クリックすると、幅<div>への伸張を制限します<span>...

疑似コード:

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

CSSを「関連するメカニズム」と「目の保養のみ」のセクションに分割して、読みやすく(そしてコードの再利用も)できるようにしました。

コードはコメントが多いため、ここにはあまりテキストがありません...

癖はどうにかにおける余分な遅延があるtransitionの切り替え時divからmax-width: 100%max-width = span width。私はChrome、Edge、IE11、Firefox(すべてW10)でこの動作を確認しましたが、すべてこの癖があるようです。ブラウザーの内部再計算が行われている、またはtransition時間が2回使用されている(「感じている」)。逆に、奇妙なことに、余分な遅延はありません。

ただし、トランジション時間が短い場合(たとえば、現在使用している150ミリ秒)、この余分な遅延は気づかれず、ほとんど気づかれません。(別のSOの質問にいい...)

更新

他のすべてをリセットする新しいバージョン<div>。飛び跳ねは本当に嫌いですが、それはフレックスボックスのストレッチとそのtransition価値によるものです。transition目に見えるジャンプなし。あなたはあなたのために何がうまくいくかを試す必要があります。

document.querySelectorAll()JavaScriptコードに追加しただけです。


どうもありがとうございました。流動的なdivをクリックするとうまくいきますが、他の2つのdivが均等に拡張されずに残りのスペースを占めるため、これは私のニーズに適合しません(ただし、それは非常に良い出発点です)。これは問題の50%です。そして、はい、この奇妙さもあります。Azametzinのスクリプトは奇抜さなしに100%仕事をします。私は彼のスクリプトとmax-widthの代わりにflexトランジションの使用の両方を混合してしまい、それは魅力のように機能しました。手伝ってくれてありがとうございます !
Bachir Messaouri

@Azametzin sの発言を支持する:このサガでは、多くの楽しい日でした。:)。次回は画像から始めますか?
Rene van der Lende

OK。説明は十分だと思いましたが、間違いでした。でも昨日からの画像です。しかし、私はあなたの要点を理解します。元のメッセージを編集して、カスタムスクリプトを数分で表示します。どうもありがとうございました !
Bachir Messaouri

カスタムソリューションで更新された元の投稿!
Bachir Messaouri
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.