阿里面试题:瀑布流布局(手撕版)前言 朋友们要问了,什么是瀑布流布局。瀑布流布局就是多行等宽元素排列,但是等宽不等高,后
前言
朋友们要问了,什么是瀑布流布局。瀑布流布局就是多行等宽元素排列,但是等宽不等高,后边的元素以此排列添加到前一行最矮元素的下方。简单说它能够在同样的空间中展示出更多的内容,吸引用户继续向下滑动浏览,并提供更好的用户体验,如下方小红书布局。
如何实现
- 这里我准备了20张图片
- 框架搭建:id为container的div内有20个类名为box的div,盒子中放类名为box-img的盒子用于放img标签
<body>
<div id="container">
<div class="box">
<div class="box-img">
<img src="./img/1.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/2.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/3.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/4.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/5.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/6.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/7.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/8.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/9.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/10.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/1.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/2.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/3.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/4.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/5.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/6.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/7.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/8.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/9.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/10.webp" alt="">
</div>
</div>
</div>
<script src="index.js"></script>
</body>
由于div为块级元素,目前状态应该是一行一张的图片,接下来我们编写一下css部分的代码
- 首先通配符将所有元素的内外边距取消
- container容器设置相对定位,后续他的子容器需要相对于他来做定位
- box做浮动,从左向右排列
- 每个box-img设置好宽度,不要宽高同时设置,避免图片变形
- img继承父级元素(box-img)的宽度就行
<style>
* {
margin: 0;
padding: 0;
}
#container {
position: relative;
}
.box {
float: left;
padding: 5px;
}
.box-img {
width: 150px;
padding: 5px;
border: 1px solid #09f758;
}
img {
width: 100%;
}
</style>
目前状态:
瀑布流布局需要把下一个照片放在上一行最矮的图片下面,因此接下来我们缕一缕思路编写js代码
- 首先要获取屏幕的宽度,因为它决定了一行能放多少张图片
documemt.documentElement.childWidth
- 我们可以写一个函数,将父级容器传进来,通过父级容器获取到有多少个box即多少张图片
- 要知道一行能放多少张图片,因此我们还需要获取到每一张照片的宽度。
offsetWidth
- 得到了屏幕宽度和照片宽度后,通过除法可以得到一个小数,我们需要做向下取整。如可以放3.5张照片我们就放3张
- 开始没有设置容器的宽度,得到了照片宽度和一行能放多少张之后,我们可以设置容器宽度
- 接下来就是瀑布流的重点操作了,如何把下一张照片放到上一行中最矮的那一列下面?
- 思路:我们可以定义一个数组,遍历一遍第一行的照片,获取到每一张照片的高度,并存储到数组中,然后获取到一行中最矮的那一列
(Math.min.apply)
,并通过indexOf
获取到是哪一列(下标),还记得我们最开始设置容器的定位为相对定位吗?这里就有作用了,接下来摆放下一张图片的位置,首先将图片的定位模式改为绝对定位,然后设置这张图片的高度(距离顶部的位置):高度即最矮的那一列的下面ccontent[i].style.top = minHeight + 'px';
设置距离左边的位置:ccontent[i].style.left = ccontent[minIndex].offsetLeft + 'px';
,最后还需要更新一下这一列的高度BoxHeightArr[minIndex] += ccontent[i].offsetHeight;
// 获取到用户屏幕的宽度,决定了一行能放下几张图片
// 操作下一张图,放到上一行最矮的列下面
function imgLocation(parent, content) {
var cparent = document.getElementById(parent);
// 有多少张图片
// var ccontent = document.querySelectorAll('.box');
// var ccontent = document.querySelectorAll('#container .box');
var ccontent = getChildElement(cparent, content);
// console.log(ccontent.length);
// 获取每一个box的宽度
var imgWidth = ccontent[0].offsetWidth;
// 屏幕一行能放下几个图片 5.6 取整放5张
var num = Math.floor(document.documentElement.clientWidth / imgWidth);
//设置container的宽度
cparent.style.width = `%{imgWidth *num}px`;
// 要操作的是哪一张?num+1,取到每一列的高度
var BoxHeightArr = [];
for (let i = 0; i < ccontent.length; i++) {
if (i < num) {
// 第一行
BoxHeightArr[i] = ccontent[i].offsetHeight;
} else {
// 需要操作的部分,下一行了
var minHeight = Math.min.apply(null, BoxHeightArr);
var minIndex = BoxHeightArr.indexOf(minHeight);
//摆放图片的位置
ccontent[i].style.position = 'absolute';
ccontent[i].style.top = minHeight + 'px';
ccontent[i].style.left = ccontent[minIndex].offsetLeft + 'px';
// 更新这一列的高度
BoxHeightArr[minIndex] += ccontent[i].offsetHeight;
console.log(minIndex);
}
}
// console.log(BoxHeightArr);
}
function getChildElement(parent, child) {
// 获取parent中所有的child
var childArr = [];
// 返回的是数组结构
var allChild = parent.getElementsByTagName('*');
// 跳出所有Child中的box
for (let i = 0; i < allChild.length; i++) {
if (allChild[i].className == child) {
childArr.push(allChild[i]);
}
}
return childArr;
}
imgLocation('container', 'box');
效果
至此瀑布流就已经实现了,但是在我们编写代码时有什么需要注意的呢?
- 小白朋友在敲代码的时候可以为了学习而学习,例如:我们知道要获取里面有多少张图片可以直接通过内置方法
var ccontent = document.querySelectorAll('.box');
但是为什么不直接使用呢?因为代码不够优雅,后续如果页面再出现类名为box的标签,那整个代码都需要修改了。或者使用var ccontent = document.querySelectorAll('#container .box');
为什么也不用呢?因为为了学习编写代码的能力学习封装思想。我们可以自己定义一个方法来获取图片的数量。var ccontent = getChildElement(cparent, content);
怎么实现的呢?定义一个数组用于存放照片,后续获取数组长度就知道有多少张照片了,通过getElementsByTagName('*')
方法得到容器中所有的标签,但是我们知道照片只存放在box的box-img的img标签中,因此倘若我们有20张照片,这里获取到的数量便是60个,因此我们可以遍历一下,通过className
属性获取到类名为box的标签,最后通过数组push方法存入数组,并将数组返回。
function getChildElement(parent, child) {
// 获取parent中所有的child
var childArr = [];
// 返回的是数组结构
var allChild = parent.getElementsByTagName('*');
// 跳出所有Child中的box
for (let i = 0; i < allChild.length; i++) {
if (allChild[i].className == child) {
childArr.push(allChild[i]);
}
}
return childArr;
}
那么,假如有一天你去到阿里面试,面试官问你瀑布流是如何实现的,看完本篇后,你能够游刃有余的给面试官解答吗?
转载自:https://juejin.cn/post/7361234872779669516