Using the following code, I can move two rectangles together with a transition, but after moving the two rectangles together, one of the rectangles moves from bottom to top again.
var rectangles = document.getElementsByClassName("rectangle");
for (var i = 0; i < rectangles.length; i++) {
var rectangle = rectangles[i];
var upButton = rectangle.getElementsByClassName("up")[0];
var downButton = rectangle.getElementsByClassName("down")[0];
upButton.addEventListener("click", function() {
moveFile(this.parentNode, "up");
});
downButton.addEventListener("click", function() {
moveFile(this.parentNode, "down");
});
}
function moveFile(currentRectangle, direction) {
var siblingRectangle = direction === "up" ? currentRectangle.previousElementSibling : currentRectangle.nextElementSibling;
if (siblingRectangle) {
currentRectangle.classList.add(direction === "up" ? "move-up" : "move-down");
siblingRectangle.classList.add(direction === "up" ? "move-down" : "move-up");
setTimeout(function() {
if (direction === "up") {
currentRectangle.parentNode.insertBefore(currentRectangle, siblingRectangle);
} else {
currentRectangle.parentNode.insertBefore(siblingRectangle, currentRectangle);
}
currentRectangle.classList.remove(direction === "up" ? "move-up" : "move-down");
siblingRectangle.classList.remove(direction === "up" ? "move-down" : "move-up");
}, 1000);
}
}
.container {
display: flex;
flex-direction: column;
align-items: center;
height: 400px;
}
.rectangle {
width: 150px;
height: 100px;
background-color: #ccc;
border: solid rgb(255, 126, 126);
margin: 10px;
text-align: center;
font-size: 18px;
position: relative;
transition: transform 0.5s ease-in-out;
}
.move-up {
transform: translateY(-120px);
}
.move-down {
transform: translateY(120px);
}
.button {
width: 100px;
height: 30px;
background-color: #ddd;
border: none;
margin: 5px;
cursor: pointer;
}
<div class="container">
<div class="rectangle">
<div>1</div>
<button class="button up">Up</button>
<button class="button down">Down</button>
</div>
<div class="rectangle">
<div>2</div>
<button class="button up">Up</button>
<button class="button down">Down</button>
</div>
<div class="rectangle">
<div>3</div>
<button class="button up">Up</button>
<button class="button down">Down</button>
</div>
</div>
You can test the above code. As you will see, after the rectangles are moved, one of the rectangles moves up again. How to remove this extra movement?
setTimeout
, I'm listening to the event transitionend
to reset everything with the new order only after the animation finishes.const container = document.querySelector(".container");
container.onclick = function(event) {
const targetElement = event.target;
const isButtonElement = targetElement.matches('button');
const isUpAction = targetElement.classList.contains('up');
const isDownAction = targetElement.classList.contains('down');
if (isButtonElement) {
const isFirstElement = !targetElement.parentNode.previousElementSibling;
const isLastElement = !targetElement.parentNode.nextElementSibling;
// ignore edge cases
if ((isUpAction && isFirstElement) || (isDownAction && isLastElement)) {
return;
}
if (isUpAction) {
const elementToMove = targetElement.parentNode;
const elementToReplace = elementToMove.previousElementSibling;
// listen to animation finish ONCE to move elements
elementToMove.addEventListener("transitionend", onTransitionFinished(
'up',
elementToMove,
elementToReplace
), {
once: true
});
// move target up and sibling down
elementToMove.classList.add('move-up');
elementToReplace.classList.add('move-down');
}
if (isDownAction) {
const elementToMove = targetElement.parentNode;
const elementToReplace = elementToMove.nextElementSibling;
// listen to animation finish ONCE to move elements
elementToMove.addEventListener("transitionend", onTransitionFinished(
'down',
elementToMove,
elementToReplace
), {
once: true
});
// move target down and sibling up
elementToMove.classList.add('move-down');
elementToReplace.classList.add('move-up');
}
}
};
function onTransitionFinished(dir, elementToMove, elementToReplace) {
return function(event) {
// clean up all transition classes
document
.querySelectorAll('.rectangle')
.forEach((rectangle) => {
rectangle.classList.remove('move-up');
rectangle.classList.remove('move-down');
});
// insert in the right place based on direction after transition finished
if (dir === 'up') {
elementToReplace.parentNode.insertBefore(elementToMove, elementToReplace);
} else {
elementToMove.parentNode.insertBefore(elementToReplace, elementToMove);
}
};
}
.container {
display: flex;
flex-direction: column;
align-items: center;
height: 400px;
--rectangle-height: 100px;
--rectangle-border: 1px;
--rectangle-margin: 10px;
--travel-distance: calc(var(--rectangle-height) + 2*var(--rectangle-border) + 2*var(--rectangle-margin));
}
.rectangle {
width: 150px;
height: var(--rectangle-height);
background-color: #ccc;
border: var(--rectangle-border) solid rgb(255, 126, 126);
margin: var(--rectangle-margin);
text-align: center;
font-size: 18px;
position: relative;
}
.move-up {
transition: transform 0.5s ease-in-out;
/* MOVED THIS HERE! */
transform: translateY(calc(-1*var(--travel-distance)));
}
.move-down {
transition: transform 0.5s ease-in-out;
/* AND HERE! */
transform: translateY(var(--travel-distance));
}
.button {
width: 100px;
height: 30px;
background-color: #ddd;
border: none;
margin: 5px;
cursor: pointer;
}
<div class="container">
<div class="rectangle">
<div>1</div>
<button class="button up">Up</button>
<button class="button down">Down</button>
</div>
<div class="rectangle">
<div>2</div>
<button class="button up">Up</button>
<button class="button down">Down</button>
</div>
<div class="rectangle">
<div>3</div>
<button class="button up">Up</button>
<button class="button down">Down</button>
</div>
</div>