I have the following snippet where I am accepting the details via a multi step form. The problem is that currently it is accepting only the text input fields. As you can see that the array of questions contains the type of questions where:
However, I am stuck on implementing this as I am new to JavaScript. How can I implement this?
The concerned part of the JavaScript is:
// 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()
}
Full working snippet.
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>
I know I need to give a minimum reproducible example with limited code but I thought creating a working snippet would give a good idea of this micro project. Any help would be appreciated. Thanks in advance :)
Radio buttons allow a user to select a single option among multiple options. You can set the Choice Value of each option, for each button, as well as group these buttons by giving them the same Group Name.
Checkbox allows one or many options to be selected. It is created by using HTML <input> tag but type attribute is set to radio. It is also created using HTML <input> tag but type attribute is set to checkbox. It is a single control unit.
With radio buttons, the user can only select one choice.
Checkboxes allow the user to choose items from a fixed number of alternatives, while radio buttons allow the user to choose exactly one item from a list of several predefined alternatives.
I tried the best to do what you asked, you will see that I added 3 divs to include your different requests
I also modify your validate condition
Hoping it can help you
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With