<!DOCTYPE html>
<html>

<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta charset="utf-8" />
    <title>flex 最后一排左对齐</title>
</head>

<body>
    <div class="nr-box">
        <ul class="nr-list">
            <li><button class="nr-add">Add</button></li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
            <li>5</li>
            <li>6</li>
            <li>7</li>
            <li>8</li>
        </ul>
    </div>

    <p>ResizeObserver 监听容器大小变化,最后一排计算补齐</p>
    <p>第一行个数太少补齐</p>
</body>

</html>
document.querySelector('.nr-add').addEventListener('click', function () {
    var ul = document.querySelector('.nr-list');
    var lastNode = ul.children[ul.children.length - ul.querySelectorAll('.flex-repair-item').length - 1];

    var node = this.parentNode;
    var nn = node.cloneNode();
    nn.innerHTML = (lastNode.innerHTML * 1) + 1;

    ul.insertBefore(nn, lastNode.nextSibling);
}, false);

var fr = {
    build: function (se) {
        var box = document.querySelector(se), si, defer = 10, repairClass = 'flex-repair-item';

        new ResizeObserver(function () {
            //移除修补
            box.querySelectorAll('.' + repairClass).forEach(item => item.remove());

            clearTimeout(si);

            si = setTimeout(function () {
                var childs = box.children, len = childs.length;

                var index = 0, one = childs[0];
                for (var i = 0; i < len; i++) {
                    var node = childs[i];
                    if (node.offsetTop != one.offsetTop) {
                        index = i;
                        break;
                    }
                }
                var repair = index > 1 ? index - len % index : 0;
                if (repair == i) {
                    repair = 0;
                }

                //第一行不足补齐
                if (repair == 0) {
                    var mw = getComputedStyle(one, null).margin.match(/\d+/)[0] * 2;

                    var rownum = Math.floor(box.parentNode.clientWidth / (one.offsetWidth + mw));
                    if (len < rownum) {
                        repair = rownum - len;
                    }
                }

                //补齐
                while (repair--) {
                    var nn = one.cloneNode();
                    nn.classList.add(repairClass);
                    box.appendChild(nn)
                }
            }, defer);
        }).observe(box);
    }
}

fr.build('.nr-list');
.nr-box {
    margin: 30px;
}

.nr-box ul {
    margin: 0;
    padding: 0;
    display: flex;
    justify-content: space-between;
    flex-direction: row;
    flex-wrap: wrap;
}

.nr-box ul li {
    width: 200px;
    height: 200px;
    margin: 30px;
    font-size: 3em;
    list-style: none;
    border-radius: 6px;
    text-align: center;
    line-height: 200px;
    border: 2px solid orange;
}

/*补齐对象样式*/
.nr-box ul li.flex-repair-item {
    border-color: transparent;
}
.nr-box ul li.flex-repair-item::after {
    color: #ddd;
    content: "补位";
}

.nr-add {
    border: none;
    outline: none;
    cursor: pointer;
    font-size: 2em;
    background-color: white;
}