Дергание нижних карточек при раскрытии (перемещение на уровень выше)
Я реализовал список карточек, при помощи интернета, которые раскрываются при клике. Если места для раскрытия недостаточно, карточка перемещается на уровень выше с помощью transform и JS.
Проблема:
Когда нижняя карточка раскрывается, карточки "дергаются" — сначала опускаются вниз, а затем возвращаются на нужную высоту. Это заметно на глаз и выглядит неаккуратно.
В чем может быть проблема?
document.querySelectorAll(".card").forEach(card => {
card.addEventListener("click", function() {
const cardItem = this.parentElement;
const grid = cardItem.parentElement;
const itemHeight = parseFloat(window.getComputedStyle(cardItem).height);
const gridGap = parseFloat(window.getComputedStyle(grid).gap);
const expandedHeight = itemHeight * 2 + gridGap;
if (this.classList.contains("expanded")) {
this.classList.remove("expanded", "expanded-up");
this.style.height = "";
this.style.transform = "";
return;
}
document.querySelectorAll(".card.expanded").forEach(c => {
c.classList.remove("expanded", "expanded-up");
c.style.height = "";
c.style.transform = "";
});
const cardRect = cardItem.getBoundingClientRect();
const gridRect = grid.getBoundingClientRect();
const spaceBelow = gridRect.bottom - cardRect.bottom;
const neededSpace = itemHeight + gridGap;
if (spaceBelow < neededSpace) {
this.classList.add("expanded", "expanded-up");
this.style.height = `${expandedHeight}px`;
this.style.transform = `translateY(calc(-50% - ${gridGap / 2}px))`;
} else {
this.classList.add("expanded");
this.style.height = `${expandedHeight}px`;
}
});
});
* {
box-sizing: border-box;
}
html,
body {
box-sizing: border-box;
}
.cards {
display: grid;
grid-template-columns: repeat(3, 300px);
grid-auto-rows: 400px;
gap: 20px;
list-style: none;
padding: 0;
margin: 0;
position: relative;
}
.card-item {
position: relative;
height: 400px;
}
.card {
position: absolute;
width: 100%;
height: 400px;
background: #f0f0f0;
border: 1px solid #ccc;
border-radius: 8px;
padding: 20px;
transition: all 0.3s ease;
cursor: pointer;
overflow: hidden;
z-index: 1;
}
.card.expanded {
z-index: 100;
background: #fff;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
}
<ul class="cards">
<li class="card-item">
<div class="card">Карточка 1</div>
</li>
<li class="card-item">
<div class="card">Карточка 2</div>
</li>
<li class="card-item">
<div class="card">Карточка 3</div>
</li>
<li class="card-item">
<div class="card">Карточка 4</div>
</li>
<li class="card-item">
<div class="card">Карточка 5</div>
</li>
<li class="card-item">
<div class="card">Карточка 6</div>
</li>
<li class="card-item">
<div class="card">Карточка 7</div>
</li>
<li class="card-item">
<div class="card">Карточка 8</div>
</li>
<li class="card-item">
<div class="card">Карточка 9</div>
</li>
</ul>
Ответы (1 шт):
Автор решения: Grundy
→ Ссылка
Проблема в вычислении внутри transform. Так как конечная высота известна, нет смысла в использовании дополнительного calc.
Конечное смещение можно вычислить сразу: -expandedHeight/2 - gridGap/2
document.querySelectorAll(".card").forEach(card => {
card.addEventListener("click", function() {
const cardItem = this.parentElement;
const grid = cardItem.parentElement;
const itemHeight = parseFloat(window.getComputedStyle(cardItem).height);
const gridGap = parseFloat(window.getComputedStyle(grid).gap);
const expandedHeight = itemHeight * 2 + gridGap;
if (this.classList.contains("expanded")) {
this.classList.remove("expanded", "expanded-up");
this.style.height = "";
this.style.transform = "";
return;
}
document.querySelectorAll(".card.expanded").forEach(c => {
c.classList.remove("expanded", "expanded-up");
c.style.height = "";
c.style.transform = "";
});
const cardRect = cardItem.getBoundingClientRect();
const gridRect = grid.getBoundingClientRect();
const spaceBelow = gridRect.bottom - cardRect.bottom;
const neededSpace = itemHeight + gridGap;
if (spaceBelow < neededSpace) {
this.classList.add("expanded", "expanded-up");
this.style.height = `${expandedHeight}px`;
this.style.transform = `translateY(${-expandedHeight/2 - gridGap/2 }px)`;
} else {
this.classList.add("expanded");
this.style.height = `${expandedHeight}px`;
}
});
});
* {
box-sizing: border-box;
}
html,
body {
box-sizing: border-box;
}
.cards {
display: grid;
grid-template-columns: repeat(3, 300px);
grid-auto-rows: 400px;
gap: 20px;
list-style: none;
padding: 0;
margin: 0;
position: relative;
}
.card-item {
position: relative;
height: 400px;
}
.card {
position: absolute;
width: 100%;
height: 400px;
background: #f0f0f0;
border: 1px solid #ccc;
border-radius: 8px;
padding: 20px;
transition: all 0.3s ease;
cursor: pointer;
overflow: hidden;
z-index: 1;
}
.card.expanded {
z-index: 100;
background: #fff;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
}
<ul class="cards">
<li class="card-item">
<div class="card">Карточка 1</div>
</li>
<li class="card-item">
<div class="card">Карточка 2</div>
</li>
<li class="card-item">
<div class="card">Карточка 3</div>
</li>
<li class="card-item">
<div class="card">Карточка 4</div>
</li>
<li class="card-item">
<div class="card">Карточка 5</div>
</li>
<li class="card-item">
<div class="card">Карточка 6</div>
</li>
<li class="card-item">
<div class="card">Карточка 7</div>
</li>
<li class="card-item">
<div class="card">Карточка 8</div>
</li>
<li class="card-item">
<div class="card">Карточка 9</div>
</li>
</ul>