getBoundingClientRect() from a div with as style `display: contents` - javascript

The goal is to set the style and get the bounding box of a whole "row" in a css grid.
(Style setting such as highlight when hovering).
For setting the style there is the specific feature to use display: contents so the styles can affect the child elements without influencing the tree.
However I notice that if I then try to get the size (height) of that row the getBoundingClientRect returns all zeros.
const row = document.getElementById("firstrow");
console.log(row.getBoundingClientRect());
const actual_cell = document.getElementById("data");
console.log(actual_cell.getBoundingClientRect());
.grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
.row {
display: contents;
}
.row:hover div {
background-color: lightgray;
}
.cell {
border: 1px black solid;
max-height: 100px;
}
.ipsum {
height: 200px;
max-height: 300px;
}
<div class="grid">
<div class="row" id="firstrow">
<div class="cell">hello</div>
<div class="cell" id="data">world</div>
<div class="cell ipsum">ipsum lorem</div>
</div>
</div>
Of course I could iterate everything "below" the row. However this is quite a complex way, as not only getting the max height of the element, one has to check how it is displayed (pop up menus, or nested display:contents etc) and where (maybe the row is actually spanning multiple rows and there are 4 cells in above example in a "row").
So can this be done in an easy way?

You could set the style of the row with JavaScript to match that of your grid; get your values; and, then remove the inline styles to reset the row to display: contents.
const grid = document.querySelector('.grid');
console.log('grid:', grid.getBoundingClientRect());
const row = document.getElementById("firstrow");
row.style.gridTemplateColumns = '1fr 1fr 1fr'
row.style.display = 'grid';
console.log('row height:', row.offsetHeight);
console.log('row top:', row.offsetTop);
row.style = ''
const actual_cell = document.getElementById("data");
console.log('ipsum cell:', actual_cell.getBoundingClientRect());
.grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
.row {
display: contents;
}
.row:hover div {
background-color: lightgray;
}
.cell {
border: 1px black solid;
max-height: 100px;
}
.ipsum {
height: 200px;
max-height: 300px;
}
* {
margin: 0;
box-sizing: border-box;
}
<h1>hello world</h1>
<div class="grid">
<div class="row" id="firstrow">
<div class="cell">hello</div>
<div class="cell">world</div>
<div class="cell ipsum" id="data">ipsum lorem</div>
</div>
</div>

Yes, Elements with display: contents feel weird as if they are and are not in the DOM at the same time.
Getting the bounds and covering the row with a red div:
const row = document.querySelectorAll("#firstrow > *");
const rect = [...row]
.map(item => item.getBoundingClientRect())
.reduce((a, b) => {
const x = Math.min(a.left, b.left),
y = Math.min(a.top, b.top),
width = Math.max(a.right, b.right) - x,
height = Math.max(a.bottom, b.bottom) - y;
return new DOMRectReadOnly(x, y, width, height);
});
console.log(rect);
var cover = document.createElement("div");
document.body.append(cover);
Object.assign(cover.style, {
position: "absolute",
top: rect.y + "px",
left: rect.x + "px",
width: rect.width + "px",
height: rect.height + "px",
background: "#FF0000A0"
});
.grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
.row {
display: contents;
}
.row:hover div {
background-color: lightgray;
}
.cell {
border: 1px black solid;
max-height: 100px;
}
.ipsum {
height: 200px;
max-height: 300px;
}
<div class="grid">
<div class="row" id="firstrow">
<div class="cell">hello</div>
<div class="cell" id="data">world</div>
<div class="cell ipsum">ipsum lorem</div>
</div>
</div>

It seems that you can get the height of one row in px through the computed value of the grid-template-rows property of the grid element.
This computed value will return a list of all the rows's computed height, you can then grab the one you're interested in by splitting the resulting string and getting the value at the index you wish:
const big_cell = document.querySelector(".ipsum");
console.log("big_cell:", big_cell.getBoundingClientRect().height);
const small_cell = document.querySelector(".small");
console.log("small_cell:", small_cell.getBoundingClientRect().height);
const grid = document.querySelector(".grid");
document.querySelectorAll(".row").forEach((row, index) => {
row.addEventListener("mouseenter", (evt) => {
const computed = getComputedStyle(grid).gridTemplateRows.split("px")[index];
console.log("current-row", computed);
});
});
.grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
.row {
display: contents;
}
.row:hover div {
background-color: lightgray;
}
.cell {
border: 1px black solid;
max-height: 100px;
}
.ipsum {
height: 200px;
max-height: 300px;
}
<div class="grid">
<div class="row">
<div class="cell">hello</div>
<div class="cell">world</div>
<div class="cell ipsum">ipsum lorem</div>
</div>
<div class="row">
<div class="cell small">hello</div>
<div class="cell">world</div>
<div class="cell">ipsum lorem</div>
</div>
<div class="row">
<div class="cell">hello</div>
<div class="cell">world</div>
<div class="cell ipsum">ipsum lorem</div>
</div>
<div class="row">
<div class="cell">hello</div>
<div class="cell">world</div>
<div class="cell">ipsum lorem</div>
</div>
</div>

