When using CSS Grid Layout to dynamically arrange elements in rows the browser calculates how many items go in a row depending on the size of the viewport. I'm looking for a way using JavaScript to determine how many items are in each row at a given viewport size. My purpose is to fill the inevitable part empty row at the end.
For example, given the CSS and HTML listed below, and at a particular viewport size results in this layout:
And at a wider viewport results in this layout:
Each have missing elements in the last row.
Having the maximum number of elements a row I can dynamically load more elements to fill in the last row so it's the same number as the rest.
Example CSS and HTML:
<style>
* {
box-sizing: border-box;
}
body {
padding: 1rem;
}
main {
max-width: 500px;
margin: 0 auto;
}
article {
margin: 1rem 0;
overflow: hidden;
}
main {
max-width: 10000px;
margin: 0;
}
article {
margin: 0;
}
.listings {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-gap: 1rem;
}
.listings {
font-family: Avenir, Roboto, Helvetica, san-serif;
font-size: 80%;
}
.listing {
display: flex;
flex-flow: column;
border: 1px solid silver;
}
.listing > h1 {
margin: 1rem 1rem 0;
}
.listing > div {
margin: 0.25em 1rem 1rem;
}
.listing > img {
width: auto;
max-height: 200px;
order: -1;
align-self: center;
}
</style>
<main class="listings">
<article class="listing">
<img src="images/computer.jpg">
<h1>87,500</h1>
<div>1008[3/2]0</div>
</article>
<article class="listing">
<img src="images/computer.jpg">
<h1>30,000</h1>
<div>952[2/1]0</div>
</article>
<article class="listing">
<img src="images/computer.jpg">
<h1>70,000</h1>
<div>1090[3/1]0</div>
</article>
<article class="listing">
<img src="images/computer.jpg">
<h1>11,000</h1>
<div>828[2/1]2</div>
</article>
<article class="listing">
<img src="images/computer.jpg">
<h1>25,000</h1>
<div>1484[2/1]0</div>
</article>
<article class="listing">
<img src="images/computer.jpg">
<h1>199,000</h1>
<div>2160[3/2]0</div>
</article>
<article class="listing">
<img src="images/computer.jpg">
<h1>42,000</h1>
<div>1509[3/2]0</div>
</article>
<article class="listing">
<img src="images/computer.jpg">
<h1>230,000</h1>
<div>1885[3/2]0</div>
</article>
</main>
elem.getBoundingClientRect().left
The elem.getBoundingClientRect().left
property can be used to get the number of dynamic rows in a CSS grid layout. Just loop through the article
elements NodeList and compare the element's getBoundingClientRect().left
value to the previous. The compared value will increase within each row then drop when starting in the next row. Count the increments until the first drop occurs to determine the number of elements in each row.
var articles = document.querySelectorAll('article')
console.log(articles[0].getBoundingClientRect().left) // 16
console.log(articles[1].getBoundingClientRect().left) // 238.156
console.log(articles[2].getBoundingClientRect().left) // 460.312
console.log(articles[3].getBoundingClientRect().left) // 682.468
console.log(articles[4].getBoundingClientRect().left) // 904.625
console.log(articles[5].getBoundingClientRect().left) // 1126.78
console.log(articles[6].getBoundingClientRect().left) // 16
Therefore we just need to count the increases in value between the previous left
value and the next and once it drops we have our row count.
Here using call
to apply array method reduce
on the NodeList. Using reduce
to carry forward the each value to compare to the next value:
var rowlen = Array.prototype.reduce.call(document.querySelectorAll('article'), function (prev, next) {
if ( !prev[2] ) {
var ret = next.getBoundingClientRect().left
// if increasing, increment counter
if ( !(prev[0] > -1 && ret < prev[1]) ) { prev[0]++ }
else { prev[2] = 1 } // else stop counting
}
return [prev[0],ret, prev[2]] // [counter, elem, stop-counting]
}, [0,null,0])[0]
Codesandbox.io "complete partial row" example.
Or old-school JS, sporting a for
loop nested in an IIFE:
var rowlen = (function () {
for ( var ii = 0, arts = document.querySelectorAll('article'),
len = arts.length, el = 0, ell = 0;
ii < len; ii++ ) {
el = arts[ii].getBoundingClientRect().left
if ( el > ell || 0 === ell ) { ell = el }
else { return ii; }
}
}())