Can a grid item adjust its span based on height? - javascript

Is there any way to get a behaviour similar to this shown on drawing?
I mean of course manually we can specify grid-row: span x, but grid-row: span auto seems doesn't work. I need all grid items to be the same size, but when one item has to resize (due to text overflow) i need to set the grid-row: span 2, and when it's getting bigger - respectively higher number.
To acheive something like this I need to write .js or can it be done with css only?
Here is code sandbox

let items = document.querySelectorAll('.item')
items.forEach(item => {
if(item.scrollHeight>item.clientHeight){
let itemSpan = Math.round(item.scrollHeight/40) + 1 // (height = 30) + (gap = 10) 40 =>
item.style.cssText = `--n : ${itemSpan}`
}
})
.container {
width: 200px;
margin: 15vw auto;
}
.grid {
width: 100%;
background-color: chartreuse;
display: grid;
justify-items: center;
grid-template-columns: 1fr 1fr;
grid-auto-rows: 30px;
grid-auto-flow: row;
grid-gap: 10px;
word-break: break-all;
}
.item {
background-color: gold;
min-height: 30px;
width: 50px;
padding: 5px;
grid-row: auto / span var(--n); /* var(--n) is calculated with js */
box-sizing: border-box;
}
<div class="container">
<div class="grid">
<div class="item">1</div>
<div class="item ">2 Lorem ipsum dolor sit amet consectetur adipisicing </div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5 t amet consectetur adipisi</div>
<div class="item">6 lorem</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9 t amet consectetur adipisi </div>
</div>
</div>

Sometimes good to review the fundamentals:
HTML is for structure.
CSS is for presentation.
JavaScript is for behavior.
Your problem falls within 3 ("item has to resize (due to text overflow)")

You can add min-height to ".item" class to set same size of grid items.
.item{
min-height:100px;
}
.App {
font-family: sans-serif;
text-align: center;
}
.container {
width: 200px;
height: 200px;
}
.grid {
width: 100%;
background-color: chartreuse;
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 10px;
grid-auto-flow: dense;
}
.item {
background-color: brown;
height: 30px;
min-height:100px;
}
.large {
height: fit-content;
word-break: break-all;
grid-row: span auto;
}
<div class="App">
<div class="container">
<div class="grid">
<div class="item"> </div>
<div class="item large">
asdasdasdasdasdsadasdasdasdasdasdasd{" "}
</div>
<div class="item"> </div>
<div class="item"> </div>
<div class="item"> </div>
<div class="item"> </div>
</div>
</div>
</div>

Related

What is a way to fit content without changing the height of the container and not have overflow hidden

const Sheet = [
{
"Code": "A-0-1",
"UPC": "4009803054728",
"Title": "U.S.S",
"Price": "$34",
"InStock": "7"
}
]
const productsEl = document.querySelector(".Sheet");
function getProducts() {
Sheet.forEach((product) => {
productsEl.innerHTML += `<div class="productContainer">
<div class="img">
<img src=${product.imgSrc} alt="" height="170px;" width="170px">
</div>
<div class="itemdesc">
<h2 class="itemName">${product.Title}</h2>
<h4 class="price"><span>${product.Price}</span></h4>
<div class="desc">
<p>${product.Code}</p>
</div>
<div class="stock">
<p> Available ${product.InStock} </p>
</div>
</div>`;
})
}
getProducts()
.Sheet {
margin-top: 100px;
margin-left: 50px;
display: grid;
grid-template-columns: repeat(5, 250px);
grid-template-rows: minmax(200px, max-content) repeat(auto-fill, 190px);
row-gap: 80px;
}
.productContainer {
display: block;
margin: 0 auto;
border: 1px solid lightgrey;
width: 200px;
max-height: 230px;
border-radius: 5px;
padding: 5px;
height: fit-content;
}
<div class="Sheet">
</div>
I have thousands of those Objects with different amount of wording. Obviously longer titles take more space therefore the text comes out of the container or the container gets bigger. How do I keep the container the same size and not have overflow? I need to wrap in the container and keeps its size so the page looks consistent.
if you really wanna keep the container the same size, then you maybe should use overflow: hidden; or overflow: scroll on your cantainer. but you can also play with your image size to give the content more space.

