Перехід гнучкості: розтягнути (або зменшити), щоб відповідати вмісту


9

Я зашифрував сценарій (за допомогою користувача тут), який дозволяє мені розширити вибраний div і змусити інші діви відповідно поводитись однаково, розтягуючи рівномірно, щоб підходити до залишків місця (крім першого, ширина якого фіксована).

А ось картина того, чого я хочу досягти:

введіть тут опис зображення

Для цього я використовую флекс та переходи.

Він працює добре, але сценарій jQuery визначає значення розтягування "400%" (що чудово підходить для тестування).

Тепер я хотів би, щоб вибраний div розширити / зменшити, щоб точно відповідати вмісту замість фіксованого значення "400%".

Я поняття не маю, як я міг це зробити.

Це можливо ?

Я спробував клонувати div, підключити його до вмісту, отримати його значення, а потім використати це значення для переходу, АЛЕ це означає, що я маю початкову ширину у відсотках, але цільове значення в пікселях. Це не працює.

І якщо я перетворюю значення пікселя у відсотки, то результат не точно відповідає вмісту з будь-якої причини.

У всіх випадках це здається трохи складним способом досягти того, чого я хочу в будь-якому випадку.

Чи не існує жодної властивості flex, яку можна було б перенести, щоб відповідати вмісту вибраного div?

Ось код ( відредагований / спрощений, оскільки для кращого читання ):

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/

EDIT: Ось моє робоче рішення за допомогою всіх вас (розміри оновлені на розмірі вікна + кількість дивок та динамічно обчислюється ширина першого стовпця):

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>

І ось повноцінний приклад на роботі (стилі + padding + більше даних):

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

Дякую всім !


1
Будь ласка, будьте ласкаві, щоб додати остаточний результат до свого запитання, що показує, що ви створили після всього цього. Мене ретельно цікавить, як це виглядає. Дякую! (... можливо, деяке розуміння того, для чого воно використовується? ...)
Рене ван дер Ленде,

Гаразд, я зроблю це, як тільки очистив свій сценарій від усіх непотрібних елементів (за кілька хвилин я здогадуюсь). Це сказало: як це зробити? Чи просто я редагую оригінальну публікацію і додаю свою думку, навіть якщо відповідь Азаметціна є ідеальною? Я
змінюю

Ось у цьому суть: я емілюю якусь таблицю Excel та хочу, щоб люди могли натискати певний стовпець, щоб відповідати вмісту. Зазвичай таблиця буде робити, але стовпці ведуть себе не правильно, коли йдеться про їх зміну так, як я хочу (легко розгортати одну колонку за один раз, але важко змусити інших вести себе в режимі реального часу, щоб зайняти простір, що залишився flex робив би для дівок. Вони просто невпинно розширюються. Я зробив деякі дослідження, і те, що я хочу, неможливо за допомогою таблиць). Є й інші причини, по яких я не використовую таблицю або таблицю даних, але це поза межами цієї публікації.
Бачір Мессаурі

1
Досить просто, просто натисніть «редагувати», перейдіть до кінця питання і почніть додавати деякі прозаїчні розмірковування, включаючи новий фрагмент (усе це). Переконайтеся, що "приховати фрагмент" за замовчуванням (нижній лівий прапорець), менш нав'язливий / відволікаючий для "нових читачів". Перша моя думка полягала в тому, що ти збираєшся використовувати його для якогось вигадливого віджета, що нагадує гармоніку, піктограми тощо. Електронна таблиця, так, у мене є повний екран, який розтягується по всьому місцю вгору / вниз / вліво / вправо, лежачи навколо, використовуючи ті самі принципи, які обговорювалися тут (якщо вам цікаво).
Рене ван дер Ленде

2
Чудово, що ви пройшли додаткову милю, розмістивши остаточний результат. Робить відповіді на запитання про ТАК набагато кориснішим! Cudos ...
Rene van der Lende

Відповіді:


4

Вирішити це можна за допомогою max-widthі calc().

По- перше, замінити width: 100%з flex: 1для діви в CSS, таким чином , вони будуть рости, що краще в цьому випадку. Крім того, використовуйте перехід для max-width.

Тепер ми повинні зберігати деякі відповідні значення:

  • Кількість дівок, які будуть анімовані ( divsLengthзмінна) - 3 у цьому випадку.
  • Загальна ширина, що використовується для фіксованого extraSpaceдіва, та межі ( змінна) - 39 пікс.

За допомогою цих 2 змінних ми можемо встановити за замовчуванням max-width( defaultMaxWidthзмінну) всі div, а також використовувати їх пізніше. Ось чому вони зберігаються у всьому світі.

defaultMaxWidthЦе calc((100% - extraSpace)/divsLength).

Тепер введемо функцію клацання:

Щоб розширити div, ширина цільового тексту буде збережена у змінній, що називається, textWidthі вона буде застосована до div так, як max-width.він використовується .getBoundingClientRect().width(оскільки повертає значення з плаваючою комою).