Related

How to make a 3 ✕ 3 grid using HTML and Css

I have 20 element for a grid view. But I want only 3✕3 grid view, where there will be only 9 element in the view window. And the rest of the element should be placed in the right side of the window as a scrollable asset.**
No matter what the screen size is I want to show only the first 9 element in the grid.
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
grid-gap: 5px;
}
.card {
background-color: dodgerblue;
color: white;
padding: 1rem;
height: 4rem;
}
<div class="cards">
<div class="card">ONE</div>
<div class="card">TWO</div>
<div class="card">THREE</div>
<div class="card">FOUR</div>
<div class="card">FIVE</div>
<div class="card">SIX</div>
<div class="card">SEVEN</div>
<div class="card">EIGHT</div>
<div class="card">NINE</div>
<div class="card">TEN</div>
<div class="card">ELEVEN</div>
<div class="card">TWELVE</div>
</div>
In this case the grid should flow vertically. And you can set it up like this with some calculation:
.cards {
/* how many columns on the first screen */
--cols: 3;
/* how many rows on the first screen */
--rows: 3;
/* grid gap */
--gap: 5px;
--width: calc((100% - var(--gap) * (var(--cols) - 1)) / var(--cols));
display: grid;
position: relative;
grid-auto-flow: column dense;
grid-template-rows: repeat(var(--rows), 1fr);
grid-auto-columns: var(--width);
grid-gap: var(--gap);
overflow-x: auto;
}
.card {
background-color: dodgerblue;
color: white;
padding: 1rem;
height: 4rem;
}
<div class="cards">
<div class="card">ONE</div>
<div class="card">TWO</div>
<div class="card">THREE</div>
<div class="card">FOUR</div>
<div class="card">FIVE</div>
<div class="card">SIX</div>
<div class="card">SEVEN</div>
<div class="card">EIGHT</div>
<div class="card">NINE</div>
<div class="card">TEN</div>
<div class="card">ELEVEN</div>
<div class="card">TWELVE</div>
<div class="card">THIRTEEN</div>
<div class="card">FOURTEEN</div>
</div>
.cards {
display: grid;
grid-template-columns: auto auto auto;
background-color: #2196F3;
padding: 10px;
}
.card {
background-color: rgba(255, 255, 255, 0.8);
border: 1px solid rgba(0, 0, 0, 0.8);
padding: 20px;
font-size: 30px;
text-align: center;
}
<div class="cards">
<div class="card">ONE</div>
<div class="card">TWO</div>
<div class="card">THREE</div>
<div class="card">FOUR</div>
<div class="card">FIVE</div>
<div class="card">SIX</div>
<div class="card">SEVEN</div>
<div class="card">EIGHT</div>
<div class="card">NINE</div>
</div>
A simple way to achieve this is by using nth-child CSS selector on your card class. Since, you want to display only he first 9 cards in the container, you will have to hide the cards from 10th position onwards.
Consider :nth-child(an + b). Here, the term b is the offset that you can specify to target cards. If you remove a and substitute the value of b as 10, it will target all the cards that appear as 10th child and later. The selector will be like so: :nth-child(n + 10). This is a comparatively readable solution.
Bonus Tip: To make sure the cards show up as 3 x 3 grid, you can explicitly update grid-template-columns CSS property to be repeat(3, 1fr) instead of repeat(auto-fill, minmax(150px, 1fr))
This is the final snippet:
.cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 5px;
}
.card {
background-color: dodgerblue;
color: white;
padding: 1rem;
height: 4rem;
}
/* Hide card which occurs at 10th position and above */
.card:nth-child(n + 10) {
display: none;
}
<div class="cards">
<div class="card">ONE</div>
<div class="card">TWO</div>
<div class="card">THREE</div>
<div class="card">FOUR</div>
<div class="card">FIVE</div>
<div class="card">SIX</div>
<div class="card">SEVEN</div>
<div class="card">EIGHT</div>
<div class="card">NINE</div>
<div class="card">TEN</div>
<div class="card">ELEVEN</div>
<div class="card">TWELVE</div>
</div>
I use this tool and it makes everything easier : https://cssgrid-generator.netlify.app/
You just select wich grid you want, how many rows... and it gives you the css + html :)

