如何在JavaScript中获取动态内容宽度并实现CSS width过渡?
问题:JavaScript动态调整元素宽度并实现CSS过渡失效问题
我在JavaScript中调整width属性时遇到问题:想要调整.btnFocus类的宽度以适配子节点(图片与文本),同时让CSS过渡正常生效,但获取.btnFocus的宽度时总是出问题——要么拿到子节点添加前的宽度值,要么直接触发代码报错。
预期实现流程:
- 初始状态下,
.hotspot元素无任何子节点;用户点击该元素时,将data-content中的内容(图片+文本)添加到.hotspot内部。 - JavaScript获取添加内容后的
.hotspot元素宽度。 - 通过CSS过渡效果让
.hotspot的宽度平滑过渡到步骤2获取的宽度值。
现有代码
treat.html
<div id="clickBox"> <a class="hotspot" data-content="Forehead <img src='/images/common file/logo2.png' class='hotLogo'> <span class='fontRed'> Deep</span>"></a> <a class="hotspot" data-content="Chin <img src='/images/common file/logo_purple.png' class='hotLogo'> <span class='fontPurple'> Volume</span>"></a> </div>
treat.css
#clickBox .hotspot { position: absolute; width: 1rem; height: 1rem; background-image: url("/images/2.treat/rect.png"); background-size: cover; border-radius: 50%; padding: 0.3rem; opacity: 0.5; box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.16); cursor: pointer; transition: 1s ease-in-out; transition-property: width, border-radius; } .hotspot>.hotLogo { width: 55px; } #clickBox .hotspot.btnFocus { /* width gonna be changed in js file*/ width: 1rem; height: auto; opacity: 0.8; border-radius: 15px; overflow: hidden; text-overflow: clip; white-space: nowrap; }
treat.js
let hotspot = document.querySelectorAll(".hotspot"); hotspot.forEach(function (element) { element.addEventListener("click", function () { smallBtn(element); }); }); function smallBtn(element) { if (element.classList.contains("btnFocus")) { element.innerHTML = ""; element.classList.toggle("btnFocus"); } else { hotspot.forEach(function (element) { element.innerHTML = ""; element.classList.remove("btnFocus"); }); let content = element.dataset["content"]; // cause error at below let contentWidth = content.getBoundingClientRect().width; let btnFocus = document.querySelector("btnFocus"); console.log("contentWidth : ",contentWidth); btnFocus.style.width = contentWidth; element.classList.add("btnFocus"); element.innerHTML = content; } }
我还尝试过使用element.style.setProperty("--contentWidth", contentWidth + "px")及CSS变量,但都无效。我需要正确获取contentWidth值来修改btnFocus的宽度。
问题分析
- 错误获取内容宽度:直接对
data-content的字符串调用getBoundingClientRect()会报错,因为字符串不是DOM元素,没有该方法。 - DOM操作顺序错误:在添加
.btnFocus类和内容之前就尝试获取该类元素,此时元素还未添加类,且选择器写错(缺少.),导致返回null。 - 宽度计算时机不对:必须先将内容插入DOM,才能计算元素真实渲染后的宽度。
修正方案
修改后的treat.js
let hotspot = document.querySelectorAll(".hotspot"); hotspot.forEach(function (element) { element.addEventListener("click", function () { smallBtn(element); }); }); function smallBtn(element) { if (element.classList.contains("btnFocus")) { element.innerHTML = ""; element.classList.toggle("btnFocus"); // 重置宽度为初始值,保证下次过渡正常触发 element.style.width = ""; } else { // 先重置所有hotspot的状态 hotspot.forEach(function (el) { el.innerHTML = ""; el.classList.remove("btnFocus"); el.style.width = ""; }); // 1. 先给目标元素添加类和内容 element.classList.add("btnFocus"); element.innerHTML = element.dataset.content; // 2. 等待DOM渲染完成后计算宽度 requestAnimationFrame(() => { const contentWidth = element.getBoundingClientRect().width; // 先重置宽度为初始值,触发过渡起点 element.style.width = "1rem"; // 触发重绘后再设置目标宽度,确保过渡生效 requestAnimationFrame(() => { element.style.width = `${contentWidth}px`; }); }); } }
可选CSS补充调整
为避免padding影响宽度计算,给.btnFocus添加盒模型设置:
#clickBox .hotspot.btnFocus { width: 1rem; height: auto; opacity: 0.8; border-radius: 15px; overflow: hidden; text-overflow: clip; white-space: nowrap; box-sizing: border-box; /* 添加此行 */ }
关键修正说明
- 先插入内容再计算宽度:必须将
data-content的内容插入DOM后,才能通过getBoundingClientRect()获取真实渲染宽度。 - 使用
requestAnimationFrame:确保DOM完成渲染后再计算宽度,避免拿到未渲染的错误值。 - 修复选择器错误:直接使用当前点击的
element操作,无需额外查询,更高效且避免选择器错误。 - 分步设置宽度触发过渡:先将宽度设为初始值
1rem,再设置目标宽度,确保CSS过渡能捕捉到状态变化。
内容的提问来源于stack exchange,提问作者white_letter
相关产品推荐
相关产品推荐