Uneven CSS Column Grids

From my research in this problem, I've learned CSS grids appear to capable of quite complex behaviour. My scenario is simpler and I was expecting to have 4 evenly spaced panel boxes with code below. My question is why the horizontal gap is not centred in the middle vertically? I worked through trail&error to test various combinations of alignment, justification and grid specs but could not find a solution. What do I need to add to my code to have the gap centred vertically on the page?
.main-container {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-gap: 20px;
height: 100vh;
align-items: stretch;
margin: 0;
padding: 0;
justify-content: space-between;
}
.container {
border: 3px solid black;
display: flex;
position: relative;
flex-direction: column;
margin: 0;
padding: 0;
}
.shared {
justify-content: center;
border: none;
}
.sub-container {
border: 3px solid black;
align-items: stretch;
height: 100%;
margin: 0;
padding: 0;
}
<div class="main-container">
<div class="container" id="A">
<h1>Section A</h1>
<p id="A-values">A Values</p>
</div>
<div class="container" id="B">
<h1>Section B</h1>
<p id="B-values">B Values</p>
</div>
<div class="container" id="C">
<h1>Section C</h1>
<p id="C-values">C Values</p>
</div>
<div class="container shared" id="D">
<div class="sub-container">
<h2>SubSection D1</h2>
<p id="D1-values">D1 Values</p>
</div>
<div class="sub-container">
<h2>SubSection D2</h2>
<p id="D2-values">D2 Values</p>
</div>
<div class="sub-container">
<h2>SubSection D3</h2>
<p id="D3-values">D3 Values</p>
</div>
</div>
</div>
By default, the height is set to match the longest item on a row, the height on the bottom row is different to the top simply because there's more content in one of it's columns.
Try setting the rows as you have the columns, add grid-template-rows: repeat(2, 1fr); to the main-container class

How to Let Float Items Split the Blank Area in Average?

