Esnek geçiş: İçeriğe uyacak şekilde gerin (veya küçültün)


9

Seçilen bir div genişletmek ve diğer divs (kalan genişlik sabit olan ilk biri hariç) eşit olarak uzanan tarafından buna göre davranmasını sağlar bir komut dosyası (burada bir kullanıcı yardımıyla) kodladı.

İşte elde etmek istediğim şeyin bir resmi:

resim açıklamasını buraya girin

Bunun için esneklik ve geçişler kullanıyorum.

İyi çalışır, ancak jQuery komut dosyası "% 400" bir esneme değeri belirtir (bu test için mükemmeldir).

Şimdi seçilen div "% 400" sabit değeri yerine tam olarak sığacak şekilde genişletmek / daraltmak istiyorum.

Bunu nasıl yapabileceğime dair hiçbir fikrim yok.

Mümkün mü ?

Div'ı klonlamaya, içeriğe sığdırmaya, değerini elde etmeye ve sonra geçiş için bu değeri kullanmaya çalıştım, ancak bu yüzde olarak başlangıç ​​genişliğine ancak piksel cinsinden bir hedef değere sahip olduğum anlamına geliyor. Bu işe yaramıyor.

Ve piksel değerini yüzde olarak dönüştürürsem, sonuç ne olursa olsun içeriğe tam olarak uymaz.

Her durumda, bu zaten istediğimi elde etmek için biraz karmaşık bir yol gibi görünüyor.

Seçilen bir div'in içeriğine sığdırmak için geçiş yapılabilecek herhangi bir flex özelliği yok mu?

İşte kod ( daha iyi okumak için düzenlenmiş / basitleştirilmiş ):

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>

İşte jsfiddle:

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

EDIT: İşte hepinizin yardımıyla çalışma çözümü (pencere yeniden boyutlandırma + div sayısı ve ilk sütun genişliği dinamik olarak hesaplanan güncellenen boyutları):

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>

Ve işte tam teşekküllü bir örnek (stiller + dolgu + daha fazla veri):

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

Hepinize teşekkür ederim !


1
Lütfen, tüm bunlardan sonra ne oluşturduğunuzu gösteren sorunuza bir nihai sonuç eklemek için yeterince nazik olun. Gerçekten neye benzediğiyle ilgileniyorum. Teşekkürler! (... belki de ne için kullanıldığına dair bir fikir??)
Rene van der Lende

Tamam, betiğimi tüm gereksiz öğelerden temizlediğim anda yapacağım (birkaç dakika içinde sanırım). Bunu nasıl yaparım? Azametzin yanıtı gayet iyi olsa da, sadece orijinal gönderiyi düzenler ve bu konudaki ilgimi ekler miyim? Birkaç şeyi değiştirdim ama% 90 aynı (bunu göstermekten memnuniyet duyarım) ..
Bachir Messaouri

İşte nokta: Bir tür excel elektronik tablosunu taklit ediyorum ve insanların içeriğe uyması için belirli bir sütunu tıklamasını istiyorum. Normalde bir tablo yapardı, ancak sütunları istediğim gibi yeniden boyutlandırma söz konusu olduğunda doğru davranmıyor (bir seferde bir sütunu genişletmek kolay, ancak kalan alanı bu şekilde işgal etmek için diğerlerinin gerçek zamanlı davranmasını sağlamak zor Flex, divs için yapardı, sadece düzensiz genişliyorlar, biraz araştırma yaptım ve istediğim şey tablolarla imkansız). Bir tablo veya dataTable kullanmamanın başka nedenleri de var ama bu yazının kapsamı dışında.
Bachir Messaouri

1
Yeterince kolay, sadece 'düzenle'ye basın, sorunun sonuna gidin ve yeni bir snippet (hepsi) dahil olmak üzere bazı prozaik vurgular eklemeye başlayın. Snippet'i varsayılan olarak (sol alt onay kutusu), 'yeni okuyucular' için daha az müdahaleci / dikkat dağıtıcı sakladığınızdan emin olun. İlk düşüncem, bazı süslü 'mızıka' benzeri widget, simgeler ve hepsi için kullanacağınızdı. E-tablo ha, bazı tam ekran var, yukarı / aşağı / sola / sağa burada tartışılan aynı ilkeleri (etrafında ilgileniyorsanız) etrafında etrafında döşeme uzanan.
Rene van der Lende

2
Harika, son sonucu göndererek fazladan yol kat ettin. SO ile ilgili soruları yanıtlamak çok daha faydalı! Cudos ...
Rene van der Lende

Yanıtlar:


4

O kullanarak çözmek mümkündür max-widthve calc().

Birincisi, yerini width: 100%ile flex: 1onlar bu durumda daha iyi olduğunu, artacak böylece, CSS'deki div için. Ayrıca, geçişini kullanın max-width.

Şimdi, ilgili bazı değerleri depolamamız gerekiyor:

  • divsLengthBu durumda canlandırılacak div ( değişken) - 3 miktarı .
  • Sabit div ve kenarlıklar için kullanılan toplam genişlik (extraSpace değişken) - bu durumda 39 piksel.

Bu 2 değişkenle varsayılan max-width(defaultMaxWidth tüm div'lere değişken) daha sonra kullanabiliriz. Bu yüzden küresel olarak depolanıyorlar.

defaultMaxWidtholduğucalc((100% - extraSpace)/divsLength) .

Şimdi tıklama fonksiyonuna girelim:

Div değerini genişletmek için, hedef metnin genişliği adlı bir değişkene depolanır textWidthve div'a max-width.kullandığı şekilde uygulanır.getBoundingClientRect().width (kayan nokta değerini döndürdüğü için).

Geri kalan div'ler calc()için max-width, kendilerine uygulanacak bir for oluşturulur .
Öyle: calc(100% - textWidth - extraScape)/(divsLength - 1).
Hesaplanan sonuç, kalan her div'in olması gereken genişliktir.

Genişletilmiş div'a tıklandığında, yani normale dönmek için varsayılan değer max-widthtüm .divöğelere tekrar uygulanır .

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>

Bu yaklaşım dinamik davranır ve herhangi bir çözüm üzerinde çalışmalıdır.
Sabit kodlamaya ihtiyacınız olan tek değer extraSpacedeğişkendir.


1
Evet, bu hassas bir hesaplama ve flexbox büzülme efektinin daha iyi anlaşılmasını gerektiren bir sorundur. Henüz "mükemmel" bir çözümüm yok ama gelecekte tekrar denemek istiyorum, bu yüzden nasıl doğru yapılacağını anlamaya çalışmak için sorunuzu başka bir zamana işaretledim. Bu iyi bir mücadeledir.
Azametzin

1
Sadece FYI, okumayı ve test etmeyi kolaylaştırmak için kodu düzenledim. İyi bir ek olan açıklıkları ekledim. Maksimum genişlikteki geçişleri eklemedim, ancak şu ana kadar harika çalışmadılar.
Bachir Messaouri

1
Güzel. Sadece çözdüm. Cevabı düzenliyorum.
Azametzin

1
Güzel! Zaten birkaç saat dışarı
çıkarken acele etmeyin

1
Oooh, sadece düzenlenmiş yorumunuzu gördüm (güncelleme yorumu bekliyordum). GetBoundingClientRect () kullanımı çok zekidir ve bana gelmedi (39 piksel sabit genişliğini hesaba katmakla aynı). Herhangi bir pencere boyutu ve herhangi bir sayıda Sıvı div için çalışıp çalışmadığını görmek için biraz daha test edeceğim. Sana geri döneceğim. Çok teşekkür ederim! Not: çözümünüz her ne sebeple olursa olsun jQuery 3.3.1 ile çalışır, ancak 3.4.1 ile DEĞİLDİR.
Bachir Messaouri

3

Width veya calc fonksiyonlarıyla uğraşmanız gerekir. Flexbox'ın bir çözümü olurdu.

Tüm div'leri eşit yapmak için (ilk değil) kullanırız 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>

Normal div ve seçilen div için esnek kurallar tanımlayın. transition: flex 1s;senin arkadaşın. Seçilen biri için esnek büyümeye ihtiyacımız yok, bu yüzden kullanıyoruz 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;
}

Kullanıcı bir div'ı her tıkladığında seçilen sınıfı ekleyin. Seçilen öğeleri bir haritaya kaydetmek ve seçilen birden çok öğeyi (elbette bu kod örneğiyle değil) gösterebilmek için ikinci tıklama için geçiş özelliğini de kullanabilirsiniz.

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

https://jsfiddle.net/f3ao8xcj/


Teşekkür ederim. Durumunuzu incelemek zorundayım çünkü istediğimin tam tersini yapıyor ve bunu nasıl tersine çevireceğimi bilmiyorum (belki de önemsizdir). Varsayılan olarak, tüm sıvı div'lerinin eşit derecede büyük olmasını istiyorum. sadece birisine tıkladığımda bunun içeriğine uyması (ve diğer sıvı div'leri kalan alanı eşit olarak paylaşıyor). Aynı div'de ikinci kez tıklarsam, tüm Fluid divs sıfırlanır ve içerikleri hakkında hiçbir endişe duymadan aynı alanı tekrar paylaşır. Yani, yanılmıyorsam, yaptığın şeyin tam tersi. Henüz nasıl tersine çevrileceğinden emin değilim.
Bachir Messaouri

Ancak, bu tersine çevrilebilirse, css width özelliğiyle uğraşmaya gerek olmadığından çözümünüzün ne kadar zarif ve ne kadar basit olduğunu seviyorum. Flex bunu sadece dahili olarak ele alıyor gibi görünüyor. Bununla birlikte, tam tersine ulaşmak daha zor olacak bir önsezim var dedi. Bekle ve gör. Takip için teşekkürler.
Bachir Messaouri

1
@ BachirMessaouri yine oldukça basit. Lütfen güncellenmiş sürüme bakın.
kasırga

Senaryonuzun zarafetini seviyorum ama çözümleriniz benim isteğimin sadece bir kısmını ele alıyor. Benim isteğim sadece içeriğin yerleştirilmesi değil, aynı zamanda akışkan div'larını tıklamadan önce, sırasında ve sonrasında kalan alanı eşit olarak paylaşmakla ilgilidir. Senaryonuz buna hiç iyi değinmiyor, hiç değilse. Ve her şeyi bir beyin yazıcısı yapan da budur. İhtiyaçlarımı çok netleştirmek için orijinal gönderiye bir resim ekledim. Yukarıdaki diğer komut dosyası tam olarak teslim edilemediği yerde başlar. Her iki komut dosyanızın bir karışımı işe yarayabilir. Test ediyorum ve bittikten sonra sana geri döneceğim.
Bachir Messaouri

@BachirMessaouri Burada eksik olan, kimsenin davanız için mükemmel bir çözüm sunmasına gerek yok. Flex transition: Stretch (or shrink) to fit contentsoru başlığınızdır ve bu cevap bunu nasıl yapacağınıza basit bir örnektir. Davanız için çözüm bulmak sizin sorumluluğunuzdadır.
kasırga

2

Birkaç deneme sürümünden sonra, bu benim en kısa ve en zorlu çözümüm gibi görünüyor.

Esasen ihtiyaç duyduğu tüm is flexbox'a germek zorunda yapılması gereken <div>varsayılan olarak kendi sınırlarına elemanları, ama ne zaman <span>tıklandığında, streç sınırlamak <div>için <span>genişlik ...

sözde kod:

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'yi kolay okuma (ve kod geri dönüşümü) için bir 'ilgili mekanizma' ve 'sadece şekerleme' bölümüne ayırdım.

Kod çok yorumlanmış, bu yüzden burada çok fazla metin yok ...

Quirk Bir şekilde ' den' e transitiongeçiş yapılırken fazladan bir gecikme olur . Bu davranışı Chrome, Edge, IE11 ve Firefox'ta (tüm W10) kontrol ettim ve hepsi bu tuhaflığa sahip gibi görünüyor. Bazı tarayıcı dahili yeniden hesaplamaları devam ediyor veyadivmax-width: 100%max-width = span widthtransition zaman iki kez kullanılıyor ('gibi hissettiriyor'). Vice Versa, garip bir şekilde, ekstra bir gecikme yok.

Ancak, kısa bir geçiş süresi (örneğin şu anda kullandığım gibi 150 ms) ile bu ekstra gecikme fark edilemez / pek fark edilmez. (Başka bir SO sorusu için güzel bir ...)

GÜNCELLEME

Diğerlerini sıfırlayan yeni sürüm <div>. Zıplayanlıktan gerçekten nefret ediyorum, ama bu Flexbox germe ve transitiondeğerden kaynaklanıyor. olmadantransitionHiçbir atlama görünür. Sizin için neyin işe yaradığını denemeniz gerekir.

Sadece document.querySelectorAll()javascript koduna ekledim .


Teklifiniz için çok teşekkür ederim. Akışkan bir div'ı tıkladığımızda olduğu gibi, ihtiyaçlarıma uymuyor (ama çok iyi bir başlangıç ​​noktası), iyi uyuyor, ancak diğer iki div, kalan alanı işgal etmek için eşit olarak genişlemiyor. Bu da sorunun% 50'sidir. Ve evet, bu tuhaflık da var. Azametzin'in senaryosu işi% 100 hiç tuhaflık olmadan yapıyor. Hem senaryosunu hem de maksimum genişlik yerine esnek geçiş kullanımını karıştırdım ve bir cazibe gibi çalışıyor. Yardımın için çok teşekkürler !
Bachir Messaouri

@Azametzin 'in sözünü söylemek: Bu destanda çok eğlenceli günlerdi. :). Belki bir dahaki sefere görüntü ile başlar?
Rene van der Lende

Tamam. Açıklamanın yeterli olduğunu düşündüm ama belli ki yanılmışım. Görüntü dünden beri orada. Ama anladım. Özel komutümü birkaç dakika içinde göstermek için orijinal iletimi düzenleyeceğim. Çok teşekkür ederim !
Bachir Messaouri

Orijinal gönderi özel çözüm ile güncellendi!
Bachir Messaouri
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.