计算动态行中的元素数量

时间:2018-04-17 21:51:24

标签: javascript css grid-layout

使用CSS Grid Layout动态排列行中的元素时,浏览器会根据视口的大小计算连续的项目数。我正在寻找一种方法,使用JavaScript来确定给定视口大小的每一行中有多少项。我的目的是在最后填补不可避免的空行。

例如,给定下面列出的CSS和HTML,并且在特定视口大小时会产生以下布局:

CSS Grid Layout Narrow, Dynamic Rows, Part Empty Last Row

在更宽的视口处产生这种布局:

CSS Grid Layout Wide, Dynamic Rows, Part Empty Last Row

每一个都在最后一行中缺少元素。

我正在寻找一种方法来确定一行中有多少元素。有了这些信息,我就可以动态加载更多的元素来填充最后一行,所以它与其余的相同。

CSS和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>
&#13;
<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>
&#13;
&#13;
&#13;

2 个答案:

答案 0 :(得分:3)

<强> elem.getBoundingClientRect().left

elem.getBoundingClientRect().left属性可用于获取CSS网格布局中的动态行数。只需遍历article元素NodeList,然后将元素的getBoundingClientRect().left值与之前的值进行比较。比较值将在每行内增加,然后在下一行开始时下降。计算增量直到第一次下降,以确定每行中的元素数。

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

因此,我们只需计算上一个left值与下一个值之间的值增加值,一旦下降,我们就会计算行数。

这里使用call在NodeList上应用数组方法reduce。使用reduce结转每个值以与下一个值进行比较:

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.

或者是老派的JS,在IIFE中嵌套for循环:

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.

答案 1 :(得分:0)

<强> elem.clientWidth

此示例使用元素属性clientWidth来计算每行中的元素数。

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
)

这给了我每行中的项目数,除了可能是最后一行。

完成最后一行

我知道我最初加载了8个元素,因此我现在可以计算完成最后一行所需的项目数量:

var numelementsloaded = 8

或者我可以计算加载元素的数量:

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

modulus(除法余数)运算符会给出最后一行中元素的数量:

var numelementslastrow = elementsloaded % rowlength

现在计算完成最后一行所需的元素数量:

var numelementsneeded = rowlength - numelementslastrow

全部一起压缩,并带有奖励:

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 completing 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)
  )
}

这就完成了最后一行:

Codesandbox.io "complete last row" example.

删除最后一行

另一个技术上更简单的选择是 - 而不是添加新项目 - 删除最后一个部分行中的所有项目:

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')
  )
}

如果最后一行已满,则会保留。例如,如果有8个项目,每行有4个项目,则8 mod 4 = 0,因此for循环不会执行。

Codesandbox.io "drop partial row" example.