别再让JS耽误你进步了。

1. 需求说明

某个阳光明媚的下午,作者正沉浸在摸鱼的氛围中,工作通讯图标闪烁-------得,来活了。设计认为两个下载按钮宽度不一致,有失美观,需要改成一样宽,宽度以最宽的那个按钮为准

页面上有两个结构相似的按钮(例如「For PC」和「For mobile device」),希望:

  • 视觉一致:两个按钮整体宽度相同;
  • 随文案变化:中英文切换时仍尽量稳定;

这不简单 js一把梭哈 十分钟搞定 不要耽误摸鱼。

2. JS 方案:测量 DOM 再写回宽度

  1. ref 指向按钮容器;
  2. nextTickquerySelectorAll('.inner-left')(或整颗 .btn-item);
  3. getBoundingClientRect().width / offsetWidth 取最大值;
  4. 通过内联 style 或 CSS 变量(如 --btn-left-width)写回;
  5. resizelocale(i18n)变化时再次同步。

脚本示意:

js 复制代码
const syncButtonWidth = async () => {
  syncedWidth.value = 0
  await nextTick()

  const nodes = containerRef.value?.querySelectorAll('.inner-left')
  if (!nodes?.length) return

  const maxW = Math.ceil(
    [...nodes].reduce((m, el) => Math.max(m, el.getBoundingClientRect().width), 0)
  )
  syncedWidth.value = maxW
}

模板与变量:

html 复制代码
<div class="btn-container" ref="containerRef" :style="buttonWidthStyle">
js 复制代码
const buttonWidthStyle = computed(() =>
  syncedWidth.value ? { '--btn-left-width': `${syncedWidth.value}px` } : {}
)

样式:

scss 复制代码
.inner-left {
  width: var(--btn-left-width, auto);
}

提交、部署、上线......

很快哈发现:改变浏览器窗口大小时,较宽的按钮会出现闪动和意外换行------不对,这里有坑。经作者抽丝剥茧(实际是问了「豆包」),遂发现:

2.2 JS 方案的问题

问题 说明
闪动 常见写法会先清空宽度再测量再写入,中间经历多帧布局,用户会看到宽度或换行突变。
resize 抖动 window.resize 高频触发时反复改 CSS 变量,易造成布局反复计算(layout thrashing)。
与换行强耦合 锁的是某一帧的像素宽;字体子像素、rem、父级 max-width 变化时,可能与真实排版不一致,出现意外换行或空白。
响应式冲突 窄屏要缩、宽屏要放时,仍按历史最大宽度锁死,往往需要更多断点与清理逻辑。

3. 纯 CSS 方案:用布局表达「同宽」

核心:不要用像素去「抄」浏览器算好的结果,而用 flex / grid 在规则上保证同宽。

3.1 H5:纵向 inline-flex + stretch

两个按钮上下排列时,用列方向的 flex ,在交叉轴(水平)上把子项拉满容器宽度;容器宽度由较宽那一行决定,两行自然同宽。

scss 复制代码
.btn-container {
  display: inline-flex;
  flex-direction: column;
  align-items: stretch;
  gap: 20px;
  max-width: calc(100vw - 40px);
  box-sizing: border-box;
}

.btn-item-wrapper {
  display: block;
  width: 100%;
}

.btn-item {
  display: flex;
  width: 100%;
  min-width: 0;
  /* 结构:inner-left | divider | inner-right */
}

原理简述:

  • 竖排时交叉轴为水平方向,stretch 使每个 wrapper 与容器同宽;
  • inline-flex 使容器水平方向收缩为「包住这一列」的宽度,该宽度由较宽的一行决定;
  • 因此两行按钮整体同宽

行内左侧文案区可用 flex: 1; min-width: 0 占据除竖线、右侧区域外的空间。文案可选用:

scss 复制代码
span {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

避免窄屏异常折行;过长用省略号,比锁死像素宽更稳。


3.2 PC:横向 grid 两列等分 + max-content

两个按钮左右并排时,用 1fr 1fr 保证两列始终等宽;用 width: max-content 让整行宽度贴近内容需求,max-width: 100% 防止超出视口。

scss 复制代码
.btn-container {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  width: max-content;
  max-width: 100%;
  box-sizing: border-box;
}

.btn-item {
  width: 100%;
  min-width: 0;
  display: flex;
}

.inner-left {
  flex: 1;
  min-width: 0;
  justify-content: center;
}

width: max-contentmax-width: 100% 的作用:

  • max-content:容器宽度按内容所需的「合适」宽度收缩,不会无故拉满整屏;
  • max-width: 100%:再宽也不能超过父级,窄视口时整行被压缩,两列仍等分

4. 方案对照小结

维度 JS 量宽 纯 CSS(flex / grid)
实现复杂度 ref、nextTick、监听较多 主要在样式
resize / 语言切换 易闪、多帧布局 随排版一次计算
与换行 固定像素易不一致 nowrap / ellipsis 或自然换行由 CSS 统一
可维护性 样式与脚本双处修改 改样式即改布局

完美!!!

相关推荐
玩嵌入式的菜鸡6 小时前
网页访问单片机设备---基于mqtt
前端·javascript·css
阿丰资源9 小时前
SpringBoot+Vue实战:打造企业级在线文档管理系统
vue.js·spring boot·后端
忆往wu前10 小时前
从0到1一步步拆解搭建,梳理一个 Vue3 简易图书后台全开发流程
前端·javascript·vue.js
光影少年11 小时前
大屏页面,一次多个请求,请求加密导致 点击 全局时间选择器 时出现卡顿咋解决(面板收起会延迟1~2秒)
前端·javascript·vue.js·学习·前端框架·echarts·reactjs
Mr.mjw11 小时前
vue中封装一个环形进度条组件,根据外部盒子大小自适应变化
前端·javascript·vue.js
唯火锅不可辜负11 小时前
uniapp开发公众号订阅功能踩坑小记
前端·vue.js
像我这样帅的人丶你还12 小时前
前端监控体系与实践(二):全局监控
前端·javascript·vue.js
前端那点事12 小时前
Vue3 超全复盘!30+前端高频核心知识点(开发+面试全覆盖)
前端·vue.js
FlyWIHTSKY13 小时前
Vue 3 中 RouteRecord 详解(Vue Router 4)
前端·javascript·vue.js
@菜菜_达14 小时前
Vue生命周期
前端·javascript·vue.js