Please run this demo:
.app {
background:pink;
width: 90vw;
overflow: hidden;
}
.app__item {
background: lightblue;
width: 200px;
float :left;
margin:5px;
height: 40px;
}
.app__item:last-child {
float: right;
}
.app__item--size2 {
height: 90px;
}
<div class="app">
<div class="app__item app__item--size2">1</div>
<div class="app__item">2</div>
<div class="app__item">3</div>
<div class="app__item">4</div>
<div class="app__item">5</div>
<div class="app__item">6</div>
<div class="app__item">7</div>
<div class="app__item">8</div>
<div class="app__item">9</div>
<div class="app__item">10</div>
<div class="app__item">
<button>search</button>
</div>
</div>
Please notice the red color text which is what I want:
How to Let Float Items Split the Blank Area in Average?
This gif point out what I want and also the reason why I am using float layout.
If using flex, the result would be
.app {
background:pink;
width: 80vw;
display: flex;
flex-flow: row wrap;
}
.app__item,
.app__item_wrapper {
margin:5px;
width: 200px;
flex: 0 0 auto;
}
.app__item {
background: lightblue;
height: 40px;
}
.app__item_wrapper {
display: flex;
flex-flow: column;
justify-content: space-between;
}
.app__item--size2 {
height: 90px;
}
.app__item--inner {
margin:0;
flex: 0 0 auto;
}
<div class="app">
<div class="app__item app__item--size2">1</div>
<div class=" app__item_wrapper app__item--size2">
<div class="app__item app__item--inner">2</div>
<div class="app__item app__item--inner">3</div>
</div>
<div class=" app__item_wrapper app__item--size2">
<div class="app__item app__item--inner">4</div>
<div class="app__item app__item--inner">5</div>
</div>
<div class="app__item">6</div>
<div class="app__item">7</div>
<div class="app__item">8</div>
<div class="app__item">
<button>search</button>
</div>
</div>
And the con is that it won't work well when viewport is changing. See this:
and this is the con:
If I understand correctly, you're wanting the cells to the right of "cell 1" to stretch-to-fill the reaming horizontal space of the enclosing div (with pink background).
You could use CSS Grid to achieve this as shown below:
.app {
background:pink;
width: 90vw;
padding:5px;
display:grid;
/* Cause first column with to be 200px, remaining two columns
to scale to fit remaining width */
grid-template-columns: 200px repeat(2, 1fr);
/* Set spacing between cells */
grid-gap:10px;
}
.app__item {
background: lightblue;
width: 100%;
height: 40px;
}
.app__item--size2 {
/* Cause top left cell to occupy two rows */
grid-row: 1 / 3;
height:100%;
}
<div class="app">
<div class="app__item app__item--size2">1</div>
<div class="app__item">2</div>
<div class="app__item">3</div>
<div class="app__item">4</div>
<div class="app__item">5</div>
<div class="app__item">6</div>
<div class="app__item">7</div>
<div class="app__item">8</div>
<div class="app__item">9</div>
<div class="app__item">10</div>
<div class="app__item">
<button>search</button>
</div>
</div>
Thank #Dacre Denny for giving me inspiration. Finally, I got it.
.app {
background:pink;
width: 90vw;
padding:5px;
display:grid;
grid-template-columns: repeat(auto-fit,200px);
justify-content:space-evenly;
grid-gap: 5px;
}
.app__item {
background: lightblue;
width: 200px;
height: 40px;
}
.app__item--size2 {
/* Cause top left cell to occupy two rows */
grid-row: 1 / 3;
height:100%;
}
.app__item:last-child {
grid-column-end: -1
}
<div class="app">
<div class="app__item app__item--size2">1</div>
<div class="app__item">2</div>
<div class="app__item">3</div>
<div class="app__item">4</div>
<div class="app__item">5</div>
<div class="app__item">6</div>
<div class="app__item">7</div>
<div class="app__item">8</div>
<div class="app__item">9</div>
<div class="app__item">10</div>
<div class="app__item">
<button>search</button>
</div>
</div>
See this gif when viewport is changing.

Creating a Responsive Grid Structure with Handlebars.js

