Mis*_*hko 47 html javascript css css3 flexbox
使用CSS flexbox实现网格.例:
此示例中的行数为4,因为我修复了容器宽度以用于演示目的.但是,实际上,它可以根据容器的宽度进行更改(例如,如果用户调整窗口大小).尝试在此示例中调整"输出"窗口的大小以获得感觉.
始终有一个活动项目,标有黑色边框.
使用JavaScript,我允许用户使用左/右箭头导航到上一个/下一个项目.在我的实现中,我只是将活动项的索引减少/增加1.
现在,我想让用户也可以向上/向下导航.为此,我只需要减少/增加活动项目的索引<amount of items in a row>.但是,如果它取决于容器的宽度,我该如何计算这个数?有没有更好的方法来实现上/下功能?
.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;
}Run Code Online (Sandbox Code Playgroud)
<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>Run Code Online (Sandbox Code Playgroud)
Bre*_*ody 28
问题比找到连续多少项更复杂.
最终,我们想知道活动元素的上方,下方,左侧和右侧是否存在元素.这需要考虑底行不完整的情况.例如,在下面的情况中,活动元素上面,下面或右边没有项目:
但是,为了确定活动项目的上方/下方/左侧/右侧是否有项目,我们需要知道连续多少项目.
要获得我们需要的每行项目数:
itemWidth-该outerWidth单个元件,包括border,padding和margin gridWidth-在innerWidth电网,排除border,padding和margin要使用纯JavaScript计算这两个值,我们可以使用:
const itemStyle = singleItem.currentStyle || window.getComputedStyle(active);
const itemWidth = singleItem.offsetWidth + parseFloat(itemStyle.marginLeft) + parseFloat(itemStyle.marginRight);
const gridStyle = grid.currentStyle || window.getComputedStyle(grid);
const gridWidth = grid.clientWidth - (parseFloat(gridStyle.paddingLeft) + parseFloat(gridStyle.paddingRight));
Run Code Online (Sandbox Code Playgroud)
然后我们可以使用以下方法计算每行的元素数量:
const numPerRow = Math.floor(gridWidth / itemWidth)
Run Code Online (Sandbox Code Playgroud)
注意:这仅适用于统一大小的项目,并且仅margin在以px单位定义时才有效.
处理所有这些宽度,填充,边距和边框确实令人困惑.有一个更简单,更简单的解决方案.
我们只需要找到offsetTop属性大于第一个网格元素的网格元素的索引offsetTop.
const grid = Array.from(document.querySelector("#grid").children);
const baseOffset = grid[0].offsetTop;
const breakIndex = grid.findIndex(item => item.offsetTop > baseOffset);
const numPerRow = (breakIndex === -1 ? grid.length : breakIndex);
Run Code Online (Sandbox Code Playgroud)
最后的三元组考虑了网格中只有一个项目和/或单行项目的情况.
const getNumPerRow = (selector) => {
const grid = Array.from(document.querySelector(selector).children);
const baseOffset = grid[0].offsetTop;
const breakIndex = grid.findIndex(item => item.offsetTop > baseOffset);
return (breakIndex === -1 ? grid.length : breakIndex);
}Run Code Online (Sandbox Code Playgroud)
.grid {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
width: 400px;
background-color: #ddd;
padding: 10px 0 0 10px;
margin-top: 5px;
resize: horizontal;
overflow: auto;
}
.item {
width: 50px;
height: 50px;
background-color: red;
margin: 0 10px 10px 0;
}
.active.item {
outline: 5px solid black;
}Run Code Online (Sandbox Code Playgroud)
<button onclick="alert(getNumPerRow('#grid'))">Get Num Per Row</button>
<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>Run Code Online (Sandbox Code Playgroud)
要知道活动元素上方或下方是否有项目,我们需要知道3个参数:
totalItemsInGridactiveIndexnumPerRow例如,在以下结构中:
<div id="grid" class="grid">
<div class="item"></div>
<div class="item"></div>
<div class="item active"></div>
<div class="item"></div>
<div class="item"></div>
</div>
Run Code Online (Sandbox Code Playgroud)
我们拥有totalItemsInGrid的5,则activeIndex有一个从零开始的索引2(这是该组中的第三个元素),并让我们说的numPerRow是3.
我们现在可以确定活动项目的上方,下方,左侧或右侧是否有项目:
isTopRow = activeIndex <= numPerRow - 1isBottomRow = activeIndex >= totalItemsInGid - numPerRowisLeftColumn = activeIndex % numPerRow === 0isRightColumn = activeIndex % numPerRow === numPerRow - 1 || activeIndex === gridNum - 1如果isTopRow是true,我们不能动了,如果isBottomRow是true我们不能下移.如果isLeftColumn是true,我们不能向左移动,如果isRightColumn如果true我们不能向右移动.
注意: isBottomRow不仅检查活动元素是否在底行,还检查其下是否有元素.在上面的示例中,活动元素不在底行,但在其下方没有项目.
我已经将这个用于调整大小的完整示例 - 并使#grid元素可调整大小,以便可以在下面的代码段中进行测试.
我创建了一个函数,navigateGrid它接受三个参数:
gridSelector - 网格元素的DOM选择器activeClass - 活动元素的类名direction-一up,down,left,或者right这可以'navigateGrid("#grid", "active", "up")与您的问题中的HTML结构一起使用.
该函数使用该offset方法计算行数,然后进行检查以查看active元素是否可以更改为向上/向下/向左/向右元素.
换句话说,该函数检查活动元素是否可以向上/向下和向左/向右移动.这意味着:
const navigateGrid = (gridSelector, activeClass, direction) => {
const grid = document.querySelector(gridSelector);
const active = grid.querySelector(`.${activeClass}`);
const activeIndex = Array.from(grid.children).indexOf(active);
const gridChildren = Array.from(grid.children);
const gridNum = gridChildren.length;
const baseOffset = gridChildren[0].offsetTop;
const breakIndex = gridChildren.findIndex(item => item.offsetTop > baseOffset);
const numPerRow = (breakIndex === -1 ? gridNum : breakIndex);
const updateActiveItem = (active, next, activeClass) => {
active.classList.remove(activeClass);
next.classList.add(activeClass);
}
const isTopRow = activeIndex <= numPerRow - 1;
const isBottomRow = activeIndex >= gridNum - numPerRow;
const isLeftColumn = activeIndex % numPerRow === 0;
const isRightColumn = activeIndex % numPerRow === numPerRow - 1 || activeIndex === gridNum - 1;
switch (direction) {
case "up":
if (!isTopRow)
updateActiveItem(active, gridChildren[activeIndex - numPerRow], activeClass);
break;
case "down":
if (!isBottomRow)
updateActiveItem(active, gridChildren[activeIndex + numPerRow], activeClass);
break;
case "left":
if (!isLeftColumn)
updateActiveItem(active, gridChildren[activeIndex - 1], activeClass);
break;
case "right":
if (!isRightColumn)
updateActiveItem(active, gridChildren[activeIndex + 1], activeClass);
break;
}
}Run Code Online (Sandbox Code Playgroud)
.grid {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
width: 400px;
background-color: #ddd;
padding: 10px 0 0 10px;
margin-top: 5px;
resize: horizontal;
overflow: auto;
}
.item {
width: 50px;
height: 50px;
background-color: red;
margin: 0 10px 10px 0;
}
.active.item {
outline: 5px solid black;
}Run Code Online (Sandbox Code Playgroud)
<button onClick='navigateGrid("#grid", "active", "up")'>Up</button>
<button onClick='navigateGrid("#grid", "active", "down")'>Down</button>
<button onClick='navigateGrid("#grid", "active", "left")'>Left</button>
<button onClick='navigateGrid("#grid", "active", "right")'>Right</button>
<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>Run Code Online (Sandbox Code Playgroud)
Tem*_*fif 23
(为获得最佳体验,请在整页上更好地运行交互式代码段)
计算每行的元素数量
你需要获得一个带有边距 的元素的宽度(如果它们也被设置,则最终为边框)然后你需要获得容器的内部宽度而不用填充.拥有这两个值,您可以进行简单的除法以获得每行的元素数.
不要忘记考虑只有一行的情况,因此您需要获得元素总数与从分部获得的数字之间的最小值.
//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);
});Run Code Online (Sandbox Code Playgroud)
.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;
}Run Code Online (Sandbox Code Playgroud)
<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>Run Code Online (Sandbox Code Playgroud)
这是一个jQuery版本的相同逻辑,代码较少:
//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);
});Run Code Online (Sandbox Code Playgroud)
.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;
}Run Code Online (Sandbox Code Playgroud)
<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>Run Code Online (Sandbox Code Playgroud)
这是交互式网格的演示:
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');
}
}
});Run Code Online (Sandbox Code Playgroud)
.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;
}Run Code Online (Sandbox Code Playgroud)
<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>Run Code Online (Sandbox Code Playgroud)
我们还可以考虑另一种在网格内导航的方法,而不需要每行的元素数量.这个想法是依靠这个功能elementFromPoint(x,y).
逻辑如下:我们在一个活跃的元素中,我们有自己的(x,y)位置.通过按键我们将增加/减少这些值,我们使用上面的函数来使用new获取新元素(x,y).我们测试是否获得有效元素以及此元素是否为项(包含item类).在这种情况下,我们从前一个中删除active,然后将其添加到新的.
这是一个我只考虑内部导航的例子.当我们到达容器的左/右边界时,我们将无法到达上一行/下一行:
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;
});Run Code Online (Sandbox Code Playgroud)
.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;
}Run Code Online (Sandbox Code Playgroud)
<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>Run Code Online (Sandbox Code Playgroud)
正如您在此方法中可能注意到的那样,我们不需要有关容器,屏幕大小,元素数量等的任何信息.唯一需要的信息是单个项目的维度.我们还需要一个小代码来纠正窗口大小调整时活动元素的位置.
如果你想拥有一个视觉上活跃的元素而不需要添加一个类或者用JS获取它,这是另一个奇特的想法.我们的想法是在容器上使用background来在活动元素后面创建一个黑盒子.
顺便说一句,这种方法有两个缺点:
这是一个带有固定高度/宽度容器的简化代码:
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";
}
});Run Code Online (Sandbox Code Playgroud)
.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;
}Run Code Online (Sandbox Code Playgroud)
<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>Run Code Online (Sandbox Code Playgroud)
我们可以看到代码非常简单,因此它适用于几乎所有值都已知并且已修复的情况.
上下移动的唯一方法就是减少不必要的复杂功能,就是每行计算一个盒子并改变索引.唯一的问题是你需要在窗口加载和调整大小事件上计算boxcount.
var boxPerRow=0;
function calculateBoxPerRow(){}
window.onload = calculateBoxPerRow;
window.onresize = calculateBoxPerRow;
Run Code Online (Sandbox Code Playgroud)
现在,如果你想要一个非常简单的方法来获得连续的盒子数量,甚至没有关心容器和盒子的大小,忘记边距和填充,你可以检查有多少盒子与第一个盒子对齐比较offsetTop属性.
HTMLElement.offsetTop只读属性返回当前元素相对于offsetParent节点顶部的距离.[来源: developer.mozilla.orgl ]
你可以像下面这样实现它:
function calculateBoxPerRow(){
var boxes = document.querySelectorAll('.item');
if (boxes.length > 1) {
? var i = 0, total = boxes.length, firstOffset = boxes[0].offsetTop;
? while (++i < total && boxes[i].offsetTop == firstOffset);
? boxPerRow = i;
? }
}
Run Code Online (Sandbox Code Playgroud)
完整的工作示例:
(function() {
var boxes = document.querySelectorAll('.item');
var boxPerRow = 0, currentBoxIndex = 0;
function calculateBoxPerRow() {
if (boxes.length > 1) {
var i = 0,
total = boxes.length,
firstOffset = boxes[0].offsetTop;
while (++i < total && boxes[i].offsetTop == firstOffset);
boxPerRow = i;
}
}
window.onload = calculateBoxPerRow;
window.onresize = calculateBoxPerRow;
function focusBox(index) {
if (index >= 0 && index < boxes.length) {
if (currentBoxIndex > -1) boxes[currentBoxIndex].classList.remove('active');
boxes[index].classList.add('active');
currentBoxIndex = index;
}
}
document.body.addEventListener("keyup", function(event) {
switch (event.keyCode) {
case 37:
focusBox(currentBoxIndex - 1);
break;
case 39:
focusBox(currentBoxIndex + 1);
break;
case 38:
focusBox(currentBoxIndex - boxPerRow);
break;
case 40:
focusBox(currentBoxIndex + boxPerRow);
break;
}
});
})();Run Code Online (Sandbox Code Playgroud)
.grid {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
width: 50%;
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;
}Run Code Online (Sandbox Code Playgroud)
<div>[You need to click on this page so that it can recieve the arrow keys]</div>
<div id="grid" class="grid">
<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>Run Code Online (Sandbox Code Playgroud)