После нескольких пробных версий это, похоже, мое самое короткое и простое решение.
Все, что по сути нужно сделать, - это по умолчанию 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), и все, кажется, имеют эту причуду. Либо происходит какое-то внутреннее восстановление в браузере c, либо, может быть, время transition
используется дважды («по ощущениям»). Наоборот, как ни странно, никакой дополнительной задержки нет.
Однако при коротком времени перехода (например, 150 мс, как я сейчас использую) эта дополнительная задержка не / почти не заметна. (Хороший вопрос к другому, ТАК вопрос ...)
$(document).on('click', '.wrapper>:not(.caption) span', function (e) {
// Save the current 'toggle' status
var elemToggled = e.target.getAttribute('toggled');
// Set parent max-width to maximum space or constraint to current child width
e.target.parentElement.style.maxWidth =
(elemToggled=="true") ? '100%' : parseFloat(window.getComputedStyle(e.target).width) + 'px';
// (Re)set child toggle state
e.target.setAttribute('toggled', (elemToggled=="true") ? false : true);
});
/*********************/
/* Wrapper mechanism */
/*********************/
.wrapper { /* main flexible parent container */
display : flex; /* [MANDATORY] Flexbox Layout container, can't FBL without */
flex-wrap: nowrap; /* [MANDATORY] default FBL, but important. wrap to next line messes things up */
flex-grow: 1; /* [OPTIONAL] Either: if '.wrapper' is a FBL child itself, allow it to grow */
width : 100%; /* [OPTIONAL] or : full parent width */
/* (Maybe a fixed value, otherwise redundant here as 'flex-grow' = 1) */
}
/* generic rule */
.wrapper>* { /* flexed child containers, being flexible parent containers themselves */
flex-grow : 1; /* [MANDATORY] important for this mechanism to work */
overflow: hidden; /* [MANDATORY] important, otherwise output looks messy */
display: flex; /* [MANDATORY] for FBL stretching */
justify-content: center;/* [MANDATORY] as per SOQ */
max-width : 100%; /* [OPTIONAL/MANDATORY], actually needed to trigger 'transition' */
}
/* exception to the rule */
.wrapper>.fixed { /* fixed child container */
flex-grow: 0; /* [MANDATORY] as per SOQ, don't allow grow */
}
/******************/
/* Eye-candy only */
/******************/
.wrapper {
border: 1px solid black;
}
.wrapper>:not(.fixed) {
transition: max-width 150ms ease-in-out;
}
.wrapper>:not(:last-child){
border-right: 1px solid black;
}
/* generic rule */
.wrapper>*>span {
white-space: nowrap;
background-color: #ddd;
}
/* exception to the rule */
.wrapper>.fixed>span {
background-color: #999;
}
/* debug helper: show all elements with outlines (put in <body>) */
[debug="1"] * { outline: 1px dashed purple }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<div class="fixed"><span>Fixed</span></div>
<div><span>Fluid (long long long long long text)</span></div>
<div><span>Fluid</span></div>
<div><span>Fluid</span></div>
</div>
ОБНОВЛЕНИЕ
Новая версия, которая сбрасывает все остальные <div>
. Я действительно ненавижу скачок, но это из-за растяжения Flexbox и значения transition
. Без transition
скачков не видно. Вам нужно попробовать то, что вам подходит.
Я только добавил document.querySelectorAll()
к коду javascript.
$(document).on('click', '.wrapper>:not(.caption) span', function (e) {
var elemToggled = e.target.getAttribute('toggled'); // Toggle status
var elemWidth = parseFloat(window.getComputedStyle(e.target).width); // Current element width
// reset ALL toggles but 'this'...
document.querySelectorAll('.wrapper>:not(.caption) span')
.forEach( function (elem,idx) {
if (elem != this){
elem.parentElement.style.maxWidth = '100%';
elem.setAttribute('toggled',false);
};
});
// Set parent max-width to maximum space or constraint to current child width
e.target.parentElement.style.maxWidth =
(elemToggled=="true") ? '100%' : parseFloat(window.getComputedStyle(e.target).width) + 'px';
// (Re)set child toggle state
e.target.setAttribute('toggled', (elemToggled=="true") ? false : true);
});
/*********************/
/* Wrapper mechanism */
/*********************/
.wrapper { /* main flexible parent container */
display : flex; /* [MANDATORY] Flexbox Layout container, can't FBL without */
flex-wrap: nowrap; /* [MANDATORY] default FBL, but important. wrap to next line messes things up */
flex-grow: 1; /* [OPTIONAL] Either: if '.wrapper' is a FBL child itself, allow it to grow */
width : 100%; /* [OPTIONAL] or : full parent width */
/* (Maybe a fixed value, otherwise redundant here as 'flex-grow' = 1) */
}
/* generic rule */
.wrapper>* { /* flexed child containers, being flexible parent containers themselves */
flex-grow : 1; /* [MANDATORY] important for this mechanism to work */
overflow: hidden; /* [MANDATORY] important, otherwise output looks messy */
display: flex; /* [MANDATORY] for FBL stretching */
justify-content: center;/* [MANDATORY] as per SOQ */
max-width : 100%; /* [OPTIONAL/MANDATORY], actually needed to trigger 'transition' */
}
/* exception to the rule */
.wrapper>.fixed { /* fixed child container */
flex-grow: 0; /* [MANDATORY] as per SOQ, don't allow grow */
}
/******************/
/* Eye-candy only */
/******************/
.wrapper {
border: 1px solid black;
}
.wrapper>:not(.fixed) {
transition: max-width 150ms ease-in-out;
}
.wrapper>:not(:last-child){
border-right: 1px solid black;
}
/* generic rule */
.wrapper>*>span {
white-space: nowrap;
background-color: #ddd;
}
/* exception to the rule */
.wrapper>.fixed>span {
background-color: #999;
}
/* show all elements with outlines (put in <body>) */
[debug="1"] * { outline: 1px dashed purple }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<div class="fixed"><span>Fixed</span></div>
<div><span>Fluid (long long long long long text)</span></div>
<div><span>Fluid</span></div>
<div><span>Fluid</span></div>
</div>