I'm working with handlebars.js and trying to create a responsive grid structure - however I can't seam to figure out how to create the structure with the handlebars!
I cant work out how to organise the divs and script so the results show in the separate 'panels'! Currently they just show in one long Column on top of each other.
I can get it to work with normal divs but not when I add the handlebars and script in.
I think there is a simple change - but I cant work out what it is!
How do I get the JSON data retired to show in each separate panel?
Thanks in advance - I've spent hours trying to figure this out!
This is the closest I have got:
CSS:
.wrapper {
margin: 0 auto;
width: 100%
}
.wrapper > * {
background-color: #fafafa;
}
.content {
padding: 8px;
display: grid;
margin: 0 auto;
grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)) ;
grid-auto-rows: minmax(264px, auto);
grid-gap: 16px;
}
.panel {
margin-left: 5px;
margin-right: 5px;
height: 50px;
}
#media (max-width: 1100px) {
.wrapper {
grid-template-columns: 1fr;
}
.content {
width: 100%;
grid-template-columns: repeat(auto-fill, minmax(360px, 1fr) ) ;
grid-auto-rows: minmax(300px, auto);
}
}
#supports (display: grid) {
.wrapper > * {
width: auto;
margin: 0;
}
}
HTML
<div id="expand-box">
<div class="wrapper">
<article class="content">
<div class="panel">
<script id="entry-template" type="text/x-handlebars-template">
{{#each this}}
<div class="column">
<div id="est_title">{{name}} <b>{{rating}}/5</b></div>
<div id="know_subtitle">Known For: <b>{{known_for}}</b></div>
<div id="price_subtitle">{{price_range}}</div>
</div>
{{/each}}
</script>
</div>
</div>
</article>
</div>
JS AJAX
var source = $("#entry-template").html();
var template = Handlebars.compile(source);
var html = template(data);
$('.panel').html(html);
Figured it out after many more hours... Surprised there isn't more detail on this on SO. Maybe it will save someone else a few hours!
CSS
.wrapper {
margin: 0 auto;
width: 100%
}
#handlebars-sandbox {
padding: 8px;
display: grid;
margin: 0 auto;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)) ;
grid-auto-rows: minmax(264px, auto);
grid-gap: 20px;
margin-top: 20px
}
.panel {
margin-left: 5px;
margin-right: 5px;
}
#media (max-width: 1100px) {
.wrapper {
grid-template-columns: 1fr;
}
#handlebars-sandbox {
width: 100%;
grid-template-columns: repeat(auto-fill, minmax(360px, 1fr) ) ;
grid-auto-rows: minmax(300px, auto);
}
}
#supports (display: grid) {
.wrapper > * {
width: auto;
margin: 0;
}
}
JQUERY
var source = $('.entry-template').html();
var template = Handlebars.compile(source);
var html = template(data);
$('#handlebars-sandbox').html(html);
HTML
<div id="expand-box">
<div class="wrapper">
<div id="handlebars-sandbox"></div>
<div class="content">
<script id="entry-template" type="text/x-handlebars-template">
{{#each this}}
<div class="panel">
<div class="column">
<div id="est_title">{{name}} <b>{{rating}}/5</b></div>
<div id="know_subtitle">Known For: <b>{{known_for}}</b></div>
<div id="price_subtitle">{{price_range}}</div>
</div>
</div>
{{/each}}
</script>
</div>
</div>
</div>

How to create a layout of 4 columns that one column has dynamic width and the other 3 are even?

I'm not sure what's the best way to solve this.. I need layout of 4 columns, the first column has unknown (dynamic) width, the other 3 columns should fill the remaining space evenly. The tricky part is that all columns must be direct siblings of each other.
Preferably pure css solution
Flexbox can do that...
.parent {
display: flex;
margin-bottom: 1em;
}
.big {
height: 100px;
border: 1px solid grey;
background: lightblue;
flex: 1 0 auto;
}
p {
white-space: nowrap;
;
}
.box {
flex: 0 1 33%;
border: 1px solid grey;
}
<div class="parent">
<div class="big">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
</div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
<div class="parent">
<div class="big">
<p>Lorem ipsum dolor sit amet</p>
</div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
You could also do this with JS
$(window).on("resize", function() {
var big = $('.big').outerWidth();
var rest = ($(window).width() - big) / 3;
$('.other').outerWidth(rest);
}).resize();
* {
box-sizing: border-box;
}
body,
html {
margin: 0;
padding: 0;
}
.big {
white-space: nowrap;
background: lightgreen;
}
.column {
border: 1px solid black;
padding: 15px;
float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="content">
<div class="column big">Lorem ipsum dolor sit amet, consectetur adipisicing.</div>
<div class="column other">Lorem</div>
<div class="column other">Lorem</div>
<div class="column other">Lorem</div>
</div>
You can do it using javascript, see fiddle: https://jsfiddle.net/hzkwxo3q/1/
HTML
<div id="col1" class="col1">
<h1>
Dynamic
</h1>
</div>
<div id="col2" class="col2">
<h1>
33%
</h1>
</div>
<div id="col3" class="col3">
<h1>
33%
</h1>
</div>
<div id="col4" class="col4">
<h1>
33%
</h1>
</div>
CSS
body {
margin: 0px;
}
.col1,
.col2,
.col3,
.col4 {
min-width: 20%;
height: 400px;
float: left;
}
.col1 {
background-color: lightgreen;
}
.col2 {
background-color: lightblue;
}
.col3 {
background-color: lightgrey;
}
.col4 {
background-color: lightyellow;
}
Javascript
var width = document.getElementById('col1').clientWidth;
var w = window.innerWidth;
var extraSpace = (w - width) * 0.333;
document.getElementById("col2").style.width = extraSpace + "px";
document.getElementById("col3").style.width = extraSpace + "px";
document.getElementById("col4").style.width = extraSpace + "px";

Categories

Resources