最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Count how many elements in dynamic row - Stack Overflow

programmeradmin1浏览0评论

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/puter.jpg">
        <h1>87,500</h1>
        <div>1008[3/2]0</div>
    </article>

    <article class="listing">
        <img src="images/puter.jpg">
        <h1>30,000</h1>
        <div>952[2/1]0</div>
    </article>

    <article class="listing">
        <img src="images/puter.jpg">
        <h1>70,000</h1>
        <div>1090[3/1]0</div>
    </article>

    <article class="listing">
        <img src="images/puter.jpg">
        <h1>11,000</h1>
        <div>828[2/1]2</div>
    </article>

    <article class="listing">
        <img src="images/puter.jpg">
        <h1>25,000</h1>
        <div>1484[2/1]0</div>
    </article>

    <article class="listing">
        <img src="images/puter.jpg">
        <h1>199,000</h1>
        <div>2160[3/2]0</div>
    </article>

    <article class="listing">
        <img src="images/puter.jpg">
        <h1>42,000</h1>
        <div>1509[3/2]0</div>
    </article>

    <article class="listing">
        <img src="images/puter.jpg">
        <h1>230,000</h1>
        <div>1885[3/2]0</div>
    </article>
</main>

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/puter.jpg">
        <h1>87,500</h1>
        <div>1008[3/2]0</div>
    </article>

    <article class="listing">
        <img src="images/puter.jpg">
        <h1>30,000</h1>
        <div>952[2/1]0</div>
    </article>

    <article class="listing">
        <img src="images/puter.jpg">
        <h1>70,000</h1>
        <div>1090[3/1]0</div>
    </article>

    <article class="listing">
        <img src="images/puter.jpg">
        <h1>11,000</h1>
        <div>828[2/1]2</div>
    </article>

    <article class="listing">
        <img src="images/puter.jpg">
        <h1>25,000</h1>
        <div>1484[2/1]0</div>
    </article>

    <article class="listing">
        <img src="images/puter.jpg">
        <h1>199,000</h1>
        <div>2160[3/2]0</div>
    </article>

    <article class="listing">
        <img src="images/puter.jpg">
        <h1>42,000</h1>
        <div>1509[3/2]0</div>
    </article>

    <article class="listing">
        <img src="images/puter.jpg">
        <h1>230,000</h1>
        <div>1885[3/2]0</div>
    </article>
</main>

Share Improve this question edited Nov 16, 2023 at 17:59 bloodyKnuckles asked Apr 17, 2018 at 21:51 bloodyKnucklesbloodyKnuckles 12.1k4 gold badges31 silver badges38 bronze badges 4
  • possible duplicate of : stackoverflow./questions/49043684/… – Temani Afif Commented Apr 17, 2018 at 21:52
  • and what about when browser resize ? – Temani Afif Commented Apr 17, 2018 at 21:53
  • @TemaniAfif There is a lot of plexity to the question you linked to, and therefore the answers. The use case I have here is an evenly aligned, minimally interactive grid of elements, somewhat closely spaced, and not expecting the user to resize the browser—which I think covers many use cases. And in the case the user does resize the browser it's no worse than it was without this example. – bloodyKnuckles Commented Apr 17, 2018 at 22:57
  • 1 i know that's why it's a possible duplicate ;) i didn't close as duplicate ... but it's almost the same thing, so both question should be linked – Temani Afif Commented Apr 17, 2018 at 23:05
Add a ment  | 

2 Answers 2

Reset to default 7

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 pare the element's getBoundingClientRect().left value to the previous. The pared 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 pare 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 "plete 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; }
  }
}())

Codesandbox.io "drop partial row" example.

elem.clientWidth

This example uses the element property clientWidth to calculate the number of elements in each row.

var rowlength = Math.floor( // round down to account for some spacing
  document.body.clientWidth / // width of viewport divided by...
  document.querySelectorAll('article')[0].clientWidth // width of an element
)

This gives me the number of items in each row, except possibly the last row.


Complete Last Row

I know I've loaded 8 elements initially, so I can now calculate how many items I need to plete the last row:

var numelementsloaded = 8

Alternately I can count the number of loaded elements:

var numelementsloaded = document.querySelectorAll('article').length

The modulus (division remainder) operator will give me the number of elements in the last row:

var numelementslastrow = elementsloaded % rowlength

Now calculate the number of elements needed to plete last row:

var numelementsneeded = rowlength - numelementslastrow

All together and pressed, with a bonus:

var rowlength = Math.floor(document.body.clientWidth/document.querySelectorAll('article')[0].clientWidth)
var numelementsneeded = rowlength - (document.querySelectorAll('article').length % rowlength)

// and duplicating the first few elements to illustrate pleting the last row
for ( var ii = 0; ii < numelementsneeded; ii++ ) {
  document.querySelector('main').appendChild(
    // here is where I'd pull in new items from the server
    document.querySelectorAll('article')[ii].cloneNode(true)
  )
}

And that pletes the last row:

Codesandbox.io "plete last row" example.


Drop Last Row

Another, technically simpler, option is to—rather than add new items—drop all the items in the last, partial row:

var rowlength = Math.floor(document.body.clientWidth/document.querySelectorAll('article')[0].clientWidth)
var numelementslastrow = document.querySelectorAll('article').length % rowlength

// and dropping the elements in the last row
for ( var ii = 0; ii < numelementslastrow; ii++ ) {
  document.querySelector('main').removeChild(
    document.querySelector('article:last-child')
  )
}

If the last row is full, it stays. For example, if there are 8 items and each row has 4 items, 8 mod 4 = 0, therefore the for loop does not execute.

Codesandbox.io "drop partial row" example.

发布评论

评论列表(0)

  1. 暂无评论