Equal height of children of unrelated parents

I want to have an equal height of two unrelated divs. Basically, I need to equalize the height of Parent B's child to Parent A's child. Below is the image to highlight what it is about:
Any idea how this can be accomplished with jQuery. Most of the equal height solutions I came across are about children of the same parent.
The code I used is for children within a container.
$(document).ready(function(){
$('.containers').each(function(){
var min_highestBox = 0;
$('.columns', this).each(function(){
if($(this).height() > min_highestBox) {
min_highestBox = $(this).height();
}
});
$('.columns',this).height(min_highestBox);
});
});
.containers {border:1px solid; width:100%; display:inline-block;}
.columns {border:1px solid red; padding: 20px; min-width:20%; float:left;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="containers">
<div class="columns">This is<br />the highest<br />column</div>
<div class="columns">One line</div>
<div class="columns">Four<br />lines<br />the highest<br />column</div>
</div>
<div class="containers">
<div class="columns">One line</div>
<div class="columns">Two<br>lines</div>
<div class="columns">One line</div>
</div>
I have tried it with a CSS grid but in this specific situation, a CSS grid is not a solution.
Update: the actual containers are both separate and independent of each other:
.a, .b {border:2px solid; padding: 25px; float:left; width: 200px;}
.a div, .b div {border:2px solid red;}
<div class="a">Parent A
<div>Child of A <br> some additional text</div>
</div>
<div class="b">Parent B
<div>Child of B</div>
</div>
In the case that they can't be placed inside a wrapper you can make use of a sorting from lowest to heighest and apply the heighest value to all affected nodes and a MutationObserver to easily monitor changes on these nodes to call the height-adjust routine again.
For equal height of parents:
$(document).ready(() => {
const selector = ".containers";
function resize() {
const nodes = $(selector)
.css("height", "auto")
.sort((a, b) => $(a).height() - $(b).height()),
heighest = nodes.last().height();
if (nodes.first().height() != heighest)
$(selector).css(`height`, `${heighest}px`);
}
resize();
const observer = new MutationObserver(resize);
$(selector).each((idx, el) => observer.observe(el, { childList: true, subtree: true }));
// Examples:
setTimeout(() => {
$(".columns").first().append("<br>Lorem<br>Ipsum<br>Dolor<br>sit<br>Amet");
}, 1500);
setTimeout(() => {
$(".columns").last().append("<br>Lorem<br>Ipsum<br>Dolor<br>sit<br>Amet");
}, 3000);
setTimeout(() => {
$(".columns").last().append("<br>Lorem<br>Ipsum<br>Dolor<br>sit<br>Amet");
}, 5000);
})
.containers {
border: 1px solid;
width: 100%;
display: inline-block;
}
.columns {
border: 1px solid red;
padding: 20px;
min-width: 20%;
float: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="containers">
<div class="columns">This is<br>the highest<br>column</div>
<div class="columns">One line</div>
<div class="columns">Four<br>lines<br>the highest<br>column</div>
</div>
<div class="containers">
<div class="columns">One line</div>
<div class="columns">Two<br>lines</div>
<div class="columns">One line</div>
</div>
For equal height of childs:
$(document).ready(() => {
const selector = ".columns";
function resize() {
const nodes = $(selector)
.css("height", "auto")
.sort((a, b) => $(a).height() - $(b).height()),
heighest = nodes.last().height();
if (nodes.first().height() != heighest)
$(selector).css(`height`, `${heighest}px`);
}
resize();
const observer = new MutationObserver(resize);
$(selector).each((idx, el) => observer.observe(el, { childList: true, subtree: true }));
// Examples:
setTimeout(() => {
$(".columns").first().append("<br>Lorem<br>Ipsum<br>Dolor<br>sit<br>Amet");
}, 1500);
setTimeout(() => {
$(".columns").last().append("<br>Lorem<br>Ipsum<br>Dolor<br>sit<br>Amet");
}, 3000);
setTimeout(() => {
$(".columns").last().append("<br>Lorem<br>Ipsum<br>Dolor<br>sit<br>Amet");
}, 5000);
})
.containers {
border: 1px solid;
width: 100%;
display: inline-block;
}
.columns {
border: 1px solid red;
padding: 20px;
min-width: 20%;
float: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="containers">
<div class="columns">This is<br>the highest<br>column</div>
<div class="columns">One line</div>
<div class="columns">Four<br>lines<br>the highest<br>column</div>
</div>
<div class="containers">
<div class="columns">One line</div>
<div class="columns">Two<br>lines</div>
<div class="columns">One line</div>
</div>
If you consider an extra wrapper you can do it with only CSS
.wrapper {
display: grid;
gap: 5px;
grid-auto-rows: 1fr; /* eqaul containers */
}
.containers {
border: 1px solid;
display: flex; /* equal childs */
}
.columns {
border: 1px solid red;
padding: 20px;
min-width: 20%;
}
<div class="wrapper">
<div class="containers">
<div class="columns">This is<br />the highest<br />column</div>
<div class="columns">One line</div>
<div class="columns">Four<br />lines<br />the highest<br />column</div>
</div>
<div class="containers">
<div class="columns">One line</div>
<div class="columns">Two<br>lines</div>
<div class="columns">One line</div>
</div>
</div>

Jquery Start from Beginning When A Div Reaches The End of Scroll

So i was trying to create a automatic horizontal scroll div which keeps on scrolling its elements in an infinite loop. This is what I have achieved so far:
$(document).ready(function(){
function animatethis(targetElement, speed) {
var scrollWidth = $(targetElement).get(0).scrollWidth;
var clientWidth = $(window).width();
$(targetElement).animate({ scrollLeft: scrollWidth - clientWidth },
{
duration: speed,
complete: function () {
$(targetElement).scrollLeft(0);
animatethis(targetElement, speed);
}
});
};
animatethis($('.editors-pick-slideshow'), 10000);
});
.editors-pick-slideshow {
border: 1px solid;
width: 100%;
height: 500px;
overflow-y: hidden;
overflow-x: auto;
display: block;
margin: auto;
}
.editors-pick-container {
width: 3150px;
}
.editors-pick-container > div {
display: inline-block;
width: 1000px;
margin: 0px 20px;
}
.editors-pick-elements {
display: grid;
grid-template-columns: 1fr 1fr;
max-width: 1000px;
min-width: 300px;
border: 1px solid green;
height: 500px;
grid-gap: 20px;
margin: auto;
}
.pick1 {
background-color: #ccc;
}
.pick2 {
background-color: #ccc;
}
.pick3 {
background-color: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="editors-pick-slideshow">
<div id="editorsPickContainer" class="editors-pick-container">
<div>
<div id="group1" class="editors-pick-elements">
<div class="pick1">Grid 1</div>
<div class="pick2">Grid 1</div>
</div>
</div>
<div>
<div id="group2" class="editors-pick-elements">
<div class="pick1">Grid 2</div>
<div class="pick2">Grid 2</div>
</div>
</div>
<div>
<div id="group3" class="editors-pick-elements">
<div class="pick1">Grid 3</div>
<div class="pick2">Grid 3</div>
</div>
</div>
</div>
</div>
As you can see in above snippet that when the animation reaches the end of the div it starts abruptly from the beginning. What I want to achieve here is when the animation reaches the end it should continue its flow towards the grid 1 element and keep on scrolling smoothly in an infinite manner. I hope what I am saying makes sense.
You can add a boolean parameter indicating which direction to scroll toward and negate the parameter each time the animation is complete.
function animatethis(targetElement, toEnd, speed) {
let pos;
if (toEnd) {
let scrollWidth = $(targetElement).get(0).scrollWidth;
let clientWidth = $(window).width();
pos = scrollWidth - clientWidth;
} else {
pos = 0;
}
$(targetElement).animate({
scrollLeft: pos
}, {
duration: speed,
complete: function() {
animatethis(targetElement, !toEnd, speed);
}
});
}
animatethis($('.editors-pick-slideshow'), true, 10000);
Live Example:
$(document).ready(function(){
function animatethis(targetElement, toEnd, speed){
let pos;
if(toEnd){
let scrollWidth = $(targetElement).get(0).scrollWidth;
let clientWidth = $(window).width();
pos = scrollWidth - clientWidth;
} else {
pos = 0;
}
$(targetElement).animate({ scrollLeft: pos},
{
duration: speed,
complete: function () {
animatethis(targetElement, !toEnd, speed);
}
});
}
animatethis($('.editors-pick-slideshow'), true, 10000);
});
.editors-pick-slideshow {
border: 1px solid;
width: 100%;
height: 500px;
overflow-y: hidden;
overflow-x: auto;
display: block;
margin: auto;
}
.editors-pick-container {
width: 3150px;
}
.editors-pick-container > div {
display: inline-block;
width: 1000px;
margin: 0px 20px;
}
.editors-pick-elements {
display: grid;
grid-template-columns: 1fr 1fr;
max-width: 1000px;
min-width: 300px;
border: 1px solid green;
height: 500px;
grid-gap: 20px;
margin: auto;
}
.pick1 {
background-color: #ccc;
}
.pick2 {
background-color: #ccc;
}
.pick3 {
background-color: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="editors-pick-slideshow">
<div id="editorsPickContainer" class="editors-pick-container">
<div>
<div id="group1" class="editors-pick-elements">
<div class="pick1">Grid 1</div>
<div class="pick2">Grid 1</div>
</div>
</div>
<div>
<div id="group2" class="editors-pick-elements">
<div class="pick1">Grid 2</div>
<div class="pick2">Grid 2</div>
</div>
</div>
<div>
<div id="group3" class="editors-pick-elements">
<div class="pick1">Grid 3</div>
<div class="pick2">Grid 3</div>
</div>
</div>
</div>
</div>

How do I set the background of a div onclick?

I'm trying to set the background of a div when it is clicked, and then when it is clicked again it reverses back to no color.
Here is my code:
<div class="wrapper">
<div onclick="set()" id="square"></div>
<div onclick="set()" id="square"></div>
<div onclick="set()" id="square"></div>
<div onclick="set()" id="square"></div>
<div onclick="set()" id="square"></div>
<div onclick="set()" id="square"></div>
<div onclick="set()" id="square"></div>
</div>
.wrapper {
display: grid;
grid-template-columns: repeat(107, 1fr);
grid-template-rows: repeat(59, 1fr);
grid-gap: 0px;
}
.wrapper div {
padding: 5px;
background-color: none;
text-align: center;
border: 0.001px solid black;
font-size: 5px;
display: flex;
justify-content: center;
align-items: center;
}
.wrapper div:hover {
background: white;
}
function set() {
var squares = document.getElementById('square');
for (var i = 0; i < squares.length; i++) {
squares[i].style.backgroundColor = "none";
}
if (squares[i].style.backgroundColor === "none") {
squares[i].style.backgroundColor = "white";
} else {
squares[i].style.backgroundColor = "none";
}
}
When I click the div it doesn't do anything. My javascript code isn't working, how should I change it so it makes the div background white when clicked and then none when clicked again.
You can put an event listener on the parent element that listens for the click event. When it gets a click event, if it is from one of the squares, toggle a class to keep the background color on it. If it already has that class, it will be removed.
document.querySelector('.squares').addEventListener('click', e => {
if (e.target.classList.contains('square')) {
e.target.classList.toggle('selected');
}
});
.wrapper {
display: grid;
grid-template-columns: repeat(107, 1fr);
grid-template-rows: repeat(59, 1fr);
grid-gap: 0px;
}
.wrapper div {
padding: 5px;
background-color: none;
text-align: center;
border: 0.001px solid black;
font-size: 24px;
display: flex;
justify-content: center;
align-items: center;
}
.wrapper .square:hover, .square.selected {
background: white;
}
body { background-color: #888; }
<div class="wrapper squares">
<div class="square">A</div>
<div class="square">B</div>
<div class="square">C</div>
<div class="square">D</div>
<div class="square">E</div>
<div class="square">F</div>
<div class="square">G</div>
</div>
Your primary issue is this:
<div onclick="set()" id="square"></div>
<div onclick="set()" id="square"></div>
<div onclick="set()" id="square"></div>
<div onclick="set()" id="square"></div>
<div onclick="set()" id="square"></div>
<div onclick="set()" id="square"></div>
<div onclick="set()" id="square"></div>
It's invalid to have the same id on more than one element. An id must be unique. So this line:
var squares = document.getElementById('square');
just selects the first div and then later, when you try to loop over all the cells, you don't because you never had all of the cells in the first place.
You just need to set one click handler on the parent of all the div cells. Then, in that handler, you can apply a CSS class to the div that triggered the event. This is called event delegation and works because events bubble up through the DOM.
// Get a reference to all the cells
let squares = document.querySelectorAll("div.wrapper > div");
// Set up you event handlers in JavaScipt, not with inline HTML
// event attributes like onclick
document.querySelector(".wrapper").addEventListener("click", function(event) {
// Remove all the background colors from all the cells
squares.forEach(function(square){
square.classList.remove("activeCell");
});
// Apply backgroun to clicked cell
event.target.classList.add("activeCell");
});
.wrapper {
display: grid;
grid-template-columns: repeat(107, 1fr);
grid-template-rows: repeat(59, 1fr);
grid-gap: 0px;
}
.wrapper > div {
padding: 5px;
background-color: none;
text-align: center;
border: 0.001px solid black;
font-size: 5px;
display: flex;
justify-content: center;
align-items: center;
}
.wrapper div:hover {
background: green;
}
.activeCell {
background-color:blue;
}
<div class="wrapper">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>

Make CSS grid or flex item equal size with stretched separator between

I try to build a dynamic toolbar where:
The number of tools is dynamic
All tools should have the same width (based on the widest one)
Tools can be separated by a separator that takes all the space available (stretched)
The separator can be placed anywhere
The html can't be changed
Expected output (given BBB the widest tool):
—————————————————————————————————————————————————————
| A |BBB| CC| SEPARATOR | D |
—————————————————————————————————————————————————————
Flex
I tried with the flex method, I can't combine all the rules:
Either the separator takes all the space but tools width are not equal:
—————————————————————————————————————————————————————
|A|BBB|CC| SEPARATOR |D|
—————————————————————————————————————————————————————
nav {
display: flex;
background: #e8e8e8;
width: 100%;
}
.item {
flex: 1;
text-align: center;
}
.separator {
width: 100%;
background: #d3d3d3;
}
<nav>
<div class="item">A</div>
<div class="item">BBB</div>
<div class="item">CC</div>
<div class="separator"></div>
<div class="item">D</div>
</nav>
Either all tools (including the separator) have the same width:
—————————————————————————————————————————————————————
| A | BBB | CC | SEPARATOR | D |
—————————————————————————————————————————————————————
nav {
display: flex;
background: #e8e8e8;
width: 100%;
}
.item {
flex: 1;
text-align: center;
}
.separator {
flex: 1;
background: #d3d3d3;
}
<nav>
<div class="item">A</div>
<div class="item">BBB</div>
<div class="item">CC</div>
<div class="separator"></div>
<div class="item">D</div>
</nav>
Grid
With the grid system, I can't get the separator without specifying a grid-template-columns, which I want to avoid. I need something dynamic.
nav {
display: grid;
grid-auto-columns: minmax(0, 1fr);
grid-auto-flow: column;
background: #e8e8e8;
width: 100%;
}
.item {
text-align: center;
}
.separator {
justify-self: stretch;
background: #d3d3d3;
}
<nav>
<div class="item">A</div>
<div class="item">BBB</div>
<div class="item">CC</div>
<div class="separator"></div>
<div class="item">D</div>
</nav>
I'm open to JavaScript solutions if there is no CSS solution. Thank you for your help.
via javascript , you can loop through .item and look for the widest , then update a custom css property.
possible example via js and flex
var bigW = "0";
for (let e of document.querySelectorAll("nav .item")) {
elW = e.offsetWidth;
if (elW > bigW) {
bigW = elW;
}
document.documentElement.style.setProperty("--myW", bigW + 1 + "px");
}
nav {
display: flex;
background: #e8e8e8;
width: 100%;
}
.item {
min-width: var(--myW, 3em);
text-align: center;
}
.separator {
flex: 1;
background: #d3d3d3;
}
nav div+div {
border-left: solid;
}
<nav>
<div class="item">A</div>
<div class="item">BBB</div>
<div class="item">CC</div>
<div class="separator"></div>
<div class="item">D</div>
</nav>
edit from a comment below
var bigW = "0";
for (let e of document.querySelectorAll("nav > div")) {
elW = e.offsetWidth;
if (elW < 7) {// includes partially possible border and padding, mind it
e.style.flexGrow = 1;
} else if (elW > bigW) {
bigW = elW;
}
}
document.documentElement.style.setProperty("--myW", bigW + 1 + "px");
nav {
display: flex;
background: #e8e8e8;
}
.item {
min-width: var(--myW, 0);
text-align: center;
border: solid 1px;
}
.separator {
background: #d3d3d3;
}
<nav>
<div class="item">A</div>
<div class="item">BBBBBBBB</div>
<!--<div class="separator"></div>-->
<div class="item">CC</div>
<div class="separator"></div>
<div class="item">D</div>
</nav>

Categories