I am creating a navigation bar in Reactjs with four elements. These items are a simple unordered list with some css using flexbox to align them horizontaly.
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
<li>Four</li>
<ul/>
What I want to achieve is: When a list item is selected, align the selected list item to center. I have added a professional picture for clarification. This change will later be animated for a smooth transition, like a carousel.
Following is the css for <ul> tag.
ul {
list-style-type: none;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
}
What I've tried is to use the align-self: center on the one of the <li> items, but with no luck.
Does anyone have any experience with doing something similar? I am open for all types of solutions, even those that does not use flexbox.
Thank You!
Do the list elements have a fixed with, and do you know how many items there are? If so, you can calculate the center of the list, the item offset, and add a CSS transform.
Example:
You have a list of four items.
Each item has width equal to 100px.
The total width of the list is therefore 400px.
The center point of the list is 200px from the left.
The center point of item number two is 150px from the left. We therefore have to move the list 200px - 150px = 50px from the left.
The center point of item number four is 350px from the left. We therefore have to move the list 200px - 350px = -150px from the left.
If your list is dynamic, both regarding to list length and item width, you can use Element.getBoundingClientRect() to find the elements' dimensions, and use the same calculations as above.
Codepen: https://codepen.io/anon/pen/vjJMVL
HTML:
<ul class="selected-2">
<li>1</li>
<li class="selected">2</li>
<li>3</li>
<li>4</li>
</ul>
<ul class="selected-4">
<li>1</li>
<li>2</li>
<li>3</li>
<li class="selected">4</li>
</ul>
CSS:
ul {
display: flex;
justify-content: center;
list-style: none;
}
li {
width: 80px;
height: 80px;
background-color: #eee;
margin: 10px;
}
.selected {
background-color: #ccc;
}
.selected-2 {
transform: translateX(50px)
}
.selected-4 {
transform: translateX(-150px)
}
Calculate the clicked <MenuItem> center by using Element.getBoundingClientRect() to get it's left, and width, and pass it to the parent (<Menu>). In the parent use the <ul>s ref to get it's left and width with Element.getBoundingClientRect(). Calculate the moveTo state, and update the <ul>s style transform: translateX() accordingly:
const { Component } = React;
const items = ['One', 'Two', 'Three', 'Four'];
class MenuItem extends Component {
clickHandler = (e) => {
const { left, width } = e.target.getBoundingClientRect();
const itemCenter = left + width / 2;
this.props.updateCenter(itemCenter);
}
render() {
const { children } = this.props;
return (
<li onClick={this.clickHandler}>{children}</li>
);
}
}
class Menu extends Component {
state = {
moveTo: 0
};
updateCenter = (itemCenter) => {
const { left, width } = this.ul.getBoundingClientRect();
//this.ul.style.transform = `translateX(${center}px)`;
this.setState(() => ({
moveTo: left + width / 2 - itemCenter
}));
};
render() {
const { items } = this.props;
const { moveTo } = this.state;
return (
<nav>
<ul ref={(ul) => this.ul = ul} style={{
transform: `translateX(${moveTo}px)`
}}>
{
items.map((text) => (
<MenuItem key={text}
updateCenter={this.updateCenter}>
{text}
</MenuItem>
))
}
</ul>
</nav>
);
}
}
ReactDOM.render(
<Menu items={items} />,
demo
);
/** demo only - display the center **/
body::before {
position: absolute;
left: 50%;
height: 100vh;
border-right: 1px solid black;
content: '';
}
nav {
overflow: hidden;
}
ul {
list-style-type: none;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
transition: transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
li {
padding: 1em;
border: 1px solid black;
cursor: pointer;
}
ul li:not(:last-child) {
margin: 0 1em 0 0;
}
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="demo"></div>
Since i don't fully get what you want, this is the best i could have came up with, applying absolute positioning to the selected one and have it overlap the others with z-index
document.querySelectorAll('li').forEach(function(li){
li.addEventListener('click',function(e){
document.querySelectorAll('li').forEach(function(obj){
obj.classList.remove('selected');
});
e.target.classList.add("selected");
});
});
ul {
list-style-type: none;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
}
li{
border:1px solid;
padding:10px;
cursor:pointer;
position:relative;
transition:all ease 1s;
margin:0 20px;
}
.selected{
transform:scale(2.1);
background:white;
box-shadow:0px 0px 55px black;
position:absolute;
z-index:5;
}
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
<li>Four</li>
</ul>
Related
I have a grid that draws squares in cells. It has number of rows and number of columns, then it draw the grid cells and check if in each cell there should be a square or not (according to an array) and draws a square if needed.
The HTML end result looks something like this: (lets say I have 1 row and 3 columns and only 2 cells should have squars)
.row {
display: flex;
flex-wrap: wrap;
flex: 10000 1 0%;
}
.column {
display: flex;
flex-wrap: wrap;
max-width: 100px;
min-width: 10px;
padding: 4px;
border: 1px solid grey;
}
.square {
background-color: red;
width: 100%;
aspect-ratio: 1/1;
border-radius: 5px;
}
<div class="row">
<div class="column">
<div class="square"></div>
</div>
<div class="column">
<div class="square"></div>
</div>
<div class="column"></div>
</div>
The rows take the full width of the screen and the size of the columns should be identical between all of the columns and changing by the number of columns on the screen (For example if I have 5 columns they should all be with a width of 100px, but if I have 1000 columns they should all be with a width of 10px).
My problem is that after a certain break point in the column size the padding and border radius seems weird and I want to change their values when I hit that break point.
I can't use #container queries as there are still not fully supported.
If it help I'm using vue 2. but I think a CSS solution will be better in this case.
Trying to address the issue described:
My problem is that after a certain break point in the column size the
padding and border radius seems weird and I want to change their
values when I hit that break point. I can't use #container queries as
there are still not fully supported.
I crafted a little demo that helped me better explore the conditions bringing to such a scenario.
Obtaining border: collapse equivalent on flexbox items
The .row element remains a flexbox container but its flex items instead of having their border set, they are styled with their outline set.
The outline doesn't occupy space and it's expected to "collapse" when colliding with the outline produced by another element.
So to make it sure the layout wasn't affected by styling oddities, in the attempt to show off the borders of the flex items, this demo just relies on 2 key aspects to render those borders:
Setting the gap between the flex items
Setting the outline size expected to cover the gap left between
elements
.row {
gap: var(--col-gap);
}
.column {
outline: var(--col-gap) solid gray;
}
Using ::after for adding content to an element
Plus the red dot is applied as an ::after pseudo element with position:absolute, again to make sure that nothing affected the grid layout:
.column.square::after {
position: absolute;
content: '';
background-color: red;
width: 50%;
aspect-ratio: 1/1;
border-radius: 100%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
The dashboard - exploring the options
Starting from there I added a "dashboard" with position: fixed that remains on top of the page and lets you control:
column width (px): here you set the width changing the cols per row according to the available container space
columns per row: here you set the cols per row changing their width according to the available container space
width
gap between cells (px): the gap between cells on the grid
toggle red dots visibility: will show/hide the red dots proving again that display: none; doesn't change the grid layout that it's depending exclusively by the .column element size set through the custom variable --col-width
toggle counter visibility: will show/hide the counter on top of each flex item
Conclusions so far:
Despite the efforts to minimize any interfence and taking all the steps needed to correctly setup a grid layout depending only on the fixed size of its cells, there are still some rendering issue with sometimes the occurrence of regular mismatching patterns on the border size for some lines. I should say that I only experience the problem on my laptop display and not on my desktop monitor so that's another factor.
I tried with different parameters on my demo and playing with the numbers, considering also the gap. A good and safe layout can be found minimizing potential problems (also raising the border size for example).
I couldn't get further than this using the flex layout.
const container = document.getElementById('container');
//draws the board
emptyElementAndFillWithColumns(container, 100);
//sets some columns randomly as .square
addRandomSquares(container);
//initializes the dashboard with the value coming from the css custom props
let columnsGap = parseInt(getCssCustomProp('col-gap'));
let columnsWidth = parseInt(getCssCustomProp('col-width'));
document.getElementById('gap').value = columnsGap;
document.getElementById('width').value = columnsWidth;
document.getElementById('width').dispatchEvent(new Event('change'));
document.getElementById('cols').value = Math.trunc(container.offsetWidth / (columnsWidth+columnsGap));
//input#width change event handler
document.getElementById('width')
.addEventListener('change', event => {
const width = parseInt(event.target.value);
const newCols = Math.trunc(container.offsetWidth / (width+columnsGap));
setCssCustomProp(container, 'col-width', `${width}px`);
document.getElementById('cols').value = newCols;
});
//input#cols change event handler
document.getElementById('cols')
.addEventListener('change', event => {
const cols = parseInt(event.target.value);
const newWidth = Math.trunc(container.offsetWidth / cols) - columnsGap;
setCssCustomProp(container, 'col-width', `${newWidth}px`);
document.getElementById('width').value = newWidth;
});
//input#gap change event handler
document.getElementById('gap')
.addEventListener('change', event => {
const gap = parseInt(event.target.value);
setCssCustomProp(container, 'col-gap', `${gap}px`);
columnsGap = gap;
});
//input#toggle-dots change event handler
document.getElementById('toggle-dots')
.addEventListener('change', event => {
container.classList.toggle('hide-dots');
});
//input#toggle-counters change event handler
document.getElementById('toggle-counters')
.addEventListener('change', event => {
container.classList.toggle('hide-counters');
});
//sets the --propName custom property at the style of target
function setCssCustomProp(target, propName, value){
target.style.setProperty(`--${propName}`, `${value}`);
}
//gets the --propName custom property value from the rule set on :root
function getCssCustomProp(propName){
const propValue =
getComputedStyle(document.documentElement).getPropertyValue(`--${propName}`);
return propValue;
}
//resets the container and appends a count number of columns
function emptyElementAndFillWithColumns(target, count){
for (i = 0; i <= count; i++) {
const column = document.createElement('div');
column.classList.add('column');
target.append(column);
}
}
//adds the square class to random .column elements in target
function addRandomSquares(target){
target.querySelectorAll('.column').forEach(column => {
if (Math.random() >= 0.5)
column.classList.add('square');
})
}
:root {
--col-width: 100px;
--col-gap: 1px;
}
*,
*::after,
*::before {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: sans-serif;
}
.row {
display: flex;
flex-wrap: wrap;
gap: var(--col-gap);
counter-reset: itemnr;
}
.column {
position: relative;
display: flex;
flex-wrap: wrap;
width: var(--col-width);
height: var(--col-width);
padding: 4px;
outline: var(--col-gap) solid gray;
}
.column.square::after {
position: absolute;
content: '';
background-color: red;
width: 50%;
aspect-ratio: 1/1;
border-radius: 100%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.dashboard {
position: fixed;
right: 1rem;
top: 2rem;
border: solid darkgray;
padding: 1em;
z-index: 100;
background: gray;
color: white;
font-weight: 600;
font-size: 1.2rem;
opacity: .9;
}
.dashboard > *{
display: grid;
grid-template-columns: 1fr auto;
width: 100%;
gap: 1em;
}
.dashboard label{
}
.dashboard input[type="number"] {
width: 5em;
cursor: pointer;
}
.dashboard input[type="checkbox"] {
width: 1rem;
line-height: 1rem;
cursor: pointer;
}
#container.hide-dots .square::after{
display: none;
}
#container.hide-counters .column::before{
display: none;
}
small{
grid-column: 1 / -1;
font-size:.8rem;
text-align: center;
width: 100%;
margin-bottom: 1rem;
}
.column::before{
position: absolute;
counter-increment: itemnr;
content: counter(itemnr);
font-size: .8rem;
z-index: 10;
font-weight: 600;
}
<div id="container" class="row">
<div class="column square">
</div>
<div class="column"></div>
</div>
<div class="dashboard">
<div>
<label for="width">column width (px):</label>
<input
id="width" type="number" max="100" min="10">
</div>
<div>
<label for="cols">columns per row:</label>
<input
id="cols" type="number" max="50" min="1">
</div>
<div>
<label for="gap">gap between cells (px):</label>
<input
id="gap" type="number" max="10" min="0">
</div>
<div style="margin-top: 1rem;">
<label for="toggle-dots">toggle red dots visibility:</label>
<input id="toggle-dots" type="checkbox" checked>
</div>
<div>
<label for="toggle-counters">toggle counter visibility:</label>
<input id="toggle-counters" type="checkbox" checked>
</div>
</div>
If you want to increase or decrease size of padding you can give padding size in percent (%) that depends on parent element.
I'm building a custom dropdown. The real thing is going to have all the necessary ARIA attributes of course, but here's the barebones version:
[...document.querySelectorAll('.select')].forEach(select => {
select.addEventListener('click', function() {
select.nextElementSibling.classList.toggle('visible');
});
});
.dropdown {
position: relative;
width: 16rem;
margin-bottom: 2rem;
}
.select {
display: flex;
align-items: center;
height: 2rem;
background-color: #ccc;
}
.popup {
display: none;
position: absolute;
left: 0;
right: 0;
height: 10rem;
background-color: #eee;
box-shadow: 0 0 0.5rem red;
}
.popup.visible {
display: block;
}
<!doctype html>
<html>
<body>
<div class="dropdown">
<div class="select">Button 1 ▼</div>
<div class="popup">Popup 1</div>
</div>
<div class="dropdown">
<div class="select">Button 2 ▼</div>
<div class="popup">Popup 2</div>
</div>
</body>
</html>
The obvious issue is that, when you open the first dropdown, popup 1 appears behind button 2. The obvious solution would be to give .popup a z-index, and make it an absurdly large value like 999 to make sure it appears above other elements on the page as well.
However, in my case, I would also like the popup to appear behind its corresponding button (in order to hide its box-shadow).
If I give the button a z-index greater than the popup's, the original problem returns: popup 1 appears behind button 2. If I instead give the z-index: 999 to the entire .dropdown and create a new stacking context, the same thing happens.
Is there any way I can meet my two requirements at the same time (popup behind its button, and only that one, but above everything else on the page)?
You could track the dropdown .open state. And use that to toggle the display property of its child .popup. However the .dropdown.open state will have a z-index:1, that way it will always show up on top of elements below it.
[...document.querySelectorAll('.select')].forEach(select => {
select.addEventListener('click', function(event) {
const {
target: {
parentElement: activeDropdown
}
} = event;
activeDropdown.classList.toggle('open');
const container = select.parentElement.parentElement;
const dropdowns = container.querySelectorAll('.dropdown');
Array.from(dropdowns).forEach(item => {
if (item !== activeDropdown) {
item.classList.remove('open')
}
})
});
});
.dropdown {
position: relative;
width: 16rem;
margin-bottom: 2rem;
}
.dropdown.open {
z-index: 1;
}
.dropdown.open>.popup {
display: block;
}
.select {
display: flex;
align-items: center;
height: 2rem;
background-color: #ccc;
}
.popup {
display: none;
position: absolute;
left: 0;
right: 0;
height: 10rem;
background-color: #fff;
box-shadow: 0 0 0.5rem red;
}
<!doctype html>
<html>
<body>
<div class="dropdown">
<div class="select">Button 1 ▼</div>
<div class="popup">Popup 1</div>
</div>
<div class="dropdown">
<div class="select">Button 2 ▼</div>
<div class="popup">Popup 2</div>
</div>
</body>
</html>
I'm currently working on a list, and it's scrollable on y-axis (overflow-y: scroll). I hope when the mouse hover above the list elements, they will scale up and overflow outside the container. However, as the container is now set to overflow-y: scroll for the scroll bar, it doesn't allow the children elements to overflow. Basically, I hope to have a scroll bar while allowing child elements to overflow out of the box. Please help.
body {
display: flex;
justify-content: center;
align-items: center;
}
ul {
padding: 8px;
border: 1px solid green;
/* overflow-y: scroll; */
height: 300px;
width: 200px;
}
li {
list-style-type: none;
height: 80px;
border: 1px solid brown;
transition: 0.5s;
}
li:hover {
transform: scale(1.3);
}
<body>
<ul>
<li>
Hello
</li>
<li>
Hello
</li>
<li>
Hello
</li>
<li>
Hello
</li>
<li>
Hello
</li>
</ul>
</body>
If I understand correctly your needs, I dont think you can show overflow parts of an element in a hoverflow:scroll container.
I think/am convinced that there is a better way to do this, but I leave my attempt here.
Notes :
It only copy the text of the element, if you have more element, (in line with this attempt) you will probably need to clone (MDN) the hovered element with its children.
Because on top of the scrollbar and my element is outside the container, it's not easy to scroll
let element = document.getElementById("hoveredElement");
showHoveredElement = (li) => {
let offsets = li.getBoundingClientRect();
element.textContent = li.textContent;
element.style.top = offsets.top + window.scrollY +"px";
element.style.left = offsets.left + window.scrollX +"px";
element.style.width = offsets.width+"px";
element.style.height = offsets.height+"px";
element.classList.remove("d-none");
}
hideHoveredElement = () => {
element.classList.add("d-none");
}
document.querySelectorAll("li").forEach((li) => {
li.addEventListener("mouseover", () => {
showHoveredElement(li);
})
li.addEventListener("mouseout", () => {
hideHoveredElement();
})
});
body {
display: flex;
justify-content: center;
align-items: center;
}
ul {
padding: 8px;
border: 1px solid green;
overflow-y: scroll;
overflow-x: hidden;
height: 300px;
width: 200px;
}
li, #hoveredElement {
height: 80px;
border: 1px solid brown;
}
#hoveredElement{
position:absolute;
transition-duration: 0.5s;
background-color:white;
}
#hoveredElement:hover {
display:block;
transform: scale(1.3);
}
.d-none{
display:none;
}
<ul>
<li>
A
</li>
<li>
B
</li>
<li>
C
</li>
<li>
D
</li>
<li>
E
</li>
</ul>
<div id="hoveredElement" class="d-none"></div>
hope you can help me.
I am trying to create a navigation with a variable amount of list elements. Now I want to automatically set the height of the list elements based on the height of the surrounding div element, and the number of list elements that are in the div. Is there a way to do so, or can I only do it the other way around, by simply not giving the div a height value, but rather give the list elements a fixed height?
Hope you can help me.
#nav ul{
margin: 0;
padding: 0;
list-style: none;
}
#nav{
margin-top: 30px;
width: 19%;
background-color: red;
float:left;
border-top: 1px solid black;
}
#nav ul li{
text-align: center;
width: 100%;
background-color: yellow;
height: 50px;
margin-top: 20px;
line-height: 50px;
border-bottom: 1px solid black;
border-left: 1px solid black;
transform: rotate(20deg);
}
You can use the below code,it will definitely work
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<div id="change" style="height:200px;border:1px solid black">
<ul>
<li>HI</li>
<li>Hello</li>
<li>Bye</li>
<li>Byesdfsd</li>
<li>sdfsd</li>
<li>sdfsd</li>
</ul>
</div>
<script>
var height=document.getElementById("change").style.height;
height=height.replace("px","");
var lis=document.getElementsByTagName("li");
for(var i=0;i<lis.length;i++){
lis[i].style.height=(parseInt(height))/(parseInt(lis.length))+"px";
console.log("getting height "+lis[i].style.height);
}
</script>
</body>
</html>
So you can give the height of the div in a class or in the inline style,but you will have to change the code accordingly in the js part for fetching the height of the div.
I have got the li tags and then depending on their number i have divided the height of the enclosing div by the number of list tags
So basically what you want is that if the div is 100px high and you have 4 items in the list, each item should be 25px high, and if you have 5 items then each should be 20px high. Is it correct?
In this case, if you don't want to resort to javascript, you can use flexbox.
Here, javascript is only used to dinamically add items to the menu in order to demonstrate flexbox behavior:
var menuEl = document.getElementById('menu');
document.getElementById('addMenuItemBtn').onclick = function () {
var numberOfItems = menuEl.childElementCount;
// create a new menu item
var newListElement = document.createElement('li');
newListElement.appendChild(document.createTextNode('Menu item ' + (numberOfItems + 1)));
newListElement.setAttribute('class', 'item');
// append the new item
menuEl.appendChild(newListElement);
}
document.getElementById('removeMenuItemBtn').onclick = function () {
var numberOfItems = menuEl.childElementCount;
if (numberOfItems > 1) {
// if there are at least 2 items, remove the last item
var lastItemIndex = numberOfItems - 1;
var lastItem = menuEl.children[lastItemIndex];
lastItem.remove();
}
}
.container {
list-style: none;
margin: 0;
padding: 0;
width: 320px;
height: 240px; /* enough for 6 items with a height of 40px */
background-color: #DDD;
display: flex;
flex-direction: column;
}
.item {
width: 100%;
margin: 5px;
padding: 5px;
flex-grow: 1;
flex-shrink: 1;
flex-basis: 30px;
}
<ul class="container" id="menu">
<li class="item">Menu item 1</li>
<li class="item">Menu item 2</li>
</ul>
<input type="button" value="Add menu item" id="addMenuItemBtn">
<input type="button" value="Remove menu item" id="removeMenuItemBtn">
Since the container is 240px high and items have a minimum height of 40px (flex-basis: 30px + margin: 5px), there is enough space for 6 items. If you add more than 6 items, the extra elements will overflow.
You can read more about flexbox in this article on css-tricks.com.
You'll likely want to change a lot of things in my snippet, it's just a proof of concept!
Just keep in mind that some old browsers don't support it, or support it with
a legacy syntax.
Is there any way to have a transition on the order of flex-box items?
In other words, can I have this (details in this fiddle)
#container {
display: flex;
}
#container:hover div:last-child {
order: -1;
}
animated (the element getting the new position assumes it's position over time), please?
I am not really answering the question because I am not using the order property.
But I wanted to do something similar to what you expect, and finally decided to :
In HTML, add a data-order attribute to the elements
Add the CSS properties for each element position
Change the data-order using Javascript
Using CSS transitions for the interpolation
setInterval(changeOrder, 3000);
function changeOrder() {
const allSlides = document.querySelectorAll(".single-slide");
const previous = "1";
const current = "2";
const next = "3";
for (const slide of allSlides) {
const order = slide.getAttribute("data-order");
switch (order) {
case current:
slide.setAttribute("data-order", previous);
break;
case next:
slide.setAttribute("data-order", current);
break;
case previous:
slide.setAttribute("data-order", next);
break;
}
}
}
.all-slides {
display: flex;
width: 80vw;
margin: 0 auto;
perspective: 500px;
height: 500px;
}
.single-slide {
padding: 30px 20px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
width: 30%;
position: absolute;
background-color: white;
transition: 2s ease;
box-shadow: 0px 5px 10px lightgrey;
}
/* Left slide*/
.single-slide[data-order="1"] {
left: 10vw;
transform: translate(-50%) scale(0.8, 0.8);
z-index: 1;
opacity: 0.7;
}
/* Middle slide */
.single-slide[data-order="2"] {
left: 40vw;
transform: translate(-50%);
z-index: 3;
opacity: 1;
}
/* Right slide*/
.single-slide[data-order="3"] {
left: 90vw;
transform: translate(-120%) scale(0.8, 0.8);
z-index: 2;
opacity: 0.7;
}
.single-slide:nth-child(2) {
order: 3;
}
.single-slide:nth-child(1) {
order: 2;
}
.single-slide:nth-child(3) {
order: 1;
}
<div class="all-slides">
<div class="single-slide" data-order="2">
<h3>First slide </h3>
<p>Some text</p>
</div>
<div class="single-slide" data-order="3">
<h3>Second slide</h3>
<p>Some other text</p>
</div>
<div class="single-slide" data-order="1">
<h3>Third slide</h3>
<p>Yet some other text</p>
</div>
</div>
This could be useful if you want to animate a slider (or anything else), but want to keep the order of the elements in the HTML for accessibility purposes, which is one of the useful usage of the order property. See https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Ordering_Flex_Items#The_order_property_and_accessibility
Sadly no: the order attribute is animatable, but only as integers. That means that for each step/frame of the animation it will interpolate the value by flooring to the neareast integer. So items will only ever show up in the slot that the computed integer value results in, never in-between in any smooth sort of motion way.
It's technically still an animation: the calculated integer position should still follow the timing function and keyframe rules of the animation, it's just that the items "jump" from position to position as they change.
See https://developer.mozilla.org/en-US/docs/Web/CSS/integer#Interpolation
This question is old now, but I recently tested this, using this Fiddle (adapted from the one posted by Jason in a comment): http://jsfiddle.net/aqrxcd1u/ (code below).
In both Chrome and Firefox this partially animates, in that the order transitions one at a time from the current value to the target value. Meaning it doesn't go from 5->1 but instead goes 5->4->3->2->1.
In desktop Safari it still goes 5->1 directly.
#container {
display: flex;
}
#container div {
width: 100px;
height: 100px;
margin-right: 10px;
background-color: red;
}
#container div:nth-child(even) {
background-color: blue;
}
}
#container div:last-child {
order: 5;
transition: order 1s;
}
#container:hover div:last-child {
order: -1 !important;
}
<div id="container">
<div style="order: 1">Element 1A</div>
<div style="order: 2">Element 2B</div>
<div style="order: 3">Element 3C</div>
<div style="order: 4">Element 4D</div>
<div style="order: 5">Element 5E</div>
</div>
As Emil stated, it only animates as an integer. However, I am thinking of a hack:
Put the element you want to display in a wrapper with 1/10 of the height, set the wrapper overflow: visible
Then put 9 spacing element between these wrappers with the same height.
Put order and transition on all of them.
Change order of a wrapper and watch it 'transitioning'.
Sadly, it's ugly and only work in Firefox.
Here is what I tested in Angular