打字机效果导致下方容器尺寸动态变化问题求助
解决打字机效果导致下方容器尺寸变化的问题
我实现了一个打字机效果,文本会逐字“键入”屏幕、回退,再切换显示不同文本。效果运行正常,但会导致其下方所有容器的尺寸随打字机文本容器一同变化。我尝试将打字机部分包裹在独立容器中并设置宽度,但后续容器仍会随之resize。相关代码如下:
原HTML代码
<link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link href="https://fonts.googleapis.com/css2?family=Hind+Madurai:wght@300&family=Poppins:ital,wght@0,600;1,600&family=Signika:wght@300;400&display=swap" rel="stylesheet" /> <div class="skip-link"> <a class="visually-hidden" href="#main-content">Skip to Main Content</a> </div> <header> <h2>Name</h2> <nav> <ul> <li><a href="#">Welcome</a></li> <li><a href="#">Selected Work</a></li> <li><a href="#">Resume</a></li> </ul> </nav> </header> <main id="main-content" class="container"> <div class="content"> <div class="typewriter-container"> <h1> I'm a <span class="typewriter"></span ><span class="typewriter-bar"></span> </h1> </div> <h2>Hi there!</h2> </div> </main> <footer></footer>
原CSS代码
html { scroll-behavior: smooth; } body { margin: 0; padding: 0; font-family: 'Hind Madurai', Arial, Helvetica, sans-serif; } h1 { font-family: 'Poppins', Arial, Helvetica, sans-serif; font-size: 10vh; line-height: 1; } h2 { font-family: 'Signika', Arial, Helvetica, sans-serif; } header { background-color: white; color: #222; padding: 20px; display: flex; justify-content: space-between; align-items: center; } header h2 { margin: 0; font-weight: 300; } nav ul { list-style: none; padding: 0; margin: 0; text-align: right; } nav li { display: inline-block; margin-right: 20px; } nav a { text-decoration: none; color: #222; transition: color 0.3s ease; } nav a:hover { color: #3ca6a6; text-decoration: underline; } @media only screen and (max-width: 600px) { header { flex-direction: column; text-align: center; } nav { margin-top: 10px; } nav ul { flex-direction: column; text-align: center; } nav li { display: block; margin: 10px 0; } } .container { display: flex; flex-direction: column; justify-content: center; align-items: center; min-height: 100vh; position: relative; margin: 0; padding: 0; } .content { flex: 1; } footer { background-color: #026773; color: white; text-align: center; padding: 10px; position: relative; z-index: 1; margin-top: auto; } footer::before { content: ""; position: absolute; bottom: 100%; left: 0; right: 0; height: 400px; background: url(imgs/layered-peaks-haikei.svg) repeat-x; z-index: 2; } .visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; } .skip-link { position: fixed; top: 0; left: 0; background: #fff; padding: 10px; z-index: 999; } .typewriter { overflow: hidden; max-width: 800px; margin: 0 auto; } .typewriter-bar { border-right: 1px solid #222; }
原JS代码
document.addEventListener("DOMContentLoaded", function () { var phrases = [ "phrase 1", "phrase 2", "phrase 3", "phrase 4", ]; var speed = 50; var flickerSpeed = 600; var pauseBetweenPhrases = 2000; var container = document.querySelector(".typewriter"); var bar = document.querySelector(".typewriter-bar"); var currentPhraseIndex = 0; var charIndex = 0; function typeWriter() { if (charIndex < phrases[currentPhraseIndex].length) { container.innerHTML += phrases[currentPhraseIndex].charAt(charIndex); charIndex++; setTimeout(typeWriter, speed); } else { setTimeout(function () { backspace(); }, pauseBetweenPhrases); } } function backspace() { if (charIndex >= 0) { container.innerHTML = phrases[currentPhraseIndex].substring( 0, charIndex ); charIndex--; setTimeout(backspace, speed); } else { currentPhraseIndex = (currentPhraseIndex + 1) % phrases.length; setTimeout(typeWriter, speed); } } function flicker() { bar.style.visibility = bar.style.visibility === "visible" ? "hidden" : "visible"; setTimeout(flicker, flickerSpeed); } flicker(); typeWriter(); // Start the typewriter effect });
问题核心
打字机容器的宽度随内容动态变化,导致父元素和下方元素的布局被拉扯。即使包裹了容器,若未设置固定宽度/最小宽度,容器仍会自适应文本长度,引发布局波动。
解决方案
1. 给打字机容器设置固定宽度
先找到所有短语中最长的那一句,根据字体大小计算其实际宽度,给.typewriter-container设置固定宽度,确保容器尺寸稳定:
.typewriter-container { /* 示例值,根据你的最长短语实际宽度调整 */ width: 450px; text-align: center; } .typewriter { display: inline-block; overflow: hidden; white-space: nowrap; width: 100%; /* 继承父容器的固定宽度 */ vertical-align: bottom; /* 确保光标和文本对齐 */ }
2. 用CSS伪元素替代JS光标动画
移除独立的.typewriter-bar元素,改用伪元素实现光标,减少JS操作同时避免光标错位:
<!-- 修改后的h1结构 --> <h1> I'm a <span class="typewriter"></span> </h1>
.typewriter::after { content: ""; position: absolute; right: 0; top: 0; height: 100%; width: 1px; background-color: #222; animation: flicker 0.6s infinite alternate; } @keyframes flicker { 0% { opacity: 1; } 100% { opacity: 0; } } /* 给.typewriter加上相对定位,让伪元素基于它定位 */ .typewriter { position: relative; display: inline-block; overflow: hidden; white-space: nowrap; width: 100%; vertical-align: bottom; }
同时删除JS中的flicker函数及调用,用CSS动画替代。
3. 稳定父容器布局
给.content设置最小高度,避免打字机容器的微小变化影响整体布局:
.content { flex: 1; min-height: 250px; /* 根据页面内容调整 */ text-align: center; /* 可选,让内容居中更美观 */ }
修改后的完整代码
HTML
<link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link href="https://fonts.googleapis.com/css2?family=Hind+Madurai:wght@300&family=Poppins:ital,wght@0,600;1,600&family=Signika:wght@300;400&display=swap" rel="stylesheet" /> <div class="skip-link"> <a class="visually-hidden" href="#main-content">Skip to Main Content</a> </div> <header> <h2>Name</h2> <nav> <ul> <li><a href="#">Welcome</a></li> <li><a href="#">Selected Work</a></li> <li><a href="#">Resume</a></li> </ul> </nav> </header> <main id="main-content" class="container"> <div class="content"> <div class="typewriter-container"> <h1> I'm a <span class="typewriter"></span> </h1> </div> <h2>Hi there!</h2> </div> </main> <footer></footer>
CSS
html { scroll-behavior: smooth; } body { margin: 0; padding: 0; font-family: 'Hind Madurai', Arial, Helvetica, sans-serif; } h1 { font-family: 'Poppins', Arial, Helvetica, sans-serif; font-size: 10vh; line-height: 1; } h2 { font-family: 'Signika', Arial, Helvetica, sans-serif; } header { background-color: white; color: #222; padding: 20px; display: flex; justify-content: space-between; align-items: center; } header h2 { margin: 0; font-weight: 300; } nav ul { list-style: none; padding: 0; margin: 0; text-align: right; } nav li { display: inline-block; margin-right: 20px; } nav a { text-decoration: none; color: #222; transition: color 0.3s ease; } nav a:hover { color: #3ca6a6; text-decoration: underline; } @media only screen and (max-width: 600px) { header { flex-direction: column; text-align: center; } nav { margin-top: 10px; } nav ul { flex-direction: column; text-align: center; } nav li { display: block; margin: 10px 0; } } .container { display: flex; flex-direction: column; justify-content: center; align-items: center; min-height: 100vh; position: relative; margin: 0; padding: 0; } .content { flex: 1; min-height: 250px; text-align: center; } footer { background-color: #026773; color: white; text-align: center; padding: 10px; position: relative; z-index: 1; margin-top: auto; } footer::before { content: ""; position: absolute; bottom: 100%; left: 0; right: 0; height: 400px; background: url(imgs/layered-peaks-haikei.svg) repeat-x; z-index: 2; } .visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; } .skip-link { position: fixed; top: 0; left: 0; background: #fff; padding: 10px; z-index: 999; } .typewriter-container { width: 450px; text-align: center; } .typewriter { position: relative; display: inline-block; overflow: hidden; white-space: nowrap; width: 100%; vertical-align: bottom; } .typewriter::after { content: ""; position: absolute; right: 0; top: 0; height: 100%; width: 1px; background-color: #222; animation: flicker 0.6s infinite alternate; } @keyframes flicker { 0% { opacity: 1; } 100% { opacity: 0; } }
JS
document.addEventListener("DOMContentLoaded", function () { var phrases = [ "phrase 1", "phrase 2", "phrase 3", "phrase 4", ]; var speed = 50; var pauseBetweenPhrases = 2000; var container = document.querySelector(".typewriter"); var currentPhraseIndex = 0; var charIndex = 0; function typeWriter() { if (charIndex < phrases[currentPhraseIndex].length) { container.innerHTML += phrases[currentPhraseIndex].charAt(charIndex); charIndex++; setTimeout(typeWriter, speed); } else { setTimeout(function () { backspace(); }, pauseBetweenPhrases); } } function backspace() { if (charIndex >= 0) { container.innerHTML = phrases[currentPhraseIndex].substring(0, charIndex); charIndex--; setTimeout(backspace, speed); } else { currentPhraseIndex = (currentPhraseIndex + 1) % phrases.length; setTimeout(typeWriter, speed); } } typeWriter(); // Start the typewriter effect });
内容的提问来源于stack exchange,提问作者DD1229
相关产品推荐
相关产品推荐

