Transisi fleksibel: Regangkan (atau susutkan) agar sesuai dengan konten

9

Saya telah membuat kode skrip (dengan bantuan pengguna di sini) yang memungkinkan saya untuk memperluas div yang dipilih dan membuat div lain berperilaku sesuai dengan peregangan yang sama agar sesuai dengan ruang yang tersisa (kecuali yang pertama yang lebarnya diperbaiki).

Dan inilah gambar yang ingin saya capai:

masukkan deskripsi gambar di sini

Untuk itu saya menggunakan flex dan transisi.

Ini bekerja dengan baik, tetapi skrip jQuery menentukan nilai peregangan "400%" (yang bagus untuk pengujian).

Sekarang saya ingin div yang dipilih untuk memperluas / menyusut agar sesuai dengan konten, bukan nilai tetap "400%".

Saya tidak tahu bagaimana saya bisa melakukan itu.

Apa itu mungkin ?

Saya mencoba mengkloning div, menyesuaikannya dengan konten, mendapatkan nilainya dan kemudian menggunakan nilai ini untuk transisi NAMUN ini berarti saya memiliki lebar awal dalam persentase tetapi nilai target dalam piksel. Itu tidak berhasil.

Dan jika saya mengonversi nilai piksel dalam persentase, maka hasilnya tidak persis sesuai dengan konten untuk alasan apa pun.

Dalam semua kasus, ini tampaknya sedikit cara yang rumit untuk mencapai apa yang saya inginkan.

Apakah tidak ada properti fleksibel yang dapat ditransisikan agar sesuai dengan konten div yang dipilih?

Berikut adalah kode ( diedit / disederhanakan karena untuk dibaca lebih baik ):

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>

Ini jsfiddle:

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

EDIT: Ini adalah solusi kerja saya dengan bantuan Anda semua (ukuran diperbarui pada ukuran jendela + jumlah divs dan lebar kolom pertama dihitung secara dinamis):

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>

Dan di sini adalah contoh yang lengkap di tempat kerja (gaya + padding + lebih banyak data):

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

Terima kasih semua !

Bachir Messaouri
sumber
1
Mohon berbaik hati untuk menambahkan hasil akhir pada pertanyaan Anda yang menunjukkan apa yang Anda buat setelah semua ini. Saya benar-benar tertarik dengan tampilannya. Terima kasih! (... mungkin beberapa wawasan tentang apa yang digunakan untuk? ...)
Rene van der Lende
Ok, saya akan melakukannya segera setelah saya membersihkan skrip saya dari semua elemen yang tidak perlu (dalam beberapa menit saya kira). Yang mengatakan, bagaimana saya melakukannya? Apakah saya hanya mengedit posting asli dan menambahkan pendapat saya meskipun jawaban Azametzin baik-baik saja? Saya mengubah beberapa hal tetapi 90% sama (saya akan senang untuk menunjukkannya) ..
Bachir Messaouri
Inilah intinya: Saya meniru semacam spreadsheet excel dan saya ingin orang-orang dapat mengklik kolom tertentu agar sesuai dengan konten. Biasanya sebuah tabel akan melakukan tetapi kolom tidak berperilaku benar ketika datang untuk mengubah ukurannya seperti yang saya inginkan (mudah untuk memperluas satu kolom pada suatu waktu tetapi sulit untuk membuat yang lain berperilaku secara real time untuk menempati ruang yang tersisa dengan cara flex akan lakukan untuk divs. Mereka hanya berkembang tidak menentu. Saya membuat beberapa penelitian dan apa yang saya inginkan tidak mungkin dengan tabel). Ada alasan lain saya tidak menggunakan tabel atau dataTable tapi itu di luar lingkup tulisan ini.
Bachir Messaouri
1
Cukup mudah, cukup tekan 'edit', pergi ke akhir pertanyaan dan mulai menambahkan beberapa perenungan proza, termasuk potongan baru (semuanya). Pastikan untuk 'menyembunyikan cuplikan' secara default (kotak centang kiri bawah), kurang mengganggu / mengganggu bagi 'pembaca baru'. Pikiran pertama saya adalah bahwa Anda akan menggunakannya untuk widget, ikon harmonika-seperti mewah, ikon dan semua. Spreadsheet ya, saya punya beberapa layar penuh, membentang di seluruh tempat atas / bawah / kiri / kanan berbaring menggunakan prinsip-prinsip yang sama yang dibahas di sini (jika Anda tertarik).
Rene van der Lende
2
Hebat Anda berusaha keras memposting hasil akhir. Menjadikan menjawab pertanyaan di SO jauh lebih bermanfaat! Cudos ...
Rene van der Lende

Jawaban:

4

Dimungkinkan untuk menyelesaikannya menggunakan max-widthdan calc().

