如何计算连续的弹性盒项目数量? [英] How to calculate the amount of flexbox items in a row?

查看:109
本文介绍了如何计算连续的弹性盒项目数量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一个网格是使用CSS flexbox实现的。 中调整输出窗口的大小,以获得一种感觉。



总是有一个活动项目,标有黑色边框。使用JavaScript,我允许用户浏览使用左/右箭头到上一个/下一个项目。在我的实现中,我只是将活动项目的索引减少/增加1。



现在,我想允许用户向上/向下导航。为此,我只需要通过<一行中的项目数量> 来减少/增加活动项目的索引。但是,我怎么计算这个数字,因为它取决于容器的宽度?有没有更好的方法来实现上/下功能?



.grid {display:flex; flex-wrap:wrap; align-content:flex-start; width:250px; height:200px; background-color:#ddd; padding:10px 0 0 10px;}。item {width:50px; height:50px;背景颜色:红色; margin:0 10px 10px 0;}。active.item {outline:5px solid black;}

 < div id =gridclass =grid> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item active>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div>< / div>  

解决方案

(为获得最佳体验,请在整页上运行交互式片段)


$ b

计算每行元素数量



您需要使用它的边距 (如果它们被设置,最终也是边框),那么你需要获得容器的内部宽度而不用填充。有了这两个值,你可以进行简单的划分,以获得每行元素的数量。



不要忘记考虑你只有一行的情况,所以你需要获得元素总数和从分部获得的数量之间的最小值。



 //元素总数n_t = document.querySelectorAll .offsetWidth); //带marginvar的元素的完整宽度m = document.querySelector('。item')。currentStyle || window.getComputedStyle(document.querySelector('。item')); w = w + parseInt(m.marginLeft)+ parseInt(m.marginRight); //容器宽度w_c = parseInt(document.querySelector('。grid' ).offsetWidth); //填充containervar c = document.querySelector('。grid')。currentStyle || window.getComputedStyle(document.querySelector('。grid')); var p_c = parseInt(c.paddingLeft)+ parseInt(c.paddingRight); // nb每行元素的元素nb = Math.min(parseInt((w_c  -  p_c )/ w),n_t); console.log(nb); window.addEventListener('resize',function(event){//只有容器的宽度会改变w_c = parseInt(document.querySelector('。grid') .offsetWidth); nb = Math.min(parseInt((w_c-p_c)/ w),n_t); console.log(nb);});  

  .grid {display:flex; flex-wrap:wrap;调整:水平; align-content:flex-start; background-color:#ddd; padding:10px 0 0 10px;}。item {width:80px; height:80px;背景颜色:红色; margin:0 10px 10px 0;}。active.item {outline:5px solid black;}  

 < div id =gridclass =grid> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item active>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div>< / div>  

这是一个jQuery版本的相同的逻辑代码少:

 

.grid {display:flex; flex-wrap:wrap;调整:水平; align-content:flex-start; background-color:#ddd; padding:10px 0 0 10px;}。item {width:80px; height:80px;背景颜色:红色; margin:0 10px 10px 0;}。active.item {outline:5px solid black;}

 < script src =https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js>< / script>< div id =gridclass =grid> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item active>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div>< / div>  





以下是交互式网格的演示: data-lang =jsdata-hide =truedata-console =truedata-babel =false>

  var all = document.querySelectorAll('。item'); var n_t = all.length; var current = 0; all [current] .classList。 add('active'); var w = parseInt(document.querySelector('。item')。offsetWidth); var m = document.querySelector('。item')。currentStyle || window.getComputedStyle(document.querySelector('。item')); w = w + parseInt(m.marginLeft)+ parseInt(m.marginRight); var w_c = parseInt(document.querySelector('。grid')。offsetWidth) ; var c = document.querySelector('。grid')。currentStyle || window.getComputedStyle(document.querySelector('。grid')); var p_c = parseInt(c.paddingLeft)+ parseInt(c.paddingRight); var nb = Math.min(parseInt((w_c  -  p_c)/ w), n_t); window.addEventListener('resize',function(e){w_c = parseInt(document.querySelector('。grid')。offsetWidth); nb = Math.min(parseInt((w_c-p_c)/ w), document.addEventListener('keydown',function(e){e = e || window.event; if(e.keyCode =='38'){if(current-nb> = 0){ all [current] .classList.remove('active'); current- = nb; all [current] .classList.add('active');}} else if(e.keyCode =='40'){if(当前+ nb  

.grid {display:flex; flex-wrap:wrap;调整:水平; align-content:flex-start; background-color:#ddd; padding:10px 0 0 10px;}。item {width:80px; height:80px;背景颜色:红色; margin:0 10px 10px 0;}。active.item {outline:5px solid black;}

 < div id =gridclass =grid> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div>< / div>  





另一个想法



我们也可以考虑另一种导航在网格内而不需要每行元素的数量。这个想法是依赖于函数 elementFromPoint (x,y)



逻辑如下:我们在一个活动元素中,我们有它的(x,y)位置。通过按一个键我们将增加/减少这些值,我们使用上面的函数来使用新的(x,y)来获取新元素。我们测试一下是否有一个有效的元素,如果这个元素是一个元素(包含 item class)。在这种情况下,我们从前一个中删除活动,并将其添加到新的活动中。



这里是一个例子,我只考虑里面导航。当我们到达容器的左/右边界时,我们将不会到达上一行/下一行:

  var a = document.querySelector('。item'); a.classList.add('active'); var off = a.getBoundingClientRect(); / *我得到中心位置避免任何潜在的边界问题* / var y = off.top + 40; var x = off.left + 40; document.addEventListener('keydown',function(e){e = e || window.event; if(e.keyCode =='38'){var elem = document.elementFromPoint x,y  -  90 / * width + both margin * /); if(elem& elem.classList.contains('item')){document.querySelector('.active')。classList.remove('active '); elem.classList.add('active'); y  -  = 90;}} else if(e.keyCode =='40'){var elem = document.elementFromPoint(x,y + 90); if elem.classList.contains('item')){document.querySelector('.active')。classList.remove('active'); elem.classList.add('active'); y + = 90;}} else if(e.keyCode =='37'){var elem = document.elementFromPoint(x-90,y); if(elem& elem.classList.contains('item')){ document.querySelector('.active')。classList.remove('active'); elem.classList.add('active'); x  -  = 90;}} else if(e。 keyCode =='39'){var elem = document.elementFromPoint(x + 90,y); if(elem& elem.classList.contains('item')){document.querySelector('.active')。classList.remove('active'); elem.classList.add(激活); x + = 90; }}}); window.addEventListener('resize',function(e){var off = document.querySelector('.active')。getBoundingClientRect(); y = off.top + 40; x = off.left + 40 ;});  

.grid {display:flex; flex-wrap:wrap;调整大小:横向; align-content:flex-start; background-color:#ddd; padding:10px 0 0 10px;}。item {width:80px; height:80px;背景颜色:红色; margin:0 10px 10px 0;}。active.item {outline:5px solid black;}

 < div id =gridclass =grid> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div>< / div>  

正如您在这种方法中注意到的那样,我们不需要关于容器,屏幕大小,元素数量等的任何信息。唯一需要的信息是单个项目的维度。我们还需要一个小代码来纠正窗口大小调整时活动元素的位置。






奖励



如果你想拥有一个视觉化活动元素而不需要添加一个类或者使用JS获取它,那么这是另一个花哨的想法 。这个想法是使用容器上的背景在活动元素后面创建一个黑框。



顺便说一句,这个方法有两个缺点:


  1. 如果没有填满元素,则不容易处理最后一行,因为我们可能在后面没有黑框

  2. 我们必须考虑每行最后一个元素后留下的空间,以避免出现黑框的奇怪位置。

一个带有固定高度/宽度容器的简化代码:

var grid = document.querySelector('。grid'); document.addEventListener('keydown',function(e){e = e || window.event; if(e.keyCode =='38'){var y = parseInt(grid.style.backgroundPositionY); y =(y-90 + 270)%270; grid.style.back groundPositionY = Y + PX; } else if(e.keyCode =='40'){var y = parseInt(grid.style.backgroundPositionY); y =(y + 90)%270; grid.style.backgroundPositionY = Y + PX; } else if(e.keyCode =='37'){var x = parseInt(grid.style.backgroundPositionX); x =(x-90 + 270)%270; grid.style.backgroundPositionX = X + 像素; } else if(e.keyCode =='39'){var x = parseInt(grid.style.backgroundPositionX); x =(x + 90)%270; grid.style.backgroundPositionX = X + 像素; }});

.grid {display:flex; flex-wrap:wrap;宽度:270px;调整大小:横向; align-content:flex-start; background-color:#ddd;填充:10px 0 0 10px;背景图像:线性梯度(#000,#000); background-size:90px 90px; background-repeat:no-repeat;}。item {width:80px; height:80px;背景颜色:红色; margin:0 10px 10px 0;}

< div id =gridclass =gridstyle =background-position:5px 5px;> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div> < div class =item>< / div>< / div>

正如我们所看到的,代码非常简单,所以它可以适用于几乎所有值都已知且固定的情况。


A grid is implemented using the CSS flexbox. Example:

The number of rows in this example is 4 because I fixed the container width for demo purposes. But, in reality, it can change based on container's width (e.g. if the user resizes the window). Try to resize the Output window in this example to get a feeling.

There is always one active item, marked with the black border.

Using JavaScript, I allow users to navigate to the previous/next item using the left/right arrow. In my implementation, I just decrease/increase the index of the active item by 1.

Now, I'd like to allow users to navigate up/down as well. For that, I just need to decrease/increase the index of the active item by <amount of items in a row>. But, how do I calculate this number given that it is dependent on container's width? Is there a better way to implement the up/down functionality?

.grid {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  width: 250px;
  height: 200px;
  background-color: #ddd;
  padding: 10px 0 0 10px;
}

.item {
  width: 50px;
  height: 50px;
  background-color: red;
  margin: 0 10px 10px 0;
}

.active.item {
  outline: 5px solid black;
}

<div id="grid" class="grid">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item active"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>

解决方案

(For optimal experience better run the interactive snippets on full page)

Calculating number of elements per row

You need to get the width of an element with its margin (eventually border if they are set also) then you need to get the inner width of the container without padding. Having these 2 values you do a simple division to get the number of element per row.

Don't forget to consider the case where you have only one row, so you need to get the minimum value between the total number of elements and the number you get from the division.

//total number of element
var n_t = document.querySelectorAll('.item').length;
//width of an element
var w = parseInt(document.querySelector('.item').offsetWidth);
//full width of element with margin
var m = document.querySelector('.item').currentStyle || window.getComputedStyle(document.querySelector('.item'));
w = w + parseInt(m.marginLeft) + parseInt(m.marginRight);
//width of container
var w_c = parseInt(document.querySelector('.grid').offsetWidth);
//padding of container
var c = document.querySelector('.grid').currentStyle || window.getComputedStyle(document.querySelector('.grid'));
var p_c = parseInt(c.paddingLeft) + parseInt(c.paddingRight);
//nb element per row
var nb = Math.min(parseInt((w_c - p_c) / w),n_t);
console.log(nb);


window.addEventListener('resize', function(event){
   //only the width of container will change
   w_c = parseInt(document.querySelector('.grid').offsetWidth);
   nb = Math.min(parseInt((w_c - p_c) / w),n_t);
   console.log(nb);
});

.grid {
  display: flex;
  flex-wrap: wrap;
  resize:horizontal;
  align-content: flex-start;
  background-color: #ddd;
  padding: 10px 0 0 10px;
}

.item {
  width: 80px;
  height: 80px;
  background-color: red;
  margin: 0 10px 10px 0;
}

.active.item {
  outline: 5px solid black;
}

<div id="grid" class="grid">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item active"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>

Here is a jQuery version of the same logic with less of code:

//total number of element
var n_t = $('.item').length;
//full width of element with margin
var w = $('.item').outerWidth(true);
//width of container without padding
var w_c = $('.grid').width();
//nb element per row
var nb = Math.min(parseInt(w_c / w),n_t);
console.log(nb);

window.addEventListener('resize', function(event){
   //only the width of container will change
   w_c = $('.grid').width();
   nb = Math.min(parseInt(w_c / w),n_t);
   console.log(nb);
});

.grid {
  display: flex;
  flex-wrap: wrap;
  resize:horizontal;
  align-content: flex-start;
  background-color: #ddd;
  padding: 10px 0 0 10px;
}

.item {
  width: 80px;
  height: 80px;
  background-color: red;
  margin: 0 10px 10px 0;
}

.active.item {
  outline: 5px solid black;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="grid" class="grid">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item active"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>


And here is a demonstration of the interactive grid:

var all = document.querySelectorAll('.item');
var n_t = all.length;
var current = 0;
all[current].classList.add('active');

var w = parseInt(document.querySelector('.item').offsetWidth);
var m = document.querySelector('.item').currentStyle || window.getComputedStyle(document.querySelector('.item'));
w = w + parseInt(m.marginLeft) + parseInt(m.marginRight);
var w_c = parseInt(document.querySelector('.grid').offsetWidth);
var c = document.querySelector('.grid').currentStyle || window.getComputedStyle(document.querySelector('.grid'));
var p_c = parseInt(c.paddingLeft) + parseInt(c.paddingRight);
var nb = Math.min(parseInt((w_c - p_c) / w),n_t);

window.addEventListener('resize', function(e){
   w_c = parseInt(document.querySelector('.grid').offsetWidth);
   nb = Math.min(parseInt((w_c - p_c) / w),n_t);
});

document.addEventListener('keydown',function (e) {
    e = e || window.event;
    if (e.keyCode == '38') {
        if(current - nb>=0) {
          all[current].classList.remove('active');
          current-=nb;
          all[current].classList.add('active');
       }
    }
    else if (e.keyCode == '40') {
        if(current + nb<n_t) {
          all[current].classList.remove('active');
          current+=nb;
          all[current].classList.add('active');
       }
    }
    else if (e.keyCode == '37') {
       if(current>0) {
          all[current].classList.remove('active');
          current--;
          all[current].classList.add('active');
       }
    }
    else if (e.keyCode == '39') {
       if(current<n_t-1) {
          all[current].classList.remove('active');
          current++;
          all[current].classList.add('active');
       }
          
    }
});

.grid {
  display: flex;
  flex-wrap: wrap;
  resize:horizontal;
  align-content: flex-start;
  background-color: #ddd;
  padding: 10px 0 0 10px;
}

.item {
  width: 80px;
  height: 80px;
  background-color: red;
  margin: 0 10px 10px 0;
}

.active.item {
  outline: 5px solid black;
}

<div id="grid" class="grid">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>


Another Idea

We can also consider another way to navigate inside the grid without the need of the number of element per row. The idea is to rely on the function elementFromPoint(x,y).

The logic is as follows: We are inside an active element and we have its (x,y) position. By pressing a key we will increase/decrease these values and we use the above function to get the new element using the new (x,y). We test if we get a valid element and if this element is an item (contains item class). In this case we remove active from the previous one and we add it to the new one.

Here is a example where I only consider an inside navigation. When we reach left/right boundary of the container we will not get to previous/next line:

var a = document.querySelector('.item');
a.classList.add('active');

var off = a.getBoundingClientRect();
/* I get the center position to avoid any potential issue with boundaries*/
var y = off.top + 40; 
var x = off.left + 40;

document.addEventListener('keydown', function(e) {
  e = e || window.event;
  if (e.keyCode == '38') {
    var elem = document.elementFromPoint(x, y - 90 /* width + both margin*/);
    if (elem &&
      elem.classList.contains('item')) {
      document.querySelector('.active').classList.remove('active');
      elem.classList.add('active');
      y -= 90;
    }
  } else if (e.keyCode == '40') {
    var elem = document.elementFromPoint(x, y + 90);
    if (elem &&
      elem.classList.contains('item')) {
      document.querySelector('.active').classList.remove('active');
      elem.classList.add('active');
      y += 90;
    }
  } else if (e.keyCode == '37') {
    var elem = document.elementFromPoint(x - 90, y);
    if (elem &&
      elem.classList.contains('item')) {
      document.querySelector('.active').classList.remove('active');
      elem.classList.add('active');
      x -= 90;
    }
  } else if (e.keyCode == '39') {
    var elem = document.elementFromPoint(x + 90, y);
    if (elem &&
      elem.classList.contains('item')) {
      document.querySelector('.active').classList.remove('active');
      elem.classList.add('active');
      x += 90;
    }
  }
});

window.addEventListener('resize', function(e) {
  var off = document.querySelector('.active').getBoundingClientRect();
  y = off.top + 40;
  x = off.left + 40;
});

.grid {
  display: flex;
  flex-wrap: wrap;
  resize: horizontal;
  align-content: flex-start;
  background-color: #ddd;
  padding: 10px 0 0 10px;
}

.item {
  width: 80px;
  height: 80px;
  background-color: red;
  margin: 0 10px 10px 0;
}

.active.item {
  outline: 5px solid black;
}

<div id="grid" class="grid">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>

As you may notice in this method, we don't need any information about the container, the screen size, the number of element, etc. The only needed information is the dimension of a single item. We also need a small code to rectify the position of the active element on window resize.


Bonus

Here is another fancy idea if you want to have a visually active element without the need of adding a class or getting it with JS. The idea is to use background on the container to create a black box behind the active element.

By the way, this method has 2 drawbacks:

  1. Not easy to deal with the last line if it's not full of element as we may have the black box behind nothing
  2. We have to consider the space left after the last element of each row to avoid having a strange position of the black box.

Here is a simplified code with a fixed height/width container:

var grid = document.querySelector('.grid');

document.addEventListener('keydown', function(e) {
  e = e || window.event;
  if (e.keyCode == '38') {
    var y = parseInt(grid.style.backgroundPositionY);
    y= (y-90 + 270)%270;
    grid.style.backgroundPositionY=y+"px";
  } else if (e.keyCode == '40') {
    var y = parseInt(grid.style.backgroundPositionY);
    y= (y+90)%270;
    grid.style.backgroundPositionY=y+"px";
  } else if (e.keyCode == '37') {
    var x = parseInt(grid.style.backgroundPositionX);
    x= (x-90 + 270)%270;
    grid.style.backgroundPositionX=x+"px";
  } else if (e.keyCode == '39') {
    var x = parseInt(grid.style.backgroundPositionX);
    x= (x+90)%270;
    grid.style.backgroundPositionX=x+"px";
  }
});

.grid {
  display: flex;
  flex-wrap: wrap;
  width:270px;
  resize: horizontal;
  align-content: flex-start;
  background-color: #ddd;
  padding: 10px 0 0 10px;
  background-image:linear-gradient(#000,#000);
  background-size:90px 90px;
  background-repeat:no-repeat;
}

.item {
  width: 80px;
  height: 80px;
  background-color: red;
  margin: 0 10px 10px 0;
}

<div id="grid" class="grid" style="background-position:5px 5px;">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>

As we can see the code is pretty simple so it can be suitable for such situation where almost all the values are known and fixed.

这篇关于如何计算连续的弹性盒项目数量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