javascript - 如何在多步 javascript 表单中添加单选按钮、复选框和选择字段?
问题描述
我有以下片段,我通过多步骤表单接受详细信息。问题是目前它只接受文本输入字段。如您所见,问题数组包含以下问题类型:
- 问题 1 可以作为文本输入,因为它可以接受名称。
- 问题 2 应该是一个单选按钮,因为它会接受性别。
- 问题 3 应该再次是文本输入字段,带有 DOB 的日期选择器,
- 问题 4 应该是一个选择框,用于从列表中选择一个国家,最后,
- 问题 5 的输入类型应为选中所有用户有兴趣看到的复选框(男性、女性、其他)。
但是,由于我是 JavaScript 新手,所以我坚持实现这一点。我该如何实施?
JavaScript的相关部分是:
// load the next question
function putQuestion() {
inputLabel.innerHTML = questions[position].question
inputField.type = questions[position].type || 'text'
inputField.value = questions[position].answer || ''
inputField.focus()
// set the progress of the background
progress.style.width = position * 100 / questions.length + '%'
previousButton.className = position ? 'ion-android-arrow-back' : 'ion-person'
showCurrent()
}
完整的工作片段。
var questions = [
{question: "What's your full name?"}, // TEXT INPUT FIELD
{question: "What's your gender?"}, // RADIO BUTTONS {male, female, other}
{question: "What's your date of birth?"}, // TEXT INPUT FIELD WITH id="datepicker"
{question: "What's your country?"}, // SELECT BOX WITH LIST OF COUNTRIES IN IT
{question: "Interested in?"} // CHECKBOXES {male, female, other}
]
//do something after the questions have been answered
var onComplete = function() {
var h1 = document.createElement('h1')
h1.appendChild(document.createTextNode('Thanks ' + questions[0].answer + ' for checking this pen out!'))
setTimeout(function() {
register.parentElement.appendChild(h1)
setTimeout(function() { h1.style.opacity = 1 }, 50)
}, 1000)
}
;(function(questions, onComplete) {
var tTime = 100 // transition transform time from #register in ms
var wTime = 200 // transition width time from #register in ms
var eTime = 1000 // transition width time from inputLabel in ms
// init
if (questions.length == 0) return
var position = 0
putQuestion()
forwardButton.addEventListener('click', validate)
inputField.addEventListener('keyup', function(e) {
transform(0, 0) // ie hack to redraw
if (e.keyCode == 13) validate()
})
previousButton.addEventListener('click', function(e) {
if (position === 0) return
position -= 1
hideCurrent(putQuestion)
})
// load the next question
function putQuestion() {
inputLabel.innerHTML = questions[position].question
inputField.type = questions[position].type || 'text'
inputField.value = questions[position].answer || ''
inputField.focus()
// set the progress of the background
progress.style.width = position * 100 / questions.length + '%'
previousButton.className = position ? 'ion-android-arrow-back' : 'ion-person'
showCurrent()
}
// when submitting the current question
function validate() {
var validateCore = function() {
return inputField.value.match(questions[position].pattern || /.+/)
}
if (!questions[position].validate) questions[position].validate = validateCore
// check if the pattern matches
if (!questions[position].validate())
wrong(inputField.focus.bind(inputField))
else ok(function() {
// execute the custom end function or the default value set
if (questions[position].done) questions[position].done()
else questions[position].answer = inputField.value
++position
// if there is a new question, hide current and load next
if (questions[position]) hideCurrent(putQuestion)
else hideCurrent(function() {
// remove the box if there is no next question
register.className = 'close'
progress.style.width = '100%'
onComplete()
})
})
}
// helper
function hideCurrent(callback) {
inputContainer.style.opacity = 0
inputLabel.style.marginLeft = 0
inputProgress.style.width = 0
inputProgress.style.transition = 'none'
inputContainer.style.border = null
setTimeout(callback, wTime)
}
function showCurrent(callback) {
inputContainer.style.opacity = 1
inputProgress.style.transition = ''
inputProgress.style.width = '100%'
setTimeout(callback, wTime)
}
function transform(x, y) {
register.style.transform = 'translate(' + x + 'px , ' + y + 'px)'
}
function ok(callback) {
register.className = ''
setTimeout(transform, tTime * 0, 0, 10)
setTimeout(transform, tTime * 1, 0, 0)
setTimeout(callback, tTime * 2)
}
function wrong(callback) {
register.className = 'wrong'
for (var i = 0; i < 6; i++) // shaking motion
setTimeout(transform, tTime * i, (i % 2 * 2 - 1) * 20, 0)
setTimeout(transform, tTime * 6, 0, 0)
setTimeout(callback, tTime * 7)
}
}(questions, onComplete))
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@100&display=swap');
body {
margin: 0;
background: #fbc02d;
font-family: 'Roboto', sans-serif;
overflow-x: hidden;
}
h1 {
position: relative;
color: #fff;
opacity: 0;
transition: .8s ease-in-out;
}
#progress {
position: absolute;
background: #c49000;
height: 100vh;
width: 0;
transition: width 0.2s ease-in-out;
}
.center {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
#register {
background: #fff;
position: relative;
width: 550px;
box-shadow: 0 16px 24px 2px rgba(0,0,0,0.14), 0 6px 30px 5px rgba(0,0,0,0.12), 0 8px 10px -5px rgba(0,0,0,0.3);
transition: transform .1s ease-in-out;
}
#register.close {
width: 0;
padding: 0;
overflow: hidden;
transition: .8s ease-in-out;
box-shadow: 0 16px 24px 2px rgba(0,0,0,0);
}
#forwardButton {
position: absolute;
right: 20px;
bottom: 5px;
font-size: 40px;
color: #fbc02d;
float: right;
cursor: pointer;
z-index: 20
}
#previousButton {
position: absolute;
font-size: 18px;
left: 30px; /* same as padding on container */
top: 12px;
z-index: 20;
color: #9e9e9e;
float: right;
cursor: pointer;
}
#previousButton:hover {color: #c49000}
#forwardButton:hover {color: #c49000}
.wrong #forwardButton {color: #ff2d26}
.close #forwardButton, .close #previousButton {color: #fff}
#inputContainer {
position: relative;
padding: 30px 20px 20px 20px;
margin: 10px 60px 10px 10px;
opacity: 0;
transition: opacity .3s ease-in-out;
}
#inputContainer input {
position: relative;
width: 100%;
border: none;
font-size: 20px;
outline: 0;
background: transparent;
box-shadow: none;
}
#inputLabel {
position: absolute;
pointer-events: none;
top: 32px; /* same as container padding + margin */
left: 20px; /* same as container padding */
font-size: 20px;
transition: .2s ease-in-out;
}
#inputContainer input:valid + #inputLabel {
top: 6px;
left: 42px; /* space for previous arrow */
margin-left: 0!important;
font-size: 11px;
font-weight: normal;
color: #9e9e9e;
}
#inputProgress {
border-bottom: 3px solid #fbc02d;
width: 0;
transition: width .6s ease-in-out;
}
.wrong #inputProgress {
border-color: #ff2d26;
}
@media (max-width: 420px) {
#forwardButton {right: 10px}
#previousButton {left: 10px}
#inputLabel {left: 0}
#inputContainer {padding-left: 0; margin-right:20px}
}
<link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<div id="progress"></div>
<div class="center">
<div id="register">
<i id="previousButton" class="ion-android-arrow-back"></i>
<i id="forwardButton" class="ion-android-arrow-forward"></i>
<div id="inputContainer">
<input id="inputField" required multiple />
<label id="inputLabel"></label>
<div id="inputProgress"></div>
</div>
</div>
</div>
我知道我需要用有限的代码给出一个最小可重复的例子,但我认为创建一个工作片段可以很好地了解这个微型项目。任何帮助,将不胜感激。提前致谢 :)
解决方案
我尽力按照您的要求做,您会看到我添加了 3 个 div 以包含您的不同请求
我还修改了您的验证条件
希望它可以帮助你
var questions = [
{
question: "What's your full name?",
type: "text"
}, // TEXT INPUT FIELD
{
question: "What's your gender?",
type: "radio",
response: ['male','female','other']
}, // RADIO BUTTONS {male, female, other}
{
question: "What's your date of birth?",
type: "date"
}, // TEXT INPUT FIELD WITH id="datepicker"
{
question: "What's your country?",
type: "select",
response: ['UK','FR','DE']
}, // SELECT BOX WITH LIST OF COUNTRIES IN IT
{
question: "Interest in?",
type: "checkbox",
response: ['male','female','other']
} // CHECKBOXES {male, female, other}
]
//do something after the questions have been answered
var onComplete = function() {
var h1 = document.createElement('h1')
h1.appendChild(document.createTextNode('Thanks ' + questions[0].answer + ' for checking this pen out!'))
setTimeout(function() {
register.parentElement.appendChild(h1)
setTimeout(function() { h1.style.opacity = 1 }, 50)
}, 1000)
}
;(function(questions, onComplete) {
var tTime = 100 // transition transform time from #register in ms
var wTime = 200 // transition width time from #register in ms
var eTime = 1000 // transition width time from inputLabel in ms
// init
if (questions.length == 0) return
var position = 0
putQuestion()
forwardButton.addEventListener('click', validate)
inputField.addEventListener('keyup', function(e) {
transform(0, 0) // ie hack to redraw
if (e.keyCode == 13) validate()
})
previousButton.addEventListener('click', function(e) {
if (position === 0) return
position -= 1
hideCurrent(putQuestion)
$('#gender').hide();
$('#country').hide();
$('#interest').hide();
})
// load the next question
function putQuestion() {
$('#gender').hide();
$('#country').hide();
$('#interest').hide();
$(inputField).attr('readonly',false);
$(inputField).attr('required',false);
inputLabel.innerHTML = questions[position].question;
inputField.type = 'text';
switch(questions[position].type){
case "text":
$(inputField).attr('required',true)
inputField.value = questions[position].answer || '';
break;
case "radio":
$(inputField).attr('readonly',true)
inputField.value = '';
let nbrGender = questions[position].response;
if( $('#gender').children().length > 0 ){
$('#gender').children().remove()
}
$('#gender').show();
for( var $i = 0; $i < nbrGender.length; ++$i ){
let checkedGender = questions[position].answer == questions[position].response[$i] ? 'checked' : '';
$('#gender').append(
'<input type="radio" name="onlyone" value="'+questions[position].response[$i]+'" '+checkedGender+'>'+
'<label id="text'+$i+'">'+questions[position].response[$i]+'</label>'
);
}
break;
case "date":
inputField.type = 'date';
inputField.value = questions[position].answer;
break;
case "select":
$(inputField).attr('readonly',true)
inputField.value = '';
var nbrCountry = questions[position].response;
if( $('#country').children().length > 0 ){
$('#country').children().remove()
}
$('#country').show();
var optionCountry;
for( var $i = 0; $i < nbrCountry.length; ++$i ){
let selectedCountry = questions[position].answer == questions[position].response[$i] ? 'selected' : '';
optionCountry += '<option value="'+questions[position].response[$i]+'" '+selectedCountry+'>'+questions[position].response[$i]+'</option>'
}
$('#country').append(' <select name="gender" id="countryList">'+optionCountry+'</select>')
break;
case "checkbox":
$(inputField).attr('readonly',true)
inputField.value = '';
let nbrInterest = questions[position].response;
if( $('#interest').children().length > 0 ){
$('#interest').children().remove()
}
$('#interest').show();
for( var $i = 0; $i < nbrInterest.length; ++$i ){
let checkedInterest = questions[position].answer == questions[position].response[$i] ? 'checked' : '';
$('#interest').append(
'<input type="checkbox" name="onlyone" value="'+questions[position].response[$i]+'" '+checkedInterest+'>'+
'<label id="text'+$i+'">'+questions[position].response[$i]+'</label>'
);
}
break;
default:
inputField.type = 'text';
inputField.value = questions[position].answer || '';
break;
}
inputField.focus()
// set the progress of the background
progress.style.width = position * 100 / questions.length + '%'
previousButton.className = position ? 'ion-android-arrow-back' : 'ion-person'
showCurrent()
}
// when submitting the current question
function validate() {
var validateCore = function() {
if(inputField.type == "date" && inputField.value ){
questions[position].answer =inputField.value;
return true;
}else if ( inputField.type != "date" ){
if(inputField.value.match(questions[position].pattern || /.+/) ){
questions[position].answer =inputField.value;
return true;
}
else if( $('#countryList').is(':visible') && $('#countryList').find(':selected').length > 0 ){
questions[position].answer = $('#country').find(':selected')[0].value
return true;
}
else if( $('#gender').is(':visible') && $('#gender').find(':checked').length > 0 ){
questions[position].answer = $('#gender').find(':checked')[0].value
return true;
}else if( $('#interest').is(':visible') && $('#interest').find(':checked').length > 0 ){
let responseInterest = [];
$('#interest').find(':checked').each(function(){
responseInterest.push($(this).val());
});
questions[position].answer = responseInterest;
return true;
}
else{
return false;
}
}else{
return false
}
}
if (!questions[position].validate) questions[position].validate = validateCore
// check if the pattern matches
if (!questions[position].validate())
wrong(inputField.focus.bind(inputField))
else ok(function() {
// execute the custom end function or the default value set
if (questions[position].done) questions[position].done()
++position
// if there is a new question, hide current and load next
if (questions[position]) hideCurrent(putQuestion)
else hideCurrent(function() {
// remove the box if there is no next question
register.className = 'close'
progress.style.width = '100%'
onComplete()
})
})
}
// helper
function hideCurrent(callback) {
inputContainer.style.opacity = 0
inputLabel.style.marginLeft = 0
inputProgress.style.width = 0
inputProgress.style.transition = 'none'
inputContainer.style.border = null
setTimeout(callback, wTime)
}
function showCurrent(callback) {
inputContainer.style.opacity = 1
inputProgress.style.transition = ''
inputProgress.style.width = '100%'
setTimeout(callback, wTime)
}
function transform(x, y) {
register.style.transform = 'translate(' + x + 'px , ' + y + 'px)'
}
function ok(callback) {
register.className = ''
setTimeout(transform, tTime * 0, 0, 10)
setTimeout(transform, tTime * 1, 0, 0)
setTimeout(callback, tTime * 2)
}
function wrong(callback) {
register.className = 'wrong'
for (var i = 0; i < 6; i++) // shaking motion
setTimeout(transform, tTime * i, (i % 2 * 2 - 1) * 20, 0)
setTimeout(transform, tTime * 6, 0, 0)
setTimeout(callback, tTime * 7)
}
}(questions, onComplete))
// load the next question
function putQuestion() {
inputLabel.innerHTML = questions[position].question
inputField.type = questions[position].type || 'text'
inputField.value = questions[position].answer || ''
inputField.focus()
// set the progress of the background
progress.style.width = position * 100 / questions.length + '%'
previousButton.className = position ? 'ion-android-arrow-back' : 'ion-person'
showCurrent()
}
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@100&display=swap');
body {
margin: 0;
background: #fbc02d;
font-family: 'Roboto', sans-serif;
overflow-x: hidden;
}
h1 {
position: relative;
color: #fff;
opacity: 0;
transition: .8s ease-in-out;
}
#progress {
position: absolute;
background: #c49000;
height: 100vh;
width: 0;
transition: width 0.2s ease-in-out;
}
.center {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
#register {
background: #fff;
position: relative;
width: 550px;
box-shadow: 0 16px 24px 2px rgba(0,0,0,0.14), 0 6px 30px 5px rgba(0,0,0,0.12), 0 8px 10px -5px rgba(0,0,0,0.3);
transition: transform .1s ease-in-out;
}
#register.close {
width: 0;
padding: 0;
overflow: hidden;
transition: .8s ease-in-out;
box-shadow: 0 16px 24px 2px rgba(0,0,0,0);
}
#forwardButton {
position: absolute;
right: 20px;
bottom: 5px;
font-size: 40px;
color: #fbc02d;
float: right;
cursor: pointer;
z-index: 20
}
#previousButton {
position: absolute;
font-size: 18px;
left: 30px; /* same as padding on container */
top: 12px;
z-index: 20;
color: #9e9e9e;
float: right;
cursor: pointer;
}
#previousButton:hover {color: #c49000}
#forwardButton:hover {color: #c49000}
.wrong #forwardButton {color: #ff2d26}
.close #forwardButton, .close #previousButton {color: #fff}
#inputContainer {
position: relative;
padding: 30px 20px 20px 20px;
margin: 10px 60px 10px 10px;
opacity: 0;
transition: opacity .3s ease-in-out;
}
#inputContainer input {
position: relative;
width: 100%;
border: none;
font-size: 20px;
outline: 0;
background: transparent;
box-shadow: none;
}
#inputLabel {
position: absolute;
pointer-events: none;
top: 32px; /* same as container padding + margin */
left: 20px; /* same as container padding */
font-size: 20px;
transition: .2s ease-in-out;
}
#inputContainer input:valid + #inputLabel {
top: 6px;
left: 42px; /* space for previous arrow */
margin-left: 0!important;
font-size: 11px;
font-weight: normal;
color: #9e9e9e;
}
#inputProgress {
border-bottom: 3px solid #fbc02d;
width: 0;
transition: width .6s ease-in-out;
}
.wrong #inputProgress {
border-color: #ff2d26;
}
@media (max-width: 420px) {
#forwardButton {right: 10px}
#previousButton {left: 10px}
#inputLabel {left: 0}
#inputContainer {padding-left: 0; margin-right:20px}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<div id="progress"></div>
<div class="center">
<div id="register">
<i id="previousButton" class="ion-android-arrow-back"></i>
<i id="forwardButton" class="ion-android-arrow-forward"></i>
<div id="inputContainer">
<input id="inputField" />
<label id="inputLabel"></label>
<div id="inputProgress"></div>
<div id="gender"></div>
<div id="country"></div>
<div id="interest"></div>
</div>
</div>
</div>
推荐阅读
- javascript - 循环挑战,递减一个变量
- c++ - 初始化期间针对安全检查的指针的 const 正确性
- c# - 自动检查二进制兼容性?
- angular - 重新加载 PrimeJS 突出显示的代码 Angular 应用程序
- angular - Angular 2 boostrap 日期选择器禁用其他日期不起作用
- python - 使用 CNN 模型进行文本分类的预测脚本出错
- python - 使用 BS4 抓取后如何清理 HTML 标签
- express - 如何将 OAuth github 身份验证添加到 VueJS 应用程序
- excel - Excel For 如果一张纸与其他纸不能正常工作
- ruby - LoadError - 无法打开共享对象文件 - 文件存在,但它说没有这样的文件