Pertama, ganti width: 100%dengan flex: 1untuk divs di CSS, sehingga mereka akan tumbuh, yang lebih baik dalam hal ini. Selain itu, gunakan transisi untuk max-width.

Sekarang, kita harus menyimpan beberapa nilai yang relevan:

  • Jumlah div yang akan dianimasikan ( divsLengthvariabel) - 3 dalam hal ini.
  • Lebar total yang digunakan untuk div tetap dan batas ( extraSpacevariabel) - 39px dalam kasus ini.

Dengan 2 variabel tersebut, kita dapat menetapkan default max-width( defaultMaxWidthvariabel) untuk semua divs, serta menggunakannya nanti. Itulah sebabnya mereka disimpan secara global.

The defaultMaxWidthadalah calc((100% - extraSpace)/divsLength).

Sekarang, mari masuk ke fungsi klik:

Untuk memperluas div, lebar teks target akan disimpan dalam variabel yang dipanggil textWidthdan itu akan diterapkan ke div saat max-width.digunakan .getBoundingClientRect().width(karena mengembalikan nilai floating-point).

Untuk div yang tersisa, dibuat calc()untuk max-widthyang akan diterapkan pada mereka.
Ini adalah: calc(100% - textWidth - extraScape)/(divsLength - 1).
Hasil yang dihitung adalah lebar yang seharusnya setiap div tersisa.

Ketika mengklik div diperluas, yaitu, untuk kembali ke normal, standarnya max-widthditerapkan lagi ke semua .divelemen.

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>

Pendekatan ini berperilaku dinamis dan harus bekerja pada resolusi apa pun.
Satu-satunya nilai yang Anda butuhkan untuk hard code adalah extraSpacevariabel.

Azametzin
sumber
1
Ya, ini adalah masalah yang membutuhkan perhitungan yang tepat dan pemahaman yang lebih besar tentang efek menyusut flexbox. Saya belum memiliki solusi "sempurna" tetapi saya ingin mencoba lagi di masa depan, jadi saya menandai pertanyaan Anda untuk waktu lain untuk mencoba mencari tahu bagaimana melakukannya dengan benar. Ini tantangan yang bagus.
Azametzin
1
Hanya FYI, saya mengedit kode sejak membuatnya lebih mudah untuk dibaca dan diuji. Saya menambahkan bentang yang merupakan tambahan yang bagus. Saya belum menambahkan transisi max-width namun sejauh ini, mereka tidak bekerja dengan baik.
Bachir Messaouri
1
Keren. Saya baru saja menyelesaikannya. Saya sedang mengedit jawabannya.
Azametzin
1
Bagus! Luangkan waktu Anda karena saya keluar selama beberapa jam ;-)
Bachir Messaouri
1
Oooh, saya baru saja melihat komentar Anda yang sudah diedit (mengharapkan komentar pembaruan). Penggunaan getBoundingClientRect () sangat pintar dan tidak terpikir oleh saya (sama untuk memperhitungkan lebar tetap 39 px). Saya akan menguji sedikit lebih banyak untuk melihat apakah itu bekerja untuk ukuran windows dan sejumlah div Fluid. Aku akan kembali padamu. Terima kasih banyak! PS: solusi Anda bekerja dengan jQuery 3.3.1 tetapi TIDAK dengan 3.4.1, untuk alasan apa pun.
Bachir Messaouri
3

Anda harus berurusan dengan fungsi lebar atau kalk. Flexbox tentu punya solusi.

Untuk membuat semua div sama (bukan yang pertama) kami gunakan 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>

Tetapkan aturan fleksibel untuk div normal dan div terpilih. transition: flex 1s;adalah temanmu Untuk yang terpilih, kita tidak perlu pertumbuhan fleksibel, jadi kita gunakan 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;
}

Tambahkan kelas yang dipilih setiap kali ketika pengguna mengklik sebuah div. Anda juga dapat menggunakan sakelar untuk klik kedua sehingga Anda dapat menyimpan item yang dipilih di peta dan Anda dapat menampilkan beberapa item yang dipilih (tidak dengan contoh kode ini tentu saja).

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

https://jsfiddle.net/f3ao8xcj/

badai
sumber
Terima kasih. Saya harus mempelajari kasus Anda karena hal itu berlawanan dengan apa yang saya inginkan dan saya belum yakin bagaimana cara membalikkannya (mungkin itu sepele). Saya ingin, secara default, semua divs fluida sama besar. itu hanya ketika saya mengklik satu yang cocok ini isinya (dan div cairan lain berbagi ruang yang sama). Dan jika saya mengklik kedua kalinya pada div yang sama, semua div Fluid ulang, berbagi ruang yang sama tanpa peduli tentang konten mereka sama sekali. Jadi, jika saya tidak salah, itu kebalikan dari apa yang Anda lakukan. Belum yakin bagaimana cara membalikkannya.
Bachir Messaouri
Namun, jika ini dapat dibalik, saya sangat suka betapa elegan dan sederhananya solusi Anda karena tidak perlu mengacaukan atribut lebar css. Flex tampaknya hanya menangani ini secara internal. Yang mengatakan, saya punya firasat akan lebih sulit untuk mencapai sebaliknya. Tunggu dan lihat. Terima kasih atas tindak lanjutnya.
Bachir Messaouri
1
@BachirMessaouri cukup sederhana lagi. Silakan lihat versi terbaru.
topan
Saya suka keanggunan naskah Anda, tetapi solusi Anda hanya mempertimbangkan bagian dari permintaan saya. Permintaan saya tidak hanya tentang mencocokkan konten, tetapi juga tentang berbagi ruang yang tersisa secara merata sebelum, selama dan setelah mengklik div cairan. Skrip Anda tidak membahas dengan baik, jika tidak sama sekali. Dan itulah yang membuat semuanya menjadi pembakar otak. Saya menambahkan gambar di posting asli untuk membuat kebutuhan saya sangat jelas. Skrip lain di atas dimulai tepat di mana skrip Anda gagal terkirim. Tampaknya aneh bahwa campuran kedua skrip Anda mungkin berfungsi. Saya menguji dan setelah selesai saya akan kembali kepada Anda.
Bachir Messaouri
@BachirMessaouri Apa yang Anda lewatkan di sini adalah, tidak ada yang perlu memberikan solusi yang sempurna untuk kasus Anda. Flex transition: Stretch (or shrink) to fit contentadalah judul pertanyaan Anda dan jawaban ini adalah contoh sederhana bagaimana melakukannya. Adalah tanggung jawab Anda untuk menemukan solusi untuk kasus Anda.
topan
2

Setelah beberapa versi uji coba, ini tampaknya menjadi solusi terpendek dan paling lurus bagi saya.

Semua yang pada dasarnya perlu dilakukan yaitu memiliki flexbox meregangkan <div>elemen untuk batas mereka secara default, tapi ketika <span>diklik, kendala peregangan dari <div>untuk <span>lebar ...

kode semu:

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

Saya telah membagi CSS menjadi bagian 'mekanisme yang relevan' dan 'eye-candy only' untuk memudahkan membaca (dan kode recyling).

Kode ini banyak dikomentari, jadi tidak banyak teks di sini ...

Quirk Entah bagaimana ada penundaan tambahan transitionsaat beralih divdari max-width: 100%ke max-width = span width. Saya telah memeriksa perilaku ini di Chrome, Edge, IE11 dan Firefox (semua W10) dan semua tampaknya memiliki kekhasan ini. Entah beberapa recalc internal browser berlangsung, atau mungkin transitionwaktu digunakan dua kali ('terasa seperti'). Begitu juga sebaliknya, cukup aneh, tidak ada penundaan tambahan.

Namun, dengan waktu transisi yang singkat (mis. 150 ms, seperti yang saya gunakan sekarang) keterlambatan tambahan ini tidak / hampir tidak terlihat. (Bagus untuk pertanyaan SO lainnya ...)

MEMPERBARUI

Versi baru yang mengatur ulang semua lainnya <div>. Saya benar-benar benci kegelisahan, tapi itu karena peregangan dan transitionnilai Flexbox . Tanpa transitionada lompatan yang terlihat. Anda perlu mencoba apa yang cocok untuk Anda.

Saya hanya menambahkan document.querySelectorAll()kode javascript.

Rene van der Lende
sumber
Terima kasih banyak atas proposal Anda. Itu tidak sesuai dengan kebutuhan saya (tapi itu adalah titik awal yang sangat baik) seperti ketika kita mengklik div cairan, itu cocok baik-baik saja tetapi dua div lainnya tidak berkembang secara merata untuk menempati ruang yang tersisa. Yang merupakan 50% dari masalah. Dan ya, ada quirkiness ini juga. Script Azametzin melakukan pekerjaan 100% tanpa quirkiness .. Saya akhirnya mencampur skripnya dan penggunaan transisi fleksibel bukan max-width dan bekerja seperti pesona. Terima kasih banyak atas bantuan Anda !
Bachir Messaouri
Komentar Seconding @Azametzin: Sangat menyenangkan dalam kisah ini. :) Mungkin lain kali mulai dengan gambar?
Rene van der Lende
Baik. Saya pikir penjelasannya sudah cukup tetapi jelas, saya salah. Gambar sudah ada sejak kemarin. Tapi saya mengerti maksud Anda. Saya akan mengedit pesan asli saya untuk menunjukkan skrip khusus saya dalam hitungan menit. Terima kasih banyak !
Bachir Messaouri
Posting asli diperbarui dengan solusi khusus!
Bachir Messaouri