如何使用JavaScript查找CSS网格布局元素位于哪列

3
如果我使用grid-template-columns: repeat(3, 1fr)将一些HTML元素放置到一个三列网格中,是否可能用JavaScript找到元素所在的列?一些元素也跨越多行,因此元素的索引不一定与其列匹配。

For example:

const myElement = document.querySelector('div:nth-child(5)');

myElement.style.background = '#f00';

const columnIndex = 1; // How do I find this?
console.log('myElement is in column ' + columnIndex);
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: 30px;
}

.item,
.item-large {
  padding: 30px;
  background: #3cf;
}

.item-large {
  grid-row: span 2;
}
<div class="grid">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item-large"></div>
  <div class="item">Which column am I in?</div>
  <div class="item-large"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>


我尝试过 myElement.style.gridColumnStart,但是除非手动设置了样式,否则它会返回一个空字符串。使用 getComputedStyle(myElement).gridColumnStart 会产生类似的结果。 - Thomas Higginbotham
你能否在这里快速编写一小段代码片段,展示你目前的进展? - Keith
这是一个快速示例:https://codepen.io/anon/pen/xLJpXQ - Thomas Higginbotham
我可以在元素上使用 offsetLeft 并测量它们的位置来确定列,但似乎应该有更简单的方法。 - Thomas Higginbotham
对于 Stack Overflow 来说,代码片段更好。人们可以在 Stack Overflow 内运行它们,可以修改和添加等等到对话中。我已经为您完成了这个。 - Keith
2个回答

1
我所做的是遍历所有元素,并且每次左侧位置增加并小于我的元素位置时,我就增加一个计数器来跟踪列数。
我还修改了这里的代码片段,使其更具交互性。如果您单击div,它会重新选择并显示新的列号。

var myElement = document.querySelector('div:nth-child(5)');
const allElements = document.querySelector('.grid').querySelectorAll('div');


//myElement.style.background = '#f00';
myElement.classList.add('item-found');

function showFound() {
  let maxcolpos = -1, colposCount = 0;
  
  for(elem of allElements) {
    let l = elem.getBoundingClientRect().left;
    if (l > maxcolpos) {
      maxcolpos = l;
      if (myElement.getBoundingClientRect().left > l) colposCount ++;
    }
  }

  const columnIndex = colposCount + 1; //zero based, leave +1 if you want 0 based
  myElement.innerText = 'Column = ' + columnIndex;
 }
 
 showFound();
 
 document.querySelector('.grid').addEventListener("click", function(e) {
   if (e.target && !e.target.matches(".grid")) {
     myElement.classList.remove('item-found');
     myElement.innerText = '';
     myElement = e.target;
     myElement.classList.add('item-found');
     showFound();
   }
 });
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: 30px;
}

.item,
.item-large {
  padding: 30px;
  background: #3cf;
}

.item-large {
  grid-row: span 2;
}

.item-found {
  background-color: red;
}
<div class="grid">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item-large"></div>
  <div class="item"></div>
  <div class="item-large"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>


0
我知道这个话题已经很老了,但对于那些在React和TypeScript上苦苦挣扎的人来说,这可能会很有趣。
我做了一个仪表板组件,其中包含一个嵌套的网格组件,该组件又包含嵌套的小部件组件,可以为其指定跨越多少列的属性 - 如果没有给出值,则默认为1。
我不喜欢网格中的最后一项“不完整”。使用这段代码,网格中的最后一项将延伸到最后一列的末尾。它计算出当前位置,因此如果您有一个具有未知网格项但仍希望获得完整块的网格,这将非常有用。
还别忘了在JSX中给网格元素添加ref={gridRef}属性 :-)
const gridRef = useRef<HTMLDivElement>(null);

useLayoutEffect(() => {
    if (!gridRef.current) return;
    const widgets = gridRef.current.children as HTMLCollectionOf<HTMLDivElement>;
    const widgetsLeftPosition: number[] = [];
    const widgetsInLastRow: HTMLDivElement[] = [];

    // add offset from screenedge to array
    for (let i = 0; i < widgets.length; i++) {
        const currentWidgetLeftPosition = widgets[i].getBoundingClientRect().left;
        widgetsLeftPosition.push(currentWidgetLeftPosition);
    }

    // add elements (from rear end) to array, and
    // check if position of current element has same offset as first item, then break 
    // (which results in having only the elements of the last row in the array)
    for (let i = widgetsLeftPosition.length - 1; i >= 0; i--) {
        widgetsInLastRow.push(widgets[i]);
        if (widgetsLeftPosition[i] === widgetsLeftPosition[0]) break;
    }

    // for every element in the last row: check the gridColumnEnd value and
    // take the last character (which is a 'string-integer').
    // parse it to a normal integer, then sum up all integers and return that value
    const columnSpanStart = () => {
        let sum = 0;
        for (let i = 0; i < widgetsInLastRow.length; i++) {
            const spanString = getComputedStyle(widgetsInLastRow[i]).gridColumnEnd;
            sum += parseInt(spanString.slice(-1));
        }
        return sum;
    };

    // finally, use the returned value from columnSpanStart() as gridColumn 'start' value.
    // this will overwrite the default 'grid-column-end' style given by the widget component.
    const lastWidget = widgets[widgets.length - 1];
    lastWidget.style.gridColumn = `${columnSpanStart()} / -1`;
}, []);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接