CSS flex布局 列表单个元素点击 本行下插入详情独占一行

news2025/3/12 12:34:39

技术栈:Vue2 + javaScript

简介

在实际开发过程中有遇到一个场景:一个list,每行个数固定,点击单个元素后,在当前行与下一行之间插入一行元素详情,便于更直观的查看到对应的数据详情。
这种情形,在移动端比较常见,比如用户列表,点击单个列表 展示详情,可以考虑 flex 布局 + position relative 定位。

实现

思路

对于需求重点和实现拆解

  1. 列表元素:for 遍历
  2. 每行固定(3)个元素:flex布局、宽度%
  3. 详情在该元素下独占一行:for 内元素、position relative

核心代码

mock数据

// list数据
list: [
      { id: 1, name: '测试数据1', desc: '测试数据1描述测试数据1描述测试数据1描述测试数据1描述' },
      { id: 2, name: '测试数据2', desc: '测试数据2描述测试数据2描述测试数据2描述测试数据2描述' },
      { id: 3, name: '测试数据3', desc: '测试数据3描述测试数据3描述测试数据3描述测试数据3描述' },
      { id: 4, name: '测试数据4', desc: '测试数据4描述测试数据4描述测试数据4描述测试数据4描述' },
      { id: 5, name: '测试数据5', desc: '测试数据5描述测试数据5描述测试数据5描述测试数据5描述' },
      { id: 6, name: '测试数据6', desc: '测试数据6描述测试数据6描述测试数据6描述测试数据6描述' },
      { id: 7, name: '测试数据7', desc: '测试数据7描述测试数据7描述测试数据7描述测试数据7描述' },
      { id: 8, name: '测试数据8', desc: '测试数据8描述测试数据8描述测试数据8描述测试数据8描述' },
      { id: 9, name: '测试数据9', desc: '测试数据9描述测试数据9描述测试数据9描述测试数据9描述' },
      { id: 10, name: '测试数据10', desc: '测试数据10描述测试数据10描述测试数据10描述测试数据10描述' },
      { id: 11, name: '测试数据11', desc: '测试数据11描述测试数据11描述测试数据11描述测试数据11描述' }
  ],
  showDetail: false, // 是否显示详情
  detail: {}, // 详情数据

DOM结构

<!-- 列表容器 -->
<div class="container">
	<!-- 单个元素 start-->
  <div v-for="(item, index) in list" :key="index" class="item-box" @click="toggleEvent(item)">
  
    <div class="item-name">{{ item.name }}</div>
    
    <!-- 详情 start -->
     <div class="item-detail" v-if="showDetail && detail.id == item.id" :style="caculateDetailLeft(index)">
     	 <!-- 气泡三角 -->
         <div class="top-jian" :style="caculateJianLeft(index)"></div>
         <!-- 详情描述 -->
         <div>{{ detail.desc }}</div>
     </div>
     <!-- 详情 end -->
     
   </div>
   <!-- 单个元素 end -->
</div>

CSS 与 动态位移

.container {
    width: 80vw; // 列表固定宽度
    display: flex;
    gap: 16px; // 元素间距
    flex-wrap: wrap;

    .item-box {
    	// calc((父元素宽度 - 间距*(每行个数-1)) / 每行个数)
        width: calc((100% - 32px) / 3); 

        .item-detail {
            width: 80vw; // 列表固定宽度
            position: relative; 
            background: #AFF050;

            .top-jian {
                width: 20px;
                height: 20px;
                position: absolute;
                background: #AFF050;
                -webkit-transform: rotate(45deg);
                transform: rotate(45deg);
                top: -6px;
            }
        }
    }
}
 caculateDetailLeft(index) {
    return {
        // calc(calc(calc(100% + 16px) * ${index%3}) * -1)
        // calc(-1 * (100% + 间距) * ${index % 每行个数})
        left: `calc(-1 * (100% + 16px) * ${index % 3})`
    }

},
caculateJianLeft(index) {
    return {
        // calc(calc(calc((100% - 32px) / 3) * ${index%3}) + calc((100% - 32px) / 6))
        // calc((100% - 间距*2) * (${index % 每行个数} / 每行个数 + 1 / (每行个数*2)))
        left: `calc((100% - 32px) * (${index % 3} / 3 + 1 / 6))`
    }

}

效果

展开效果

完整代码

<template>
    <div class="container">
        <div v-for="(item, index) in list" :key="index" class="item-box" @click="toggleEvent(item)">
            <div class="item-name">{{ item.name }}</div>
            <div class="item-detail" v-if="showDetail && detail.id == item.id" :style="caculateDetailLeft(index)">
                <div class="top-jian" :style="caculateJianLeft(index)"></div>
                <div>
                    {{ detail.desc }}
                </div>
            </div>

        </div>
    </div>
</template>
<script>

export default {
    name: 'Test',
    data() {
        return {
            list: [
                { id: 1, name: '测试数据1', desc: '测试数据1描述测试数据1描述测试数据1描述测试数据1描述' },
                { id: 2, name: '测试数据2', desc: '测试数据2描述测试数据2描述测试数据2描述测试数据2描述' },
                { id: 3, name: '测试数据3', desc: '测试数据3描述测试数据3描述测试数据3描述测试数据3描述' },
                { id: 4, name: '测试数据4', desc: '测试数据4描述测试数据4描述测试数据4描述测试数据4描述' },
                { id: 5, name: '测试数据5', desc: '测试数据5描述测试数据5描述测试数据5描述测试数据5描述' },
                { id: 6, name: '测试数据6', desc: '测试数据6描述测试数据6描述测试数据6描述测试数据6描述' },
                { id: 7, name: '测试数据7', desc: '测试数据7描述测试数据7描述测试数据7描述测试数据7描述' },
                { id: 8, name: '测试数据8', desc: '测试数据8描述测试数据8描述测试数据8描述测试数据8描述' },
                { id: 9, name: '测试数据9', desc: '测试数据9描述测试数据9描述测试数据9描述测试数据9描述' },
                { id: 10, name: '测试数据10', desc: '测试数据10描述测试数据10描述测试数据10描述测试数据10描述' },
                { id: 11, name: '测试数据11', desc: '测试数据11描述测试数据11描述测试数据11描述测试数据11描述' }
            ],
            showDetail: false,
            detail: {},
        }
    },
    methods: {
        toggleEvent(item) {
            if (item.id == this.detail.id) {
                this.showDetail = !this.showDetail
            } else {
                this.showDetail = true
                this.detail = { ...item }
            }
        },
        caculateDetailLeft(index) {
            return {
                // calc(calc(calc(100% + 16px) * ${index%3}) * -1)
                left: `calc(-1 * (100% + 16px) * ${index % 3})`
            }

        },
        caculateJianLeft(index) {
            return {
                // calc(calc(calc((100% - 32px) / 3) * ${index%3}) + calc((100% - 32px) / 6))
                left: `calc((100% - 32px) * (${index % 3} / 3 + 1 / 6))`
            }

        }
    }
}
</script>
<style lang="scss" scoped>
.container {
    width: 80vw;
    display: flex;
    gap: 16px;
    flex-wrap: wrap;

    .item-box {
        width: calc((100% - 32px) / 3);

        .item-name {
            border: 1px solid #ccc;
            padding: 30px;
            text-align: center;
            border-radius: 15px;
        }

        .item-detail {
            width: 80vw;
            position: relative;
            background: #AFF050;
            margin-top: 16px;
            padding: 30px;
            border-radius: 15px;

            .top-jian {
                width: 20px;
                height: 20px;
                position: absolute;
                background: #AFF050;
                -webkit-transform: rotate(45deg);
                transform: rotate(45deg);
                top: -6px;
            }
        }
    }
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2299704.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

无人机航迹规划: 梦境优化算法(Dream Optimization Algorithm,DOA)求解无人机路径规划MATLAB

一、梦境优化算法 梦境优化算法&#xff08;Dream Optimization Algorithm&#xff0c;DOA&#xff09;是一种新型的元启发式算法&#xff0c;其灵感来源于人类的梦境行为。该算法结合了基础记忆策略、遗忘和补充策略以及梦境共享策略&#xff0c;通过模拟人类梦境中的部分记忆…

权限五张表

重点&#xff1a;权限五张表的设计 核心概念&#xff1a; 在权限管理系统中&#xff0c;经典的设计通常涉及五张表&#xff0c;分别是用户表、角色表、权限表、用户角色表和角色权限表。这五张表的设计可以有效地管理用户的权限&#xff0c;确保系统的安全性和灵活性。 用户&…

Docker-数据卷

1.数据卷 容器是隔离环境&#xff0c;容器内程序的文件、配置、运行时产生的容器都在容器内部&#xff0c;我们要读写容器内的文件非常不方便。大家思考几个问题&#xff1a; 如果要升级MySQL版本&#xff0c;需要销毁旧容器&#xff0c;那么数据岂不是跟着被销毁了&#xff1…

IT : 是工作還是嗜好? Delphi 30周年快乐!

又到2月14日了, 自从30多年前收到台湾宝蓝(Borland)公司一大包的3.5 磁盘片, 上面用黑色油性笔写着Delphi Beta开始, Delphi便和我的工作生涯有了密不可分的关系. 一年后Delphi大获成功, 自此对于使用Delphi的使用者来说2月14日也成了一个特殊的日子! 我清楚记得Delphi Beta使用…

DeepPose

目录 摘要 Abstract DeepPose 算法框架 损失函数 创新点 局限性 训练过程 代码 总结 摘要 DeepPose是首个将CNN应用于姿态估计任务的模型。该模型在传统姿态估计方法的基础上&#xff0c;通过端到端的方式直接从图像中回归出人体关键点的二维坐标&#xff0c;避免了…

[HarmonyOS]鸿蒙(添加服务卡片)推荐商品 修改卡片UI(内容)

什么是服务卡片 &#xff1f; 鸿蒙系统中的服务卡片&#xff08;Service Card&#xff09;就是一种轻量级的应用展示形式&#xff0c;它可以让用户在不打开完整应用的情况下&#xff0c;快速访问应用内的特定功能或信息。以下是服务卡片的几个关键点&#xff1a; 轻量级&#…

DeepSeek R1 本地部署和知识库搭建

一、本地部署 DeepSeek-R1&#xff0c;是幻方量化旗下AI公司深度求索&#xff08;DeepSeek&#xff09;研发的推理模型 。DeepSeek-R1采用强化学习进行后训练&#xff0c;旨在提升推理能力&#xff0c;尤其擅长数学、代码和自然语言推理等复杂任务 。 使用DeepSeek R1, 可以大大…

领域驱动设计叕创新,平安保险申请DDD专利

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 见下图&#xff1a; 这个名字拼得妙&#xff1a;领域驱动设计模式。 是领域驱动设计&#xff1f;还是设计模式&#xff1f;还是领域驱动设计设计模式&#xff1f;和下面这个知乎文章的…

团体程序设计天梯赛-练习集——L1-041 寻找250

前言 10分的题&#xff0c;主要的想法就一个&#xff0c;按这个想法可以出几个写法 L1-041 寻找250 对方不想和你说话&#xff0c;并向你扔了一串数…… 而你必须从这一串数字中找到“250”这个高大上的感人数字。 输入格式&#xff1a; 输入在一行中给出不知道多少个绝对值…

C#控制台大小Console.SetWindowSize函数失效解决

在使用C#修改控制台大小相关API会失效. 由于VS将控制台由命令提示符变成了终端&#xff0c;因此在设置大小时会出现问题 测试代码&#xff1a; Console.SetWindowSize(100, 50);

spring boot 对接aws 的S3 服务,实现上传和查询

1.aws S3介绍 AWS S3&#xff08;Amazon Simple Storage Service&#xff09;是亚马逊提供的一种对象存储服务&#xff0c;旨在提供可扩展、高可用性和安全的数据存储解决方案。以下是AWS S3的一些主要特点和功能&#xff1a; 1.1. 对象存储 对象存储模型&#xff1a;S3使用…

25/2/16 <算法笔记> DirectPose

DirectPose 是一种直接从图像中预测物体的 6DoF&#xff08;位姿&#xff1a;6 Degrees of Freedom&#xff09;姿态 的方法&#xff0c;包括平移和平面旋转。它在目标检测、机器人视觉、增强现实&#xff08;AR&#xff09;和自动驾驶等领域中具有广泛应用。相比于传统的位姿估…

数据结构-8.Java. 七大排序算法(下篇)

本篇博客给大家带来的是排序的知识点, 由于时间有限, 分两天来写, 下篇主要实现最后一种排序算法: 归并排序。同时把中篇剩下的快排非递归实现补上. 文章专栏: Java-数据结构 若有问题 评论区见 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是…

DeepSeek私有化部署+JAVA通过API调用离线大模型问答

在当今快速发展的数字化时代&#xff0c;企业对于高效、灵活的技术解决方案需求日益增长。DeepSeek作为一款领先的智能搜索与分析平台&#xff0c;凭借其强大的数据处理能力和精准的搜索结果&#xff0c;已经成为众多企业提升运营效率的得力助手。为了更好地满足企业对数据安全…

【吾爱出品】针对红警之类老游戏适用WIN10和11的补丁cnc-ddraw7.1汉化版

针对红警之类老游戏适用WIN10和11的补丁cnc-ddraw7.1汉化版 链接&#xff1a;https://pan.xunlei.com/s/VOJ8PZd4avMubnDzHQAeZDxWA1?pwdnjwm# 直接复制到游戏安装目录&#xff0c;保持与游戏主程序同目录下。

内容中台驱动企业数字化内容管理高效协同架构

内容概要 在数字化转型加速的背景下&#xff0c;企业对内容管理的需求从单一存储向全链路协同演进。内容中台作为核心支撑架构&#xff0c;通过统一的内容资源池与智能化管理工具&#xff0c;重塑了内容生产、存储、分发及迭代的流程。其核心价值在于打破部门壁垒&#xff0c;…

【第14章:神经符号集成与可解释AI—14.4 神经符号集成与可解释AI的未来发展趋势与挑战】

想象一下,如果AI既能像人类一样直觉感知(比如一眼认出街角的咖啡店),又能像数学家一样逻辑推理(比如计算最优路线避开拥堵),这个世界会变成什么样?这种“双脑协同”正是神经符号集成技术的终极目标。 但现实是,当前99%的AI系统要么只会“死记硬背”数据(如深度学习模…

[Spring Boot] Expense API 实现

[Spring Boot] Expense API 实现 项目地址&#xff1a;expense-api 项目简介 最近跟着视频做的一个 spring boot 的项目&#xff0c;包含了比较简单的记账功能的实现&#xff08;只限 API 部分&#xff09;&#xff0c;具体实现的功能有&#xff1a; 记账&#xff08;expen…

设置默认构建变体 Build Variant

Android Studio在打开项目时有时会把我设置好的build Variant改为默认的变体&#xff0c;没注意的话可能打完包才发现打错了&#xff0c;浪费时间。因此&#xff0c;有必要通过代码设置一个我想要的默认变体。 代码其实很简单&#xff0c;只要在变体下面加上isDefault true即可…

【大模型】DeepSeek使用与原理解析:从V3到R1

文章目录 一、引言二、使用与测评1.7大R1使用技巧2.官网实测 发展历程三、Deepseek MoE&#xff1a;专家负载均衡 &#xff08;2024年1月&#xff09;四、GRPO&#xff1a;群体相对策略优化&#xff08;DeepSeek-Math&#xff0c;2024年4月&#xff09;五、三代注意力&#xff…