Гибкий переход: растягивание (или сжатие) по размеру содержимого

9

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

И вот картина того, чего я хочу добиться:

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

Для этого я использую flex и переходы.

Это хорошо работает, но сценарий 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/

РЕДАКТИРОВАТЬ: Вот мое рабочее решение с помощью всех вас (размеры обновляются при изменении размера окна + количество делений и динамически рассчитывается ширина первого столбца):

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
Пожалуйста, будьте любезны добавить окончательный результат в свой вопрос, показывая, что вы создали после всего этого. Мне действительно интересно, как это выглядит. Спасибо! (... может быть, некоторое представление о том, для чего он используется? ...)
Рене ван дер Ленде
Хорошо, я сделаю это, как только очистю свой скрипт от всех ненужных элементов (думаю, через несколько минут). Тем не менее, как мне это сделать? Должен ли я просто отредактировать исходное сообщение и добавить свое мнение об этом, даже если ответ Азаметзина в порядке? Я изменил несколько вещей, но это на 90% то же самое (хотя я был бы рад показать это) ..
Башир Мессаури
Вот в чем дело: я эмулирую своего рода электронную таблицу Excel и хочу, чтобы люди могли щелкать по конкретному столбцу, чтобы соответствовать содержанию. Обычно таблица справляется, но столбцы не ведут себя правильно, когда дело доходит до изменения их размера так, как я хочу (легко расширять один столбец за раз, но трудно заставить другие вести себя в режиме реального времени, чтобы занять оставшееся пространство flex сделал бы для div. Они просто беспорядочно расширяются. Я провел некоторые исследования, и то, что я хочу, невозможно с таблицами). Есть и другие причины, по которым я не использую таблицу или таблицу данных, но это выходит за рамки этого поста.
Башир Мессаури
1
Достаточно просто, просто нажмите «редактировать», перейдите к концу вопроса и начните добавлять некоторые прозаические размышления, включая новый фрагмент (все это). Убедитесь, что «скрыть фрагмент» по умолчанию (нижний левый флажок), менее навязчивый / отвлекающий для «новых читателей». Моей первой мыслью было, что вы собираетесь использовать его для какого-нибудь причудливого «гармошкообразного» виджета, иконок и всего остального. Электронная таблица да, у меня есть какой-то полноэкранный режим, растянувшийся повсюду вверх / вниз / влево / вправо, используя те же принципы, что и здесь (если вам интересно).
Рене ван дер Ленде
2
Отлично, вы прошли лишнюю милю, опубликовав окончательный результат. Делает ответы на вопросы о ТАК гораздо более полезным! Престижность ...
Рене ван дер Ленде

Ответы:

4

Это можно решить используя max-widthи calc().

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

Теперь мы должны сохранить некоторые соответствующие значения:

  • Количество div, которые будут анимированы ( divsLengthпеременные) - 3 в этом случае.
  • Общая ширина, используемая для фиксированного div и границ ( extraSpaceпеременная) - в этом случае 39px.

С этими двумя переменными мы можем установить значение по умолчанию max-width( defaultMaxWidthпеременную) для всех элементов div, а также использовать их позже. Вот почему они хранятся по всему миру.

defaultMaxWidthЭто calc((100% - extraSpace)/divsLength).

Теперь давайте введем функцию щелчка:

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

Для остальных элементов div он создается calc()для того, 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переменная.

Azametzin
источник
1
Да, это проблема, которая требует точного расчета и лучшего понимания эффекта сжатия flexbox. У меня пока нет «идеального» решения, но я хотел бы попробовать еще раз в будущем, поэтому я поставил ваш вопрос на другой раз, чтобы попытаться выяснить, как сделать это правильно. Это хороший вызов.
Азаметзин
1
Просто к вашему сведению, я редактировал код, потому что его было проще читать и тестировать. Я добавил пролеты, которые являются хорошим дополнением. Однако я не добавил переходы максимальной ширины, так как пока они не очень хорошо работают.
Башир Мессаури
1
Круто. Я просто решил это. Я редактирую ответ.
Азаметзин
1
Ницца! В любом случае, не
торопись,
1
Ооо, я только что видел ваш отредактированный комментарий (ожидал обновления комментарий). Использование getBoundingClientRect () очень умно, и мне это не пришло в голову (то же самое для учета фиксированной ширины 39 пикселей). Я протестирую немного больше, чтобы увидеть, работает ли он для любого размера окна и любого количества делений жидкости. Я вернусь к вам. Большое спасибо! PS: ваше решение работает с jQuery 3.3.1, но НЕ с 3.4.1, по любой причине.
Башир Мессаури
3

Вам нужно разобраться с функциями width или calc. У Flexbox было бы решение.

Чтобы сделать все 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 Grow, поэтому мы используем 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. Вы также можете использовать переключатель для второго щелчка, чтобы сохранить выбранные элементы на карте и показать несколько выбранных элементов (конечно, не с этим примером кода).

$(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 будут сброшены, снова разделив поровну пространство, не заботясь об их содержании. Так что, если я не ошибаюсь, это полная противоположность тому, что вы сделали. Пока не уверен, как это изменить.
Башир Мессаури
Однако, если это можно изменить, мне очень нравится, насколько элегантно и насколько просто ваше решение, так как нет необходимости возиться с атрибутом css width. Flex, кажется, просто справляется с этим внутренне. Тем не менее, я догадываюсь, что будет труднее добиться противоположного пути. Ждать и смотреть. Спасибо за продолжение.
Башир Мессаури
1
@BachirMessaouri это довольно просто снова. Пожалуйста, смотрите обновленную версию.
ураган
Мне нравится элегантность вашего сценария, но ваши решения учитывают только часть моего запроса. Моя просьба касается не только подгонки контента, но и равномерного распределения оставшегося пространства до, во время и после щелчка жидких элементов div. Ваш сценарий не очень хорошо, если не вообще. И это то, что делает все это мозговым ожогом. Я добавил фотографию в исходное сообщение, чтобы сделать мои потребности очень ясными. Другой сценарий выше начинается именно там, где ваш не удается выполнить. Кажется странным, что сочетание обоих ваших сценариев может работать. Я проверяю, и когда я закончу, я вернусь к вам.
Башир Мессаури
@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 мс, как я сейчас использую), эта дополнительная задержка не заметна. (Хороший для другого ТАК вопрос ...)

ОБНОВИТЬ

Новая версия, которая сбрасывает все остальные <div>. Я действительно ненавижу скачок, но это происходит из-за растяжения Flexbox и его transitionценности. Без transitionскачков видно. Вы должны попробовать то, что работает для вас.

Я только добавил document.querySelectorAll()к коду JavaScript.

Рене ван дер Ленде
источник
Большое спасибо за ваше предложение. Это не соответствует моим потребностям (но это очень хорошая отправная точка), так как, когда мы нажимаем на жидкий div, он подходит хорошо, но два других div не расширяются одинаково, чтобы занять оставшееся пространство. Что составляет 50% проблемы. И да, в этом тоже есть причуды. Скрипт Azametzin делает работу на 100% без излишеств. Я закончил тем, что смешал и его скрипт, и использование flex-перехода вместо max-width, и он работает как шарм. Спасибо большое за помощь !
Башир Мессаури
Второе замечание @Azametzin: было много веселых дней в этой саге. :). Может быть, в следующий раз начать с изображения?
Рене ван дер Ленде
Хорошо. Я думал, что объяснения было достаточно, но, очевидно, я был неправ. Изображение там со вчерашнего дня, хотя. Но я понимаю вашу точку зрения. Я отредактирую свое исходное сообщение, чтобы показать мой собственный скрипт в считанные минуты. Большое спасибо !
Башир Мессаури
Оригинальный пост дополнен пользовательским решением!
Башир Мессаури