关于float元素浮动后高度变化导致排列错位的问题

简介: 本文详解float布局中因元素高度变化导致的错位问题,结合代码与案例,分析其成因并给出通过`clear:left`清除浮动的实用解决方案,适用于维护使用float布局的老旧项目。

目录


前言

你好,我是喵喵侠。在现代Web布局中,flex和grid布局用到的会比较多,但我们仍然会遇到一些老旧项目,里面的前端UI框架,采用的还是float布局。在这种情况下,如果你对float布局不了解,就会在开发的过程中踩到坑。下面我来为你讲解,float元素高度变化后,是如何影响相邻元素的,以及如何解决这样的问题。

问题描述

首先假设有一个容器盒子,宽度是300px,高度是300px,它里面有9个div子元素,元素的宽高都是100px,都是float:left左浮动。

正常效果

根据这样的描述,我写了一个正常效果的demo如下:

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>浮动布局示例</title>
<style>
  .container {
    width: 300px;
    height: 300px;
    border: 1px solid #ccc;
    position: relative;
  }
  .box {
    width: 100px;
    height: 100px;
    float: left;
    text-align: center;
    line-height: 100px;
  }
  .box1 { background-color: #ff9999; }
  .box2 { background-color: #ffcc99; }
  .box3 { background-color: #ffff99; }
  .box4 { background-color: #ccff99; }
  .box5 { background-color: #99ffcc; }
  .box6 { background-color: #99ccff; }
  .box7 { background-color: #cc99ff; }
  .box8 { background-color: #ff99cc; }
  .box9 { background-color: #cccccc; }
</style>
</head>
<body>
<div class="container">
  <div class="box box1">1</div>
  <div class="box box2">2</div>
  <div class="box box3">3</div>
  <div class="box box4">4</div>
  <div class="box box5">5</div
  <div class="box box6">6</div>
  <div class="box box7">7</div>
  <div class="box box8">8</div>
  <div class="box box9">9</div>
</div>
</body>
</html>

正常效果如下:

问题效果

如果我把其中一个子元素,比方说子元素1的高度,修改为150px。此时你会发现,原本的子元素4跑到了原本5的位置,5跑到了原本6的位置,以此类推。

问题代码如下:

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>浮动布局示例</title>
<style>
  .container {
    width: 300px;
    height: 300px;
    border: 1px solid #ccc;
    position: relative;
  }
  .box {
    width: 100px;
    height: 100px;
    float: left;
    text-align: center;
    line-height: 100px;
  }
  .box1 { height:150px;background-color: #ff9999; }
  .box2 { background-color: #ffcc99; }
  .box3 { background-color: #ffff99; }
  .box4 { background-color: #ccff99; }
  .box5 { background-color: #99ffcc; }
  .box6 { background-color: #99ccff; }
  .box7 { background-color: #cc99ff; }
  .box8 { background-color: #ff99cc; }
  .box9 { background-color: #cccccc; }
</style>
</head>
<body>
<div class="container">
  <div class="box box1">1</div>
  <div class="box box2">2</div>
  <div class="box box3">3</div>
  <div class="box box4">4</div>
  <div class="box box5">5</div>
  <div class="box box6">6</div>
  <div class="box box7">7</div>
  <div class="box box8">8</div>
  <div class="box box9">9</div>
</div>
</body>
</html>

问题效果如下:

解决方案

这个是float浮动布局导致的,如果不用这个布局就不会有这个问题,我们要做的是,清楚浮动给子元素带来的影响。

我这里有个通俗的理解,所有的元素是左浮动,那么每个元素都会尽可能地去贴上一个元素的右边。比方说2会去贴1,3会去贴2。由于1的高度变化了,比2和3要长,那么4正好是可以贴上去的,所以4会贴1,然后原本4的位置被1占用了,4就只能靠右占5的位置,5就占6,以此类推。

要想解决这个问题,那就是强行让4不要去贴1的边。

所以最终的解决方案是,给受到影响的换行子元素,加上clear:left即可。

示例代码如下:

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>浮动布局示例</title>
<style>
  .container {
    width: 300px;
    height: 300px;
    border: 1px solid #ccc;
    position: relative;
  }
  .box {
    width: 100px;
    height: 100px;
    float: left;
    text-align: center;
    line-height: 100px;
  }
  .box1 { height:150px;background-color: #ff9999; }
  .box2 { background-color: #ffcc99; }
  .box3 { background-color: #ffff99; }
  .box4 { background-color: #ccff99; }
  .box5 { background-color: #99ffcc; }
  .box6 { background-color: #99ccff; }
  .box7 { background-color: #cc99ff; }
  .box8 { background-color: #ff99cc; }
  .box9 { background-color: #cccccc; }
  .box:nth-child(3n+1){
    clear: left
  }
</style>
</head>
<body>
<div class="container">
  <div class="box box1">1</div>
  <div class="box box2">2</div>
  <div class="box box3">3</div>
  <div class="box box4">4</div>
  <div class="box box5">5</div>
  <div class="box box6">6</div>
  <div class="box box7">7</div>
  <div class="box box8">8</div>
  <div class="box box9">9</div>
</div>
</body>
</html>

效果如下:

关键是要给3n+1个子元素加上清除左浮动,防止后续其他元素高度变化后,出现类似的问题。

实际案例

我开发的项目中,用到了ant-design-vue 1.7.8这个前端UI框架,里面的formModel表单,表单项用到的就是float布局。

这里有个示例代码,可以复现这个问题。

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ant Design Vue 表单示例</title>
<!-- 引入 Vue 和 Ant Design Vue 1.7.8 的 CDN -->
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.min.js"></script>
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/ant-design-vue/1.7.8/antd.min.css">
<script src="https://cdn.bootcdn.net/ajax/libs/ant-design-vue/1.7.8/antd.min.js"></script>
</head>
<body>
<div id="app">
  <a-form-model
    ref="formModel"
    :model="form"
    :rules="rules"
    layout="inline"
  >
    <a-row :gutter="24">
      <!-- 多选 Select -->
      <a-col :span="12">
        <a-form-model-item label="兴趣爱好" name="hobbies">
          <a-select
            v-model="form.hobbies"
            mode="multiple"
            placeholder="请选择你的兴趣爱好"
            allow-clear
            style="width:340px"
          >
            <a-select-option v-for="item in options" :key="item.value" :value="item.value">
              {{ item.label }}
            </a-select-option>
          </a-select>
        </a-form-model-item>
      </a-col>
      <!-- 输入框 -->
      <a-col :span="12">
        <a-form-model-item label="姓名" name="name">
          <a-input v-model="form.name" placeholder="请输入姓名"></a-input>
        </a-form-model-item>
      </a-col>
      <!-- 数字输入框 -->
      <a-col :span="12">
        <a-form-model-item label="年龄" name="age">
          <a-input-number v-model="form.age" placeholder="请输入年龄" style="width: 100%;"></a-input-number>
        </a-form-model-item>
      </a-col>
      <!-- 单选框组 -->
      <a-col :span="12">
        <a-form-model-item label="性别" name="gender">
          <a-radio-group v-model="form.gender">
            <a-radio value="male">男</a-radio>
            <a-radio value="female">女</a-radio>
          </a-radio-group>
        </a-form-model-item>
      </a-col>
      <!-- 日期选择器 -->
      <a-col :span="12">
        <a-form-model-item label="生日" name="birthday">
          <a-input v-model="form.birthday" placeholder="选择日期"></a-input>
        </a-form-model-item>
      </a-col>
      <!-- 开关 -->
      <a-col :span="12">
        <a-form-model-item label="是否订阅" name="subscribe">
          <a-switch v-model="form.subscribe"></a-switch>
        </a-form-model-item>
      </a-col>
      <!-- 滑块 -->
      <a-col :span="12">
        <a-form-model-item label="满意度" name="satisfaction">
          <a-slider v-model="form.satisfaction"></a-slider>
        </a-form-model-item>
      </a-col>
      
      <!-- 滑块 -->
      <a-col :span="12">
        <a-form-model-item label="满意度" name="satisfaction">
          <a-slider v-model="form.satisfaction"></a-slider>
        </a-form-model-item>
      </a-col>
      <!-- 提交与重置按钮 -->
      <a-col :span="24">
        <a-form-model-item>
          <a-button type="primary" @click="handleSubmit">提交</a-button>
          <a-button style="margin-left: 10px;" @click="handleReset">重置</a-button>
        </a-form-model-item>
      </a-col>
    </a-row>
  </a-form-model>
</div>
<script>
  new Vue({
    el: '#app',
    data() {
      return {
        form: {
          hobbies: [],
          name: '',
          age: null,
          gender: '',
          birthday: null,
          subscribe: false,
          satisfaction: 0,
        },
        rules: {
          hobbies: [{ required: true, message: '请选择至少一个兴趣爱好', type: 'array' }],
          name: [{ required: true, message: '请输入姓名' }],
          age: [{ type: 'number', required: true, message: '请输入年龄' }],
          gender: [{ required: true, message: '请选择性别' }],
          birthday: [{ required: true, message: '请选择生日' }],
        },
        options: Array.from({ length: 20 }, (_, index) => ({
          value: `option-${index + 1}`,
          label: `选项 ${index + 1}`,
        })),
      };
    },
    methods: {
      handleSubmit() {
        this.$message.success('表单提交成功:' + JSON.stringify(this.form));
      },
      handleReset() {
        this.$refs.formModel.resetFields();
        this.$message.info('表单已重置');
      },
    },
  });
</script>
</body>
</html>

效果如下:

咋一看没问题,但只要你在select中多选,选择足够多,多到足以改变高度,问题就出现了。第三个表单项被挤压到了原本第四个元素的位置。

解决办法跟上面一样,设置一个选择器清除左浮动即可。

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ant Design Vue 表单示例</title>
<!-- 引入 Vue 和 Ant Design Vue 1.7.8 的 CDN -->
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.min.js"></script>
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/ant-design-vue/1.7.8/antd.min.css">
<script src="https://cdn.bootcdn.net/ajax/libs/ant-design-vue/1.7.8/antd.min.js"></script>
<style>
  .ant-row.form-row .ant-col-12:nth-child(3n){
    clear: left;
  }
</style>
</head>
<body>
<div id="app">
  <a-form-model
    ref="formModel"
    :model="form"
    :rules="rules"
    layout="inline"
  >
    <a-row :gutter="24" class="form-row">
      <!-- 多选 Select -->
      <a-col :span="12">
        <a-form-model-item label="兴趣爱好" name="hobbies">
          <a-select
            v-model="form.hobbies"
            mode="multiple"
            placeholder="请选择你的兴趣爱好"
            allow-clear
            style="width:340px"
          >
            <a-select-option v-for="item in options" :key="item.value" :value="item.value">
              {{ item.label }}
            </a-select-option>
          </a-select>
        </a-form-model-item>
      </a-col>
      <!-- 输入框 -->
      <a-col :span="12">
        <a-form-model-item label="姓名" name="name">
          <a-input v-model="form.name" placeholder="请输入姓名"></a-input>
        </a-form-model-item>
      </a-col>
      <!-- 数字输入框 -->
      <a-col :span="12">
        <a-form-model-item label="年龄" name="age">
          <a-input-number v-model="form.age" placeholder="请输入年龄" style="width: 100%;"></a-input-number>
        </a-form-model-item>
      </a-col>
      <!-- 单选框组 -->
      <a-col :span="12">
        <a-form-model-item label="性别" name="gender">
          <a-radio-group v-model="form.gender">
            <a-radio value="male">男</a-radio>
            <a-radio value="female">女</a-radio>
          </a-radio-group>
        </a-form-model-item>
      </a-col>
      <!-- 日期选择器 -->
      <a-col :span="12">
        <a-form-model-item label="生日" name="birthday">
          <a-input v-model="form.birthday" placeholder="选择日期"></a-input>
        </a-form-model-item>
      </a-col>
      <!-- 开关 -->
      <a-col :span="12">
        <a-form-model-item label="是否订阅" name="subscribe">
          <a-switch v-model="form.subscribe"></a-switch>
        </a-form-model-item>
      </a-col>
      <!-- 滑块 -->
      <a-col :span="12">
        <a-form-model-item label="满意度" name="satisfaction">
          <a-slider v-model="form.satisfaction"></a-slider>
        </a-form-model-item>
      </a-col>
      
      <!-- 滑块 -->
      <a-col :span="12">
        <a-form-model-item label="满意度" name="satisfaction">
          <a-slider v-model="form.satisfaction"></a-slider>
        </a-form-model-item>
      </a-col>
      <!-- 提交与重置按钮 -->
      <a-col :span="24">
        <a-form-model-item>
          <a-button type="primary" @click="handleSubmit">提交</a-button>
          <a-button style="margin-left: 10px;" @click="handleReset">重置</a-button>
        </a-form-model-item>
      </a-col>
    </a-row>
  </a-form-model>
</div>
<script>
  new Vue({
    el: '#app',
    data() {
      return {
        form: {
          hobbies: [],
          name: '',
          age: null,
          gender: '',
          birthday: null,
          subscribe: false,
          satisfaction: 0,
        },
        rules: {
          hobbies: [{ required: true, message: '请选择至少一个兴趣爱好', type: 'array' }],
          name: [{ required: true, message: '请输入姓名' }],
          age: [{ type: 'number', required: true, message: '请输入年龄' }],
          gender: [{ required: true, message: '请选择性别' }],
          birthday: [{ required: true, message: '请选择生日' }],
        },
        options: Array.from({ length: 20 }, (_, index) => ({
          value: `option-${index + 1}`,
          label: `选项 ${index + 1}`,
        })),
      };
    },
    methods: {
      handleSubmit() {
        this.$message.success('表单提交成功:' + JSON.stringify(this.form));
      },
      handleReset() {
        this.$refs.formModel.resetFields();
        this.$message.info('表单已重置');
      },
    },
  });
</script>
</body>
</html>

这样就改好了!

总结

这类问题的解决办法,最好是从源头解决。我曾经尝试过换布局,但这样改动量会比较大,不是很合适。关于float清除浮动的实际含义,可以看看张鑫旭大佬的文章,我放到参考链接里面了。

目录
相关文章
|
18小时前
|
JavaScript 前端开发 开发者
JavaScript实战:探究数组循环截取的实现技巧
本文介绍如何使用JavaScript实现数组的循环截取功能,适用于视频列表轮播等场景。通过for循环和slice两种方法,每隔固定时间截取4个元素并循环滚动索引,保证无缝衔接。详细讲解实现思路、代码解析及两种方法的优劣对比,帮助开发者提升实际编码能力。
14 1
|
18小时前
|
缓存 JavaScript 前端开发
Vue的生命周期详解及业务场景应用
本文详细介绍Vue.js的生命周期概念及各阶段钩子函数的作用,结合实际业务场景讲解如何合理使用created、mounted、updated等钩子进行数据初始化、DOM操作、资源清理等,帮助开发者提升组件管理能力与代码性能。
13 0
Vue的生命周期详解及业务场景应用
|
18小时前
|
安全 网络安全 开发工具
解决 Host key verification failed 报错的三种方法(含 SSH 安全建议)
本文由喵喵侠撰写,详解Git提交时常见报错“Host key verification failed”的成因与解决方案。当SSH密钥变更或服务器重装系统时,本地记录的主机密钥会失效,导致连接被拒。文章提供三种解决方法:使用`ssh-keygen -R`删除旧密钥、手动编辑`known_hosts`文件,或临时禁用严格检查(不推荐生产环境)。同时强调安全建议:确认服务器状态、核对密钥指纹、启用`VerifyHostKeyDNS`防范中间人攻击。帮助开发者快速定位问题,保障代码提交顺畅。
20 0
|
18小时前
|
自然语言处理 前端开发 Windows
推荐一款很好用的VSCode变量翻译插件
本文介绍VSCode插件“var-translate-en”,可一键将中文翻译为英文并转为小驼峰等命名格式,支持百度、腾讯、阿里等翻译服务。通过简单配置与快捷键设置,提升变量命名效率,解决命名难题。
21 0
|
18小时前
|
JavaScript 搜索推荐 测试技术
为什么要使用 Git 作为代码版本管理工具?
本文记录了一次Vue2项目打包卡在最后一步的排查过程。作者发现测试环境打包被阻塞,通过回退配置定位到`code-inspector-plugin`插件及`.env.test`中`NODE_ENV=production`的错误配置,修正后问题解决。
12 1
|
18小时前
|
JavaScript 前端开发 开发者
如何防止Vue页面局部元素滚动时,页面整体滚动?
本文由喵喵侠分享,针对Vue中列表组件滚动时触发页面整体滚动的问题,分析了`e.stopPropagation()`无法阻止默认行为的原因,并提出使用`e.preventDefault()`来禁止浏览器默认滚动。同时扩展介绍了该方法在阻止表单提交、链接跳转等场景的应用,以及与`return false`的区别,帮助开发者更精准地控制事件行为。
14 1
|
19小时前
|
前端开发 JavaScript API
CSS弹性盒子布局图标的展示效果优化技巧
本文介绍前端开发中一个弹性布局的优化问题:小屏幕下图标错位影响美观。通过媒体查询和ResizeObserver API两种方案,实现容器宽度不足时隐藏图标,提升响应式体验。重点讲解ResizeObserver对单个元素的精准监听与Vue中的实践应用,兼顾用户体验与代码性能。
15 0
CSS弹性盒子布局图标的展示效果优化技巧
|
18小时前
|
Web App开发 前端开发 JavaScript
一次远程会议中我用到的 Chrome DevTools 调试技巧
本文由“喵喵侠”分享三个被忽视的Chrome DevTools实用技巧:删除遮挡元素、撤销误删操作、快速搜索DOM节点。通过真实场景演示,帮助开发者提升调试效率,避免因小失误影响协作,适合前端新手和非开发人员学习掌握。
16 0
一次远程会议中我用到的 Chrome DevTools 调试技巧
|
19小时前
|
JavaScript 数据可视化 视频直播
如何手动停止 videojs 直播视频流 m3u8 请求?
在可视化大屏项目中,多个VideoJS组件播放m3u8流时,即使暂停仍持续请求,导致页面卡顿。通过监听display属性,结合`dispose()`销毁实例并重建同ID的video DOM元素,有效释放资源且保留组件结构,解决性能问题,提升用户体验。
30 0
|
18小时前
|
开发工具 git 开发者
为什么要使用 Git 作为代码版本管理工具?
本文由喵喵侠分享,对比传统SVN与主流Git的优劣。SVN集中管理适合小团队,但离线受限、分支繁琐;Git分布式架构高效灵活,支持离线操作与强大分支管理,虽学习成本较高,但已成为现代开发标配。未来将向AI集成、智能协作与图形化界面发展,是开发者必备技能。
16 0
为什么要使用 Git 作为代码版本管理工具?