Chuyển đổi linh hoạt: Kéo dài (hoặc thu nhỏ) để phù hợp với nội dung


9

Tôi đã mã hóa một tập lệnh (với sự trợ giúp của người dùng ở đây) cho phép tôi mở rộng một div đã chọn và làm cho các div khác hoạt động tương ứng bằng cách kéo dài bằng nhau để phù hợp với không gian còn lại (ngoại trừ cái đầu tiên có chiều rộng được cố định).

Và đây là bức tranh về những gì tôi muốn đạt được:

nhập mô tả hình ảnh ở đây

Cho rằng tôi sử dụng flex và chuyển tiếp.

Nó hoạt động tốt, nhưng tập lệnh jQuery chỉ định giá trị kéo dài "400%" (rất phù hợp để thử nghiệm).

Bây giờ tôi muốn div được chọn mở rộng / thu nhỏ để phù hợp chính xác với nội dung thay vì giá trị cố định "400%".

Tôi không biết làm thế nào tôi có thể làm điều đó.

Có thể không?

Tôi đã cố gắng sao chép div, khớp với nội dung, lấy giá trị của nó và sau đó sử dụng giá trị này để chuyển tiếp NHƯNG điều này có nghĩa là tôi có chiều rộng ban đầu theo tỷ lệ phần trăm nhưng giá trị đích tính bằng pixel. Điều đó không hiệu quả.

Và nếu tôi chuyển đổi giá trị pixel theo tỷ lệ phần trăm, thì kết quả không chính xác phù hợp với nội dung vì bất kỳ lý do gì.

Trong mọi trường hợp, điều này có vẻ hơi phức tạp để đạt được những gì tôi muốn.

Không có bất kỳ thuộc tính flex nào có thể được chuyển đổi để phù hợp với nội dung của một div đã chọn?

Đây là mã (được chỉnh sửa / đơn giản hóa để đọc tốt hơn ):

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>

Đây là jsfiddle:

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

EDIT: Đây là giải pháp làm việc của tôi với sự giúp đỡ của tất cả các bạn (kích thước được cập nhật trên thay đổi kích thước cửa sổ + số lượng div và chiều rộng của cột đầu tiên được tính toán động):

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>

Và đây là một ví dụ đầy đủ trong công việc (kiểu + đệm + thêm dữ liệu):

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

Cảm ơn tất cả !


1
Hãy tử tế để thêm một kết quả cuối cùng cho câu hỏi của bạn cho thấy những gì bạn đã tạo sau tất cả điều này. Tôi thực sự quan tâm đến những gì nó trông như thế nào. Cảm ơn! (... có lẽ một số cái nhìn sâu sắc về những gì nó được sử dụng cho? ...)
Rene van der Lende

Ok, tôi sẽ làm điều đó ngay khi tôi dọn sạch tập lệnh của mình khỏi tất cả các yếu tố không cần thiết (trong vài phút tôi đoán). Điều đó nói rằng, làm thế nào để tôi làm điều đó? Tôi chỉ cần chỉnh sửa bài viết gốc và thêm nhận của tôi về điều này mặc dù câu trả lời của Azametzin là hoàn toàn tốt? Tôi thay đổi một vài thứ nhưng nó giống nhau 90% (tôi sẽ rất vui khi thể hiện điều đó) ..
Bachir Messaouri

Đây là điểm: Tôi mô phỏng một loại bảng tính excel và tôi muốn mọi người có thể nhấp vào một cột cụ thể để phù hợp với nội dung. Thông thường một bảng sẽ làm nhưng các cột không hoạt động chính xác khi thay đổi kích thước chúng theo cách tôi muốn (dễ dàng mở rộng một cột tại một thời điểm nhưng thật khó để làm cho các cột khác hoạt động theo thời gian thực để chiếm không gian còn lại theo cách flex sẽ làm cho div. Họ chỉ mở rộng thất thường. Tôi đã thực hiện một số nghiên cứu và những gì tôi muốn là không thể với các bảng). Có nhiều lý do khác khiến tôi không sử dụng bảng hoặc dataTable nhưng nó nằm ngoài phạm vi của bài đăng này.
Bachir Messaouri