Для інших див, вона створена calc()для , max-widthякий буде застосовуватися до них.
Це: calc(100% - textWidth - extraScape)/(divsLength - 1).
Обчислений результат - ширина, якою має бути кожен залишився діл.

При натисканні на розширений 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
Тільки FYI, я відредагував код з тих пір, щоб зробити його простішим для читання та тестування. Я додав прольоти, які є хорошим доповненням. Я не додав переходи максимальної ширини, однак, як і раніше, вони не спрацювали чудово.
Бачір Мессаурі,

1
Класно. Я просто вирішив це. Я редагую відповідь.
Азамецін

1
Приємно! Приділіть собі час, коли я виходжу на пару годин у будь-якому випадку ;-)
Бачір Мессаурі,

1
Ооо, я щойно побачив ваш відредагований коментар (очікував оновлення коментаря). Використання getBoundingClientRect () дуже розумне, і мені не прийшло в голову (те саме, що враховувати фіксовану ширину 39 пікселів). Я перевірю трохи більше, щоб перевірити, чи працює він для будь-якого розміру Windows та будь-якої кількості Fluid divs. Я повернуся до вас. Дуже дякую! PS: ваше рішення працює з jQuery 3.3.1, але НЕ з 3.4.1 з будь-якої причини.
Бачір Мессаурі,

3

Потрібно розібратися з функціями ширини або обчислення. Flexbox мав би рішення.

Щоб зробити всі диви рівними (не першими), ми використовуємо 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>

Визначте гнучкі правила для звичайного діва та вибраного діва. 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;
}

Додайте вибраний клас щоразу, коли користувач натискає діл. Ви також можете використовувати тумблер для другого клацання, щоб ви могли зберегти вибрані елементи на карті, і ви зможете показати кілька вибраних елементів (не, звичайно, з цим прикладом коду).

$(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, усі Fluid-диски скидаються, обмінюючись ними однаково простір, не турбуючись про їхній вміст. Отже, якщо я не помиляюся, це абсолютно протилежне тому, що ви робили. Ще не впевнений, як це змінити.
Бачір Мессаурі,

Однак, якщо це можна змінити, мені дуже подобається, наскільки елегантне та просте ваше рішення, оскільки немає необхідності возитися з атрибутом ширини css. Flex, здається, просто вирішує це внутрішньо. Але, маючи на увазі, я маю на увазі, що буде складніше досягти зворотного шляху. Почекайте і побачите. Дякуємо за подальші дії.
Бачір Мессаурі,

1
@BachirMessaouri знову досить просто. Перегляньте оновлену версію.
ураган

Я люблю вишуканість вашого сценарію, але ваші рішення розглядають лише частини мого запиту. Моє прохання стосується не лише відповідності вмісту, але й поділу залишків місця однаково до, під час і після натискання на флюїд. Ваш сценарій не дуже добре вирішує цю проблему, якщо не є. І саме це робить все, що спалює мозок. Я додав зображення в оригінальний пост, щоб зробити мої потреби дуже зрозумілими. Інший вищезазначений сценарій починається саме там, де ваш не доставить. Дивно здається, що суміш обох ваших сценаріїв може працювати. Я тестую, і як тільки я закінчу, я повернуся до вас.
Бачір Мессаурі

@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час використовується два рази ("відчувається, що"). Віце Верса, як не дивно, зайвих затримок немає.

Однак, якщо короткий час переходу (наприклад, 150 мс, як я зараз використовую), ця додаткова затримка не є / навряд чи помітна. (Приємно для іншого питання SO) ...

ОНОВЛЕННЯ

Нова версія, яка скидає всі інші <div>. Я справді ненавиджу стрибки, але це пов’язано з розтягуванням Flexbox і transitionцінністю. Без transitionвидимих ​​стрибків. Вам потрібно спробувати, що для вас працює.

Я додав лише document.querySelectorAll()до коду javascript.


Дуже дякую за вашу пропозицію. Це не відповідає моїм потребам (але це дуже хороша відправна точка), оскільки коли ми натискаємо на флюїдний дів, він добре відповідає, але два інших діви не розширюються однаково, щоб зайняти залишок місця. Це 50% випуску. І так, є і ця химерність. Сценарій Азамеціна робить роботу на 100% без зайвих зусиль. Я в кінцевому підсумку змішав і його сценарій, і використання гнучких переходів замість max-width, і це працює як шарм. Велике спасибі за вашу допомогу !
Бачір Мессаурі,

Зауваження @Azametzin: У цій сазі були дуже веселі дні. :). Може, наступного разу почніть із зображення?
Рене ван дер Ленде

Добре. Я вважав, що пояснень достатньо, але, очевидно, я помилявся. Зображення є з вчорашнього дня, хоча. Але я розумію вашу думку. Я відредагую своє оригінальне повідомлення, щоб за лічені хвилини показати свій власний сценарій. Дуже дякую !
Бачір Мессаурі

Оригінальна публікація оновлена ​​спеціальним рішенням!
Бачір Мессаурі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.