index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
<script src="script.js" defer></script>
</head>
<body>
<form action="" data-multi-step class="multi-step-form">
<div class="card active" data-step>
<h3 class="step-title">面板1</h3>
<div class="form-group">
<label for="email">邮箱</label>
<input type="email" name="email" id="email">
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" name="password" id="password">
</div>
<button type="button" data-next>下一步</button>
</div>
<div class="card" data-step>
<h3 class="step-title">面板2</h3>
<div class="form-group">
<label for="address">住址</label>
<input type="text" name="address" id="address">
</div>
<div class="form-group">
<label for="city">城市</label>
<input type="text" name="city" id="city">
</div>
<div class="form-group">
<label for="zipcode">邮编</label>
<input type="text" name="zipcode" id="zipcode">
</div>
<button type="button" data-previous>上一步</button>
<button type="button" data-next>下一步</button>
</div>
<div class="card" data-step>
<h3 class="step-title">面板3</h3>
<div class="form-group">
<label for="firstName">姓名</label>
<input type="text" name="firstName" id="firstName">
</div>
<div class="form-group">
<label for="lastName">手机</label>
<input type="text" name="lastName" id="lastName">
</div>
<button type="button" data-previous>上一步</button>
<button type="submit">提交</button>
</div>
</form>
</body>
</html>
style.css
.card{
background-color: #fff;
border: 1px solid #333;
border-radius: .5rem;
padding: .5rem;
max-width: 300px;
margin: 0 auto;
animation: fade 250ms ease-in-out forwards;
}
.form-group{
display: flex;
flex-direction: column;
gap: .25rem;
margin-bottom: .5rem;
}
.form-group:last-child{
margin: 0;
}
.form-group > label{
font-weight: bold;
font-size: .8em;
color: #333;
}
.form-group > input{
border: 1px solid #333;
border-radius: .25em;
font-size: 1rem;
padding: .25em;
}
.step-title{
margin: 0;
margin-bottom: 1rem;
text-align: center;
}
.card.active {
animation: slide 250ms 125ms ease-in-out both;
}
.multi-step-form{
overflow: hidden;
position: relative;
}
.hide{
display: none;
}
@keyframes slide{
0%{
transform: translateX(200%);
opacity: 0;
}
100%{
transform:translateX(0);
opacity: 1;
}
}
@keyframes fade{
0%{
transform:scale(1);
opacity: 1;
}
50%{
transform:scale(.75);
opacity: 0;
}
100%{
opacity: 0;
transform:scale(0);
}
}
script.js
const multiStepForm = document.querySelector('[data-multi-step]')
const formSteps = [...multiStepForm.querySelectorAll('[data-step]')]
let currentStep = formSteps.findIndex(step => {
return step.classList.contains('active')
})
if (currentStep < 0) {
currentStep = 0
formSteps[currentStep].classList.add('active')
}
multiStepForm.addEventListener('click', e => {
let incrementor
if (e.target.matches('[data-next]')) {
incrementor = 1
} else if (e.target.matches('[data-previous]')) {
incrementor = -1
}
if (incrementor == null) return
const inputs = [...formSteps[currentStep].querySelectorAll('input')]
const allValid = inputs.every(input => input.reportValidity())
if (allValid) {
currentStep += incrementor
showCurrentStep()
}
})
formSteps.forEach(step => {
step.addEventListener('animationend',e=>{
formSteps[currentStep].classList.remove('hide')
e.target.classList.toggle('hide',!e.target.classList.contains('active'))
})
})
function showCurrentStep() {
formSteps.forEach((step, index) => {
step.classList.toggle('active', index === currentStep)
})
}
|