1
Đủ dễ dàng, chỉ cần nhấn 'chỉnh sửa', đi đến cuối câu hỏi và bắt đầu thêm một số suy ngẫm prozaic, bao gồm một đoạn trích mới (tất cả câu hỏi). Đảm bảo 'ẩn đoạn trích' theo mặc định (hộp kiểm dưới cùng bên trái), ít xâm phạm / gây mất tập trung cho 'độc giả mới'. Suy nghĩ đầu tiên của tôi là bạn sẽ sử dụng nó cho một số vật dụng, biểu tượng và âm thanh giống như 'hòa tấu'. Bảng tính huh, tôi có một số màn hình đầy đủ, trải dài khắp nơi lên / xuống / trái / phải đặt xung quanh bằng cách sử dụng các nguyên tắc tương tự được thảo luận ở đây (nếu bạn quan tâm).
Rene van der Lende

2
Tuyệt vời bạn đã đi thêm dặm đăng kết quả cuối cùng. Làm cho việc trả lời các câu hỏi về SO bổ ích hơn nhiều! Cudos ...
Rene van der Lende

Câu trả lời:


4

Có thể giải quyết nó bằng cách sử dụng max-widthcalc().

Trước hết, thay thế width: 100%bằng flex: 1cho các div trong CSS, vì vậy họ sẽ phát triển, mà là tốt hơn trong trường hợp này. Ngoài ra, sử dụng chuyển đổi cho max-width.

Bây giờ, chúng ta phải lưu trữ một số giá trị liên quan:

  • Số lượng div sẽ được tạo hình động ( divsLengthbiến) - 3 trong trường hợp này.
  • Tổng chiều rộng được sử dụng cho div cố định và đường viền ( extraSpacebiến) - 39px trong trường hợp này.

Với 2 biến đó, chúng ta có thể đặt mặc định max-width( defaultMaxWidthbiến) cho tất cả các div, cũng như sử dụng chúng sau này. Đó là lý do tại sao chúng đang được lưu trữ trên toàn cầu.

Các defaultMaxWidthcalc((100% - extraSpace)/divsLength).

Bây giờ, hãy nhập chức năng nhấp:

Để mở rộng div, chiều rộng của văn bản đích sẽ được lưu trữ trong một biến được gọi textWidthvà nó sẽ được áp dụng cho div khi max-width.nó sử dụng .getBoundingClientRect().width(vì nó trả về giá trị dấu phẩy động).

Đối với các div còn lại, nó được tạo ra calc()cho max-widthnó sẽ được áp dụng cho chúng.
Đó là : calc(100% - textWidth - extraScape)/(divsLength - 1).
Kết quả tính toán là chiều rộng mà mỗi div còn lại phải có.

Khi nhấp vào div mở rộng, nghĩa là, để trở lại bình thường, mặc định max-widthđược áp dụng lại cho tất cả các .divyếu tố.

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>

Cách tiếp cận này hành xử linh hoạt và nên làm việc trên bất kỳ độ phân giải.
Giá trị duy nhất bạn cần để mã cứng là extraSpacebiến.


1
Có, đây là một vấn đề đòi hỏi phải tính toán chính xác và hiểu rõ hơn về hiệu ứng thu nhỏ flexbox. Tôi chưa có giải pháp "hoàn hảo" nhưng tôi muốn thử lại trong tương lai, vì vậy tôi đã đánh dấu câu hỏi của bạn vào một lần khác để cố gắng tìm ra cách làm đúng. Đó là một thử thách tốt.
Azametzin

1
Chỉ cần FYI, tôi đã chỉnh sửa mã từ đó để đơn giản hơn để đọc và kiểm tra. Tôi đã thêm các nhịp là một bổ sung tốt. Tôi đã không thêm các chuyển đổi độ rộng tối đa, tuy nhiên cho đến nay, chúng không hoạt động tốt.
Bachir Messaouri

1
Mát mẻ. Tôi chỉ giải quyết nó. Tôi đang chỉnh sửa câu trả lời.
Azametzin

1
Đẹp! Hãy dành thời gian của bạn vì tôi sẽ ra ngoài trong vài giờ ;-)
Bachir Messaouri

1
Oooh, tôi vừa thấy bình luận đã được chỉnh sửa của bạn (đang mong đợi một bình luận cập nhật). Việc sử dụng getBoundingClientRect () rất thông minh và nó đã không xảy ra với tôi (tương tự với việc sử dụng chiều rộng cố định 39 px). Tôi sẽ kiểm tra thêm một chút để xem nó có hoạt động với bất kỳ kích thước cửa sổ nào không và số lượng div Fluid. Tôi sẽ lấy lại cho bạn. Cảm ơn rât nhiều! PS: giải pháp của bạn hoạt động với jQuery 3.3.1 nhưng KHÔNG với 3.4.1, vì bất kỳ lý do gì.
Bachir Messaouri

3

Bạn cần phải đối phó với các hàm chiều rộng hoặc calc. Flexbox sẽ có một giải pháp.

Để làm cho tất cả các div bằng nhau (không phải đầu tiên) chúng tôi sử dụng 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>

Xác định quy tắc flex cho div bình thường của bạn và div đã chọn. transition: flex 1s;là bạn của bạn. Đối với một lựa chọn, chúng tôi không cần tăng trưởng flex nên chúng tôi sử dụng 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;
}

Thêm lớp được chọn mỗi lần khi người dùng nhấp vào div. Bạn cũng có thể sử dụng chuyển đổi cho lần nhấp thứ hai để bạn có thể lưu các mục đã chọn trong bản đồ và bạn có thể hiển thị nhiều mục đã chọn (tất nhiên không phải với ví dụ mã này).

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

https://jsfiddle.net/f3ao8xcj/


Cảm ơn bạn. Tôi phải nghiên cứu trường hợp của bạn bởi vì nó trái ngược với những gì tôi muốn và tôi không chắc làm thế nào để đảo ngược điều đó (có thể đó là chuyện nhỏ). Tôi muốn, theo mặc định, tất cả các div chất lỏng đều lớn như nhau. Chỉ khi tôi nhấp vào một cái thì cái này mới phù hợp với nội dung của nó (và các div chất lỏng khác chia sẻ không gian còn lại bằng nhau). Và nếu tôi nhấp vào lần thứ hai trên cùng một div, tất cả các div Fluid sẽ đặt lại, chia sẻ không gian một lần nữa mà không cần quan tâm đến nội dung của chúng. Vì vậy, nếu tôi không nhầm, nó hoàn toàn ngược lại với những gì bạn đã làm. Không chắc chắn làm thế nào để đảo ngược điều đó.
Bachir Messaouri

Tuy nhiên, nếu điều này có thể đảo ngược, tôi thực sự thích sự thanh lịch và đơn giản của giải pháp của bạn như thế nào vì không cần phải lộn xộn với thuộc tính chiều rộng css. Flex dường như chỉ xử lý nội bộ này. Điều đó nói rằng, tôi có linh cảm sẽ khó khăn hơn để đạt được điều ngược lại. Chờ và xem. Cảm ơn đã theo lên.
Bachir Messaouri

1
@BachirMessaouri nó lại khá đơn giản. Xin vui lòng xem phiên bản cập nhật.
cơn bão

Tôi yêu sự thanh lịch của kịch bản của bạn nhưng các giải pháp của bạn chỉ xem xét các phần trong yêu cầu của tôi. Yêu cầu của tôi không chỉ là về việc phù hợp với nội dung, mà còn về việc chia sẻ không gian còn lại như nhau trước, trong và sau khi nhấp vào divs chất lỏng. Kịch bản của bạn không giải quyết điều đó rất tốt, nếu không phải tất cả. Và đó là những gì làm cho toàn bộ một bộ não. Tôi đã thêm một hình ảnh trong bài viết gốc để làm cho nhu cầu của tôi rất rõ ràng. Các kịch bản khác ở trên bắt đầu chính xác nơi bạn không cung cấp. Có vẻ kỳ lạ là sự kết hợp của cả hai tập lệnh của bạn có thể hoạt động. Tôi đang thử nghiệm và sau khi hoàn thành, tôi sẽ quay lại với bạn.
Bachir Messaouri

@BachirMessaouri Điều bạn đang thiếu ở đây là, không ai cần cung cấp một giải pháp hoàn hảo cho trường hợp của bạn. Flex transition: Stretch (or shrink) to fit contentlà tiêu đề câu hỏi của bạn và câu trả lời này là ví dụ đơn giản về cách thực hiện. Bạn có trách nhiệm tìm giải pháp cho trường hợp của mình.
cơn bão

2

Sau một vài phiên bản dùng thử, đây dường như là giải pháp ngắn nhất và căng thẳng nhất của tôi.

Tất cả những gì cơ bản cần phải làm là có Flexbox kéo dãn <div>các yếu tố để giới hạn của họ theo mặc định, nhưng khi <span>nhấn vào, hạn chế sự căng của <div>để <span>chiều rộng ...

mã giả:

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

Tôi đã chia CSS thành một phần 'cơ chế có liên quan' và 'chỉ dùng mắt' để dễ đọc (và mã hóa lại).

Mã được bình luận nhiều, vì vậy không có nhiều văn bản ở đây ...

Quirk Bằng cách nào đó có một độ trễ thêm trong transitionkhi chuyển divtừ từ max-width: 100%sang max-width = span width. Tôi đã kiểm tra hành vi này trong Chrome, Edge, IE11 và Firefox (tất cả W10) và tất cả dường như có sự giải quyết này. Hoặc một số recalc nội bộ trình duyệt đang diễn ra hoặc có thể transitionthời gian được sử dụng hai lần ('cảm thấy như'). Vice Versa, đủ kỳ lạ, không có sự chậm trễ thêm.

Tuy nhiên, với thời gian chuyển tiếp ngắn (ví dụ 150ms, như tôi đang sử dụng), độ trễ thêm này không / hầu như không đáng chú ý. (Một câu hỏi hay cho câu hỏi SO khác ...)

CẬP NHẬT

Phiên bản mới mà đặt lại tất cả khác <div>. Tôi thực sự ghét sự tăng vọt, nhưng đó là do Flexbox kéo dài và transitiongiá trị. Không transitioncó bước nhảy có thể nhìn thấy. Bạn cần phải thử những gì làm việc cho bạn.

Tôi chỉ thêm vào document.querySelectorAll()mã javascript.


Cảm ơn bạn rất nhiều cho đề xuất của bạn. Nó không phù hợp với nhu cầu của tôi (nhưng đó là điểm khởi đầu rất tốt) vì khi chúng tôi nhấp vào div chất lỏng, nó phù hợp nhưng hai div khác không mở rộng như nhau để chiếm không gian còn lại. Đó là 50% của vấn đề. Và vâng, có sự khó chịu này quá. Kịch bản của Azametzin thực hiện công việc 100% mà không có sự khó hiểu .. Cuối cùng tôi đã trộn cả kịch bản của anh ấy và sử dụng chuyển đổi flex thay vì độ rộng tối đa và nó hoạt động như một nét quyến rũ. Cảm ơn bạn rất nhiều vì đã giúp đỡ của bạn !
Bachir Messaouri

Nhận xét của @Azametzin: Đó là rất nhiều ngày vui vẻ trong câu chuyện này. :). Có lẽ lần sau bắt đầu với hình ảnh?
Rene van der Lende

Đồng ý. Tôi nghĩ rằng lời giải thích là đủ nhưng rõ ràng, tôi đã sai. Hình ảnh là có từ ngày hôm qua mặc dù. Nhưng tôi nhận được quan điểm của bạn. Tôi sẽ chỉnh sửa tin nhắn ban đầu của mình để hiển thị tập lệnh tùy chỉnh trong vài phút. Cảm ơn rât nhiều !
Bachir Messaouri

Bài viết gốc được cập nhật với các giải pháp tùy chỉnh!
Bachir Messaouri
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.