如何检测CSS Flex Wrap事件

col17t5w  于 2023-04-13  发布在  其他
关注(0)|答案(7)|浏览(308)

我有一个flex容器,里面有项目。如何检测flex wrap事件?我想应用一些新的css到已经被 Package 的元素上。我想用纯css是不可能检测到wrap事件的。但这将是一个非常强大的功能!我可以尝试在元素 Package 到新的行/行时通过媒体查询“捕获”这个断点事件。但这是一个糟糕的方法。我可以尝试通过脚本检测它,但它也不是很好。

我非常惊讶,但是简单的$(“#element”).resize()不能检测flex容器的高度或宽度变化,从而将适当的css应用到子元素。
我发现只有这个例子的jquery代码工作jquery event listen on position changed
但还是很可怕。

nfzehxib

nfzehxib1#

这里有一个可能的解决方案。可能还有其他的陷阱和边缘情况需要检查。
基本思想是循环遍历flex items,并根据前一个兄弟测试它们的top位置。如果top值更大(因此在页面的更下方),则该项目已经 Package 。
函数detectWrap返回一个已经 Package 的DOM元素数组,可以根据需要进行样式设置。
理想情况下,该函数可以与ResizeObserver一起使用(同时使用window 's resize event作为后备)作为触发器,以检查窗口调整大小或页面中的元素因脚本和其他用户交互而更改时的换行。由于StackOverflow代码窗口不会调整大小,因此它在这里不起作用。
这里有一个CodePen,它可以调整屏幕大小。

var detectWrap = function(className) {
  
  var wrappedItems = [];
  var prevItem = {};
  var currItem = {};
  var items = document.getElementsByClassName(className);

  for (var i = 0; i < items.length; i++) {
    currItem = items[i].getBoundingClientRect();
    if (prevItem && prevItem.top < currItem.top) {
      wrappedItems.push(items[i]);
    }
    prevItem = currItem;
  };
  
  return wrappedItems;

}

window.onload = function(event){
  var wrappedItems = detectWrap('item');
  for (var k = 0; k < wrappedItems.length; k++) {
    wrappedItems[k].className = "wrapped";
  }
};
div  {
  display: flex;
  flex-wrap: wrap;
}

div > div {
  flex-grow: 1;
  flex-shrink: 1;
  justify-content: center;
  background-color: #222222;
  padding: 20px 0px;
  color: #FFFFFF;
  font-family: Arial;
  min-width: 300px;
}

div.wrapped {
  background-color: red;
}
<div>
  <div class="item">A</div>
  <div class="item">B</div>
  <div class="item">C</div>
</div>
vd2z7a6w

vd2z7a6w2#

为了这个目的,在jQuery上稍微改进了一点代码片段。

wrapped();

$(window).resize(function() {
   wrapped();
});

function wrapped() {
    var offset_top_prev;

    $('.flex-item').each(function() {
       var offset_top = $(this).offset().top;

      if (offset_top > offset_top_prev) {
         $(this).addClass('wrapped');
      } else if (offset_top == offset_top_prev) {
         $(this).removeClass('wrapped');
      }

      offset_top_prev = offset_top;
   });
}
v1uwarro

v1uwarro3#

我已经修改了sansSpoon的代码,即使元素不在页面的绝对顶部也可以工作。Codepen:https://codepen.io/tropix126/pen/poEwpVd

function detectWrap(node) {
    for (const container of node) {
        for (const child of container.children) {
            if (child.offsetTop > container.offsetTop) {
                child.classList.add("wrapped");
            } else {
                child.classList.remove("wrapped");
            }
        }
    }
}

请注意,margin-top不应该应用于项目,因为它被分解到getBoundingClientRect中,并将触发 Package 类应用于所有项目。

drnojrws

drnojrws4#

我使用了类似的方法来确定<li>是否被 Package 在<ul>中,该<ul>的显示设置为flex

ul = document.querySelectorAll('.list');

function wrapped(ul) {

    // loops over all found lists on the page - HTML Collection
    for (var i=0; i<ul.length; i++) {

        //Children gets all the list items as another HTML Collection
        li = ul[i].children;

        for (var j=0; j<li.length; j++) {
            // offsetTop will get the vertical distance of the li from the ul.
            // if > 0 it has been wrapped.
            loc = li[j].offsetTop;
            if (loc > 0) {
                li[j].className = 'wrapped';
            } else {
                li[j].className = 'unwrapped';
            }
        }
    }
}
z5btuh9x

z5btuh9x5#

我注意到元素通常会相对于第一个元素进行换行。比较每个元素的顶部偏移量与第一个元素的偏移量是一种更简单的方法。这适用于wrap和wrap-reverse。(如果元素使用灵活顺序,可能不起作用)

var wrappers = $('.flex[class*="flex-wrap"]'); //select flex wrap and wrap-reverse elements

    if (wrappers.length) { //don't add listener if no flex elements
        $(window)
            .on('resize', function() {
                wrappers.each(function() {
                    var prnt = $(this),
                        chldrn = prnt.children(':not(:first-child)'), //select flex items
                        frst = prnt.children().first();

                    chldrn.each(function(i, e) { $(e).toggleClass('flex-wrapped', $(e).offset().top != frst.offset().top); }); //element has wrapped
                    prnt.toggleClass('flex-wrapping', !!prnt.find('.flex-wrapped').length); //wrapping has started
                    frst.toggleClass('flex-wrapped', !!!chldrn.filter(':not(.flex-wrapped)').length); //all are wrapped
               });
            })
            .trigger('resize'); //lazy way to initially call the above
    }
.flex {
    display: flex;
}

.flex.flex-wrap {
    flex-wrap: wrap;
}

.flex.flex-wrap-reverse {
    flex-wrap: wrap-reverse;
}

.flex.flex-1 > * { /*make items equal width*/
    flex: 1;
}

.flex > * {
  flex-grow: 1;
}

.cc-min-width-200 > * { /*child combinator*/
  min-width: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="flex flex-1 flex-wrap-reverse cc-min-width-200">
    <div>Hello</div>
    <div>There</div>
    <div>World</div>
</div>
vdzxcuhz

vdzxcuhz6#

如果有人想找到行的最后一个元素,可以使用下面的逻辑。它也适用于多行

window.onresize = function (event) {
            const elements = document.querySelectorAll('.borrower-detail');
            let previousElement = {};
            let rowTop = elements[0].getBoundingClientRect().top;
            elements.forEach(el => el.classList.remove('last-el-of-row'))
            elements.forEach(el => {
                const elementTop = el.getBoundingClientRect().top;

                if (rowTop < elementTop) {
                    previousElement.classList.add('last-el-of-row');
                    rowTop = elementTop;
                }

                previousElement = el;
            })
        };
pkwftd7m

pkwftd7m7#

避免在这类任务中使用媒体查询,它太不稳定了。想想当另一个flex项目被添加,或者文本长度改变时所涉及的工作,你必须重新调整这些媒体查询。
请考虑使用Resize Observer API。
Flex框中的链接列表:

<ul class=flex_box role=list>
    <li><a href="#/">Fake link 1</a></li>
    <li><a href="#/">Fake link 2</a></li>
    <li><a href="#/">Fake link 3</a></li>
    <li><a href="#/">Fake link 4</a></li>
    <li><a href="#/">Fake link 5</a></li>
</ul>

CSS:

.flex_box {
    display: flex;
    flex-wrap: wrap;
    padding: 0;
    gap: 1rem;
}

/* Optional coloring prettiness */
.flex_box > * {
    background-color: #000;
    color: #fff;
}
.flex_box-wrapped > .flex_item-wrapped {
    background-color: #333
}

Package 后,脚本将类flex_box-wrapped添加到Flex容器中,并将flex_item-wrapped添加到 Package 后的每个Flex项中。
这是通过测试每个项目相对于第一个项目的顶部位置来实现的。
当浏览器宽度(或高度,或字体大小)更改时,Resize Observer API会相应地重新计算并修改每个类名。

const flexBoxWrapDetection = (_ => {

    'use strict';

    const flexBoxQuery = '.flex_box';
    const boxWrappedClass = 'flex_box-wrapped';
    const itemWrappedClass = 'flex_item-wrapped';

    const markFlexboxAndItemsAsWrapped = flexBox => {

        const flexItems = flexBox.children;

        // Needs to be in a row for the calculations to work
        flexBox.setAttribute('style', 'flex-direction: row');

        // Rounded for inline-flex sub-pixel discrepencies:
        const firstItemTop = Math.round(flexItems[0].getBoundingClientRect().top);
        const lastItemTop = Math.round(flexItems[flexItems.length - 1].getBoundingClientRect().top);

        // Add / remove wrapped class to each wrapped item
        for (const flexItem of flexItems) {
            if (firstItemTop < flexItem.offsetTop) {
                flexItem.classList.add(itemWrappedClass);
            } else {
                flexItem.classList.remove(itemWrappedClass);
            }
        }

        // Remove flex-direction:row - only used while calculating spacing
        flexBox.removeAttribute('style');

        // Add / remove wrapped class to the flex container
        requestAnimationFrame(_ => {
            if (firstItemTop >= lastItemTop) {
                flexBox.classList.remove(boxWrappedClass);
            } else {
                flexBox.classList.add(boxWrappedClass);
            }
        });
    };

    // Using .flex_box as an identifier:
    const flexBoxes = document.querySelectorAll(flexBoxQuery);
    for (const flexBox of flexBoxes) {

        markFlexboxAndItemsAsWrapped(flexBox);

        // Listen for width/height changes on the flexbox
        new ResizeObserver(entries => 
            entries.forEach(entry => markFlexboxAndItemsAsWrapped(entry.target))
        ).observe(flexBox);
    }

})();

这里有一个演示CodePen:Flex-wrap detection script,它还显示了如何在Flex框 Package 或展开时立即在水平和垂直之间切换。
该演示也适用于direction: rtl;和/或flex-wrap: row-reverse;

相关问题