When xiaobian just entered the line, referring to the answer in Baidu, with JS to achieve a waterfall flow layout. So when receiving the waterfall flow layout design draft, the first reaction is to use JS implementation.

Js implementation

The general idea is:

css

    1. The waterfall flow container sets the absolute positioning, and then sets the relative positioning of all the child elements.
    1. Get the data through an Ajax request and render it to the page
	div class="waterfall-list-container" >
      <div class="item-container" >
      </div>
    </div>
    css
    .waterfall-list-container {
      position: relative;
      .item-container {
        position: absolute; }}Copy the code

js

    1. Define an array variable (the array length is the same as the number of columns displayed in the waterfall flow table) to record the height change of each column.
    1. Get all the children of the waterfall flow container and iterate over them.
    1. Set the height from the top to the children of each column, in turn, based on the value in the record height change array. And change the height of the waterfall flow container.
      // Waterfall flow container
      const parent = document.querySelector('.waterfall-list-container')
      parent.style.height = `The ${0}px`
      // Item in the waterfall stream
      const waterfalls = document.querySelectorAll('.item-container')
      if(! waterfalls.length) {return
      }
      / / the number of columns
      const col = 2
      / / interval
      const xspace = this.gutterWidth
      const yspace = this.gutterWidth
      const w = waterfalls[0].offsetWidth
      // An array that records the height of each column from the top
      const colHs = []
      waterfalls.forEach((ele, index) = > {
        // Waterfall container height
        const parentH = parent.offsetHeight
        // The height of the previous element
        const preDomH = index - col >=0 && waterfalls[index - col].offsetHeight
        // Current element height
        const currentDomH = ele.offsetHeight
        if (index < col) {
          // The first line is special, and the distance from the top is 0
          colHs[index] = 0
        } else {
          // Record the height change of each column
          const i = index % col
          preDomH && (colHs[i] += preDomH + yspace)
        }
        ele.style.top = `${colHs[index % col]}px`
        ele.style.left = `${(index % col) * (w + xspace)}px`
        let maxH = colHs[0]
        colHs.forEach(h= > {
          +maxH < h && (maxH = h)
        })
        if (maxH) {
          parent.style.height = `${+maxH + currentDomH}px`
        } else {
          parentH < currentDomH && (parent.style.height = `${currentDomH}px`)}})Copy the code

Since using this method, the test has given me a bug that hasn’t broken o(╥﹏╥)o, roughly some of the child elements overlap. I guess the reason is: because the child element of the project made by xiaobian is with a picture, so it should be that the picture has not been loaded when JS is executed, resulting in the height calculation error. (If not, welcome you to correct!)

I’ve been thinking about it. It’s not gonna work. The Vue project also has manipulation of DOM elements, which is a bit bad. So again visited the almighty Baidu, found a pure CSS method to achieve the waterfall flow method.

Pure CSS implementation

Pure CSS implements waterfall flow

According to baidu to the method tried, really convenient and effective. Waterfall flow is ordered vertically in a pure CSS layout, but it is required horizontally (omg, not required ~ ~ ~ ~).

demo

<! DOCTYPE html><html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<style>
		.masonry {
		    column-count: 2;
		    column-gap: 0;
		}
		.item {
		    break-inside: avoid;
		    box-sizing: border-box;
			text-align: center;
			line-height: 150px;
			border: 1px solid skyblue;
		}
	</style>
	<body>
		<div class="masonry">
		    <div class="item i1">
		        <div class="item__content">
					1
		        </div>
		    </div>
		    <div class="item i2">
		        <div class="item__content">
					2
		        </div>
		    </div>
			<div class="item i3">
			    <div class="item__content">
			    </div>
			</div>
			<div class="item i4">
				3
			    <div class="item__content">
			    </div>
			</div>
			<div class="item i5">
				4
			    <div class="item__content">
			    </div>
			</div>
			<div class="item i6">
				5
			    <div class="item__content">
			    </div>
			</div>
			<div class="item i7">
				6
			    <div class="item__content">
			    </div>
			</div>
			<div class="item i8">
				7
			    <div class="item__content">
			    </div>
			</div>
			<div class="item i9">
				8
			    <div class="item__content">
			    </div>
			</div>
			<div class="item i10">
				9
			    <div class="item__content">
			    </div>
			</div>
		    <! -- more items -->
		</div>
	</body>
</html>
Copy the code

The renderings are as follows:

Ah! What is to be done? The waterfall flow layout can be divided into the desired number of columns, and then set the left and right spacing between the columns and the upper and lower spacing of the child elements in each column. As for how to divide into different columns, the data order is still left to right? As mentioned above, small series projects are using the VUE framework, using % operation can quickly achieve the effect.

Other methods

    1. The waterfall stream parent declares the Flex layout
    1. Renders the child element of the corresponding column number
    1. Render the subelements of each column to distinguish the same column method (list subscript % number of columns === number of currently rendered columns subscript)
  <div class="container"> <! -- col: Number of cascaded streams --><div class="listl-container" v-for="(item, index) in col" :key="colIndex">
      <div
        v-for="(items, indexs) in list"
        v-if="indexs % col === index"
        :key="`wf-list-${index}`"
        class="item">
        <! -- you content -->
      </div>
    </div>
  </div>
  
  .container {
    display: flex;
    .list-container {
      flex: 1;
      margin: 0 10px;
      &:first-of-type {
        margin-right: 4px;
      }
      &:last-of-type { margin-left: 4px; }}}Copy the code

In fact, this method is not optimal, to render col * list.length times. However, we haven’t come up with a better solution yet, so we’ll have to use this one