设置text-overflow: ellipsis后引起的文本对齐问题

本文探讨了CSS中inline-block元素的对齐问题,通过添加vertical-align属性解决左右span元素不对齐的现象,并解释了基线对齐原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

<p>
  <span class="left">Hello Hello Hello</span>
  <span class="right">xhaha</span>
</p>
p{
  width: 40%;
  margin: 20px auto;
  font-size: 50px;
}
span{
  display: inline-block;
}
.left{
  width:40%;
  text-overflow: ellipsis;
  white-space: nowrap;
  overflow: hidden;
}
.right{
  /* overflow: hidden; */
}

按以上代码最后得到的显示效果是,span.left和span.right没有对齐。右边的会沉下去点,这个在demo里面可以看到。

然后我就想这是什么原因造成的,在调试器里勾选掉.left的overflow: hidden后,就显示正常了(当然,省略号儿也没了),然后我就捉摸着这是不是BFC的问题,因为平时自己清除浮动什么的,都喜欢用overflow:hidden来触发BFC,以便包裹元素的来着。当然了,给.right设置overflow: hidden或者float: right之后,也确实会显示正常(float: right会让文字右浮动,不过对齐的效果确实是达到了),之后我就在BFC的问题上纠结了好久,因为MDN上说了,inline-block元素本身就是会触发BFC的,那么前面所说的和BFC有关,就不那么准确了。

后来在stackoverflow上得到了答案,对span加上一个vertical-align: top然后就会对齐了。

至于原因,是因为inline-block元素默认的对齐方式是基线对齐,那么基线是什么呢? 
如果一个inline-block盒子是空的,或者说他的overflow属性不为visible, 那么他的基线就是其下边距边缘,
否则的话,就是其内部最后一个内联元素的基线(文字就是内联元素咯。。)

如下所示:

这里写图片描述

那么,span.left的基线就是那个背景色的最下边,而右边span.right的基线,就是字符x的底部,基线对齐的意思,就是这两条线是在同一水平线上的,所以,右边的元素为了对齐,就要往下沉咯。现在我们目测的话,也是这两条线貌似也确实是在一条水平线上的。

所以,设置了vertical-align: top之后,改变了其默认对齐方式,所以就对齐咯。

然后使用右浮动之后,因为浮动会使盒子的display属性变为block,所以就不是inline-block元素,自然就不会受到前面的规则的影响了。

之后是使用overflow: hidden,这个属性使得inline-block元素的基线发生了改变,变得和左边元素一样,所以也能对齐。

<template> <view class="container"> <!-- 搜索表单区域 --> <view class="search-card"> <view class="form-group title-group"> <text class="form-label">问卷标题:</text> <input v-model="formData.dcWjTitle" placeholder="请输入问卷标题" class="form-input title-input" /> </view> <view class="form-row"> <!-- 被测评人选择器 --> <view class="form-group form-group-half"> <text class="form-label">被测评人:</text> <view class="search-select-container"> <view class="input-container" @click="showBdrList = true"> <!-- 已选项显示 --> <view v-if="formData.dcId.length > 0" class="selected-display"> {{ selectedBdrLabel }} </view> <!-- 占位符 --> <text v-else-if="showBdrPlaceholder" class="placeholder-text"> 请选择被测评人 </text> <!-- 下拉箭头 --> <view class="dropdown-icon"> <text>▼</text> </view> </view> <!-- 下拉列表 --> <view v-if="showBdrList" class="dropdown-list"> <view class="search-box"> <input v-model="bdrSearchKeyword" placeholder="搜索被测评人" class="search-input" @input="filterBdrOptions" /> </view> <scroll-view scroll-y="true" class="dropdown-scroll"> <view v-for="(item, index) in filteredBdrOptions" :key="index" class="dropdown-item" :class="{ 'selected': formData.dcId[0] === item.value }" @click="handleBdrSelect(item)" > {{ item.label }} </view> <view v-if="filteredBdrOptions.length === 0" class="empty-option"> 无匹配结果 </view> </scroll-view> </view> <!-- 遮罩层 --> <view v-if="showBdrList" class="dropdown-mask" @click="showBdrList = false" ></view> </view> </view> <!-- 人员部门 --> <view class="form-group form-group-half"> <text class="form-label">人员部门:</text> <input v-model="formData.dcDept" placeholder="请输入部门" class="form-input" /> </view> </view> <!-- 提交状态和按钮组 --> <view class="form-row-bottom"> <!-- 提交状态 --> <view class="state-group"> <text class="form-label">提交状态:</text> <view class="search-select-container"> <view class="state-select-box" @click="toggleStateList"> <text class="selected-state"> {{ getStateLabel(formData.state) }} </text> <view class="dropdown-icon"> <text>▼</text> </view> </view> <!-- 状态下拉列表 --> <view v-if="showStateList" class="dropdown-list state-dropdown"> <view v-for="(state, index) in stateOptions" :key="index" class="dropdown-item" :class="{ 'selected': formData.state === state.value }" @click="handleStateSelect(state)" > {{ state.label }} </view> </view> <!-- 遮罩层 --> <view v-if="showStateList" class="dropdown-mask" @click="showStateList = false" ></view> </view> </view> <!-- 按钮组 --> <view class="button-group"> <button class="search-button" @click="handleSearch">搜索</button> <button class="reset-button" @click="handleReset">重置</button> </view> </view> </view> <!-- 数据显示区域 --> <view class="data-card"> <view class="card-header"> <button class="refresh-button" @click="refreshData">刷新数据</button> </view> <!-- 加载状态 --> <view v-if="loading" class="loading-container"> <view class="loading-spinner"></view> <text class="loading-text">加载中...</text> </view> <!-- 数据展示 --> <view v-else> <view v-for="(item, index) in surveyList" :key="index" class="data-card-item" > <view class="card-header-section"> <view class="card-title">{{ item.dcWjTitle }}</view> </view> <view class="card-body-section"> <view class="card-row"> <text class="card-label">被测评人:</text> <text class="card-value">{{ item.dcName }}</text> </view> <view class="card-row"> <text class="card-label">部门:</text> <text class="card-value">{{ item.dcDept }}</text> </view> <view class="card-row"> <text class="card-label">创建时间:</text> <text class="card-value">{{ item.createTime }}</text> </view> <view class="card-row"> <text class="card-label">提交时间:</text> <text class="card-value">{{ item.updateTime || '-' }}</text> </view> </view> <view class="card-footer-section"> <view class="status-container"> <view :class="[ 'status-tag', item.state === '1' ? 'status-submitted' : 'status-not-submitted' ]" > {{ item.state === '1' ? '已提交' : '未提交' }} </view> <view class="score">总分: {{ item.score || '0' }}</view> </view> <button class="view-button" @click="handleView(item)">编辑/查看</button> </view> </view> <!-- 空数据提示 --> <view v-if="surveyList.length === 0" class="empty"> <text>暂无数据</text> </view> </view> <!-- 分页控件 --> <view v-if="surveyList.length > 0" class="pagination-container"> <picker mode="selector" :range="[5, 10, 20, 50]" :value="[5, 10, 20, 50].indexOf(pagination.size)" @change="handleSizeChange" class="page-size-picker" > <view class="picker"> 每页 {{ pagination.size }} 条 </view> </picker> <view class="pagination-buttons"> <button :disabled="pagination.current === 1" @click="handlePageChange(pagination.current - 1)" class="page-button prev-button" > 上一页 </button> <text class="page-info"> 第 {{ pagination.current }} 页 / 共 {{ Math.ceil(pagination.total / pagination.size) }} 页 </text> <button :disabled="pagination.current >= Math.ceil(pagination.total / pagination.size)" @click="handlePageChange(pagination.current + 1)" class="page-button next-button" > 下一页 </button> </view> <text class="total-records">共 {{ pagination.total }} 条记录</text> </view> </view> </view> </template> <style scoped> .container { padding: 20rpx; background-color: #f5f5f5; min-height: 100vh; } /* 卷标题*/ .title-group { display: flex; align-items: center; gap: 20rpx; } /* 表单布局优化 */ .form-row { display: flex; flex-wrap: wrap; gap: 20rpx; margin-bottom: 20rpx; } .form-group { margin-bottom: 25rpx; width: 100%; } .form-group-half { flex: 1; min-width: 300rpx; } .form-label { font-size: 28rpx; color: #606266; font-weight: 500; white-space: nowrap; flex-shrink: 0; /* 防止标签被压缩 */ } .title-input { flex: 1; /* 输入框占据剩余空间 */ min-width: 0; /* 防止溢出 */ } .form-input { width: 100%; height: 80rpx; padding: 0 20rpx; border: 1px solid #dcdfe6; border-radius: 8rpx; font-size: 28rpx; background-color: #fff; box-sizing: border-box; } /* 搜索卡片优化 */ .search-card { background: #fff; border-radius: 16rpx; padding: 25rpx; margin-bottom: 25rpx; box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05); } .button-group { display: flex; gap: 15rpx; flex-shrink: 0; } .search-button, .reset-button { width: 150rpx; height: 80rpx; line-height: 80rpx; font-size: 28rpx; border-radius: 8rpx; text-align: center; border: none; padding: 0; margin: 0; box-sizing: border-box; } /* 按钮悬停效果 */ .search-button:hover, .reset-button:hover { opacity: 0.9; transform: translateY(-2rpx); } .search-button:active, .reset-button:active { opacity: 1; transform: translateY(0); } .search-button { background-color: #409eff; color: #fff; } .reset-button { background-color: #f5f7fa; color: #606266; border: 1px solid #dcdfe6; } /* 被测评人选择器优化 */ .search-select-container { position: relative; width: 100%; } .input-container { position: relative; width: 100%; height: 80rpx; padding: 0 60rpx 0 20rpx; border: 1px solid #dcdfe6; border-radius: 8rpx; background-color: #fff; display: flex; align-items: center; box-sizing: border-box; } .selected-display { color: #303133; font-size: 28rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; } .placeholder-text { color: #c0c4cc; font-size: 28rpx; flex: 1; } .dropdown-icon { position: absolute; right: 20rpx; top: 50%; transform: translateY(-50%); font-size: 24rpx; color: #606266; } .dropdown-list { position: absolute; top: 100%; left: 0; right: 0; max-height: 400rpx; background: #fff; border: 1px solid #dcdfe6; border-radius: 8rpx; z-index: 1000; box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1); margin-top: 8rpx; } .search-box { padding: 15rpx; border-bottom: 1px solid #f0f2f5; } .search-input { width: 100%; height: 70rpx; padding: 0 20rpx; border: 1px solid #dcdfe6; border-radius: 8rpx; font-size: 28rpx; background-color: #f5f7fa; box-sizing: border-box; } .dropdown-scroll { max-height: 300rpx; } .dropdown-item { padding: 20rpx; font-size: 28rpx; color: #333; border-bottom: 1px solid #f0f2f5; } .dropdown-item.selected { background-color: #f5f7fa; color: #409eff; font-weight: 500; } .dropdown-item:last-child { border-bottom: none; } .dropdown-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: transparent; z-index: 999; } .empty-option { padding: 20rpx; text-align: center; color: #999; font-size: 28rpx; } /* 状态选择器优化 */ .state-select-box { position: relative; width: 100%; height: 80rpx; padding: 0 60rpx 0 20rpx; border: 1px solid #dcdfe6; border-radius: 8rpx; font-size: 28rpx; background-color: #fff; display: flex; align-items: center; box-sizing: border-box; } .selected-state { color: #303133; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .state-dropdown { max-height: 300rpx; } /* 数据卡片优化 */ .data-card { background: #fff; border-radius: 16rpx; padding: 25rpx; box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05); } .card-header { margin-bottom: 25rpx; } .refresh-button { background-color: #409eff; color: #fff; height: 70rpx; line-height: 70rpx; font-size: 28rpx; border-radius: 8rpx; border: none; } .data-card-item { background: #fff; border-radius: 12rpx; padding: 25rpx; margin-bottom: 25rpx; box-shadow: 0 2rpx 6rpx rgba(0,0,0,0.03); border: 1px solid #ebeef5; } .card-header-section { padding-bottom: 15rpx; border-bottom: 1px solid #f0f2f5; margin-bottom: 15rpx; } .card-title { font-size: 32rpx; font-weight: bold; color: #303133; line-height: 1.4; word-break: break-word; } .card-body-section { margin-bottom: 15rpx; } .card-row { display: flex; margin-bottom: 12rpx; font-size: 28rpx; } .card-label { color: #606266; width: 150rpx; flex-shrink: 0; } .card-value { color: #303133; flex: 1; word-break: break-word; } .card-footer-section { display: flex; justify-content: space-between; align-items: center; padding-top: 15rpx; border-top: 1px solid #f0f2f5; } .status-container { display: flex; align-items: center; gap: 15rpx; } .status-tag { padding: 6rpx 18rpx; border-radius: 40rpx; font-size: 24rpx; font-weight: 500; } .status-submitted { background-color: #f0f9eb; color: #67c23a; border: 1px solid #e1f3d8; } .status-not-submitted { background-color: #fef0f0; color: #f56c6c; border: 1px solid #fde2e2; } .score { font-size: 28rpx; color: #e6a23c; font-weight: 500; } .view-button { background-color: #409eff; color: #fff; height: 60rpx; line-height: 60rpx; padding: 0 25rpx; font-size: 26rpx; border-radius: 40rpx; border: none; } /* 分页样式优化 */ .pagination-container { margin-top: 30rpx; display: flex; flex-direction: column; align-items: center; gap: 15rpx; } .page-size-picker { width: 200rpx; height: 60rpx; border: 1px solid #dcdfe6; border-radius: 8rpx; text-align: center; line-height: 60rpx; font-size: 26rpx; background: #fff; } .pagination-buttons { display: flex; align-items: center; gap: 15rpx; } .page-button { height: 60rpx; line-height: 60rpx; padding: 0 25rpx; font-size: 26rpx; border-radius: 8rpx; background-color: #f5f7fa; color: #606266; border: 1px solid #dcdfe6; } .page-button:disabled { opacity: 0.5; background-color: #fafafa; } .page-info { font-size: 26rpx; color: #606266; } .total-records { font-size: 26rpx; color: #909399; } /* 加载状态样式 */ .loading-container { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 80rpx 0; } .loading-spinner { width: 70rpx; height: 70rpx; border: 6rpx solid #f3f3f3; border-top: 6rpx solid #3498db; border-radius: 50%; animation: spin 1s linear infinite; margin-bottom: 25rpx; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .loading-text { color: #666; font-size: 28rpx; } /* 空数据提示 */ .empty { text-align: center; padding: 80rpx 0; color: #999; font-size: 30rpx; } .form-row-bottom { display: flex; align-items: center; justify-content: space-between; gap: 20rpx; margin-top: 10rpx; } .state-group { flex: 1; display: flex; align-items: center; gap: 15rpx; } </style> 我想要编辑/查看按钮靠右
最新发布
07-26
<template> <view> <view style="position: relative; width: 100%; display: flex; align-items: flex-start;" v-if="isHide"> <view class="dt-content" :style="'-webkit-line-clamp:'+line"> <text class="content"> <slot>{{ dt ? dt : '' }}</slot> </text> </view> <view class="button-show" @tap="isHide = false" v-if="enableButton&&lines>line"> <text style="color: #69bde8">{{ expandText }}</text> </view> </view> <view v-else> <view> <text class="content"> <slot>{{ dt ? dt : '' }}</slot> </text> </view> <view class="fold-hint" v-if="foldHint"> <view @tap="isHide = true"> {{ foldHint }} </view> </view> </view> <view> <text class="placeholder"> {{ placeholder }} </text> </view> </view> </template> <script> export default { data() { return { // 是否隐藏多余行。初始状态不隐藏 isHide: true, // 全量所占文本高度 textHeight: 0, // 单行文本所占高度 lineHeight: 1, // 占位文本 placeholder: '占位' }; }, props: { // 展示多少行 line: { type: [Number, String], default: 1 }, // 文本 dt: { type: [String], default: '' }, enableButton: { type: Boolean, default: true }, // 自定义展开提示 expandText: { type: String, default: "展开" }, // 自定义收起提示 foldHint: { type: String, default: "收起" } }, watch:{ dt(){ let that = this setTimeout(() => { let query = uni.createSelectorQuery().in(that); // 获取所有文本html中的高度 query.select('.content').boundingClientRect(data => { that.textHeight = data.height }).exec(); }, 100) } }, mounted() { if (this.enableButton) { let query = uni.createSelectorQuery().in(this); // 获取所有文本html中的高度 query.select('.content').boundingClientRect(data => { this.textHeight = data.height }).exec(); // 通过占位元素获取单行文本的高度 query.select('.placeholder').boundingClientRect(data => { this.lineHeight = data.height }).exec(); } // 获取单行文本高度后,置空占位元素,使其释放占位 this.placeholder = '' }, computed: { // 全文本所占总行数 lines() { if (!this.enableButton) { return this.line } return Math.floor(this.textHeight > 0 && this.lineHeight > 0 ? this.textHeight / this.lineHeight : 0) } } } </script> <style scoped> .dt-content { min-width: 0; /* 关键:避免 flex 子项溢出 */ overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-box-orient: vertical; } .button-show { z-index: 1; min-width: 80rpx; text-align: right; } .fold-hint { color: #69bde8; text-align: right; } </style>如何解决换行展开和收起不固定在右侧问题
06-30
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值