Tengo el siguiente fragmento donde acepto los detalles a través de un formulario de varios pasos. El problema es que actualmente solo acepta los campos de entrada de texto. Como puede ver, la matriz de preguntas contiene el tipo de preguntas donde:
Sin embargo, estoy atascado en la implementación de esto ya que soy nuevo en JavaScript. ¿Cómo puedo implementar esto?
La parte interesada del JavaScript es:
// 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() }Fragmento de trabajo completo.
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>Sé que necesito dar un ejemplo reproducible mínimo con un código limitado, pero pensé que crear un fragmento funcional daría una buena idea de este microproyecto. Cualquier ayuda sería apreciada. Gracias por adelantado :)
Parece que no está definiendo el tipo en la matriz de preguntas. Esta línea tiene como valor predeterminado todos los campos de entrada en texto porque no pudo encontrar el tipo.
inputField.type = preguntas[posición].tipo || 'texto'
var questions = [ {question: "What's your full name?"}, // TEXT INPUT FIELD {question: "What's your gender?", type:"radio"}, // 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: "Interest 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>Bien, primero, debe modificar su objeto de preguntas para que contenga las propiedades necesarias para crear sus elementos.
Luego, modifique un poco su html para que contenga otro contenedor dentro del contenedor de entrada donde pueda agregar campos de entrada adicionales (radio, casilla de verificación, seleccionar)
No estoy seguro si esto es lo que quieres, pero puedes referirte a esto:
var questions = [{ question: "What's your full name?", type: 'text', name: 'fullname' }, // TEXT INPUT FIELD { question: "What's your gender?", type: 'radio', name: 'gender', values: ['male', 'female', 'other'] }, // RADIO BUTTONS {male, female, other} { question: "What's your date of birth?", type: 'text', name: 'dob' }, // TEXT INPUT FIELD WITH id="datepicker" { question: "What's your country?", type: 'select', name: 'country', values: ['Canada', 'US', 'Other'] }, // SELECT BOX WITH LIST OF COUNTRIES IN IT { question: "Interest in?", type: 'checkbox', name: 'interest', values: ['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) }) // load the next question function putQuestion() { //hide the elements that you create so they don't show up when you proceed to next steps const hideExtraElems = document.querySelectorAll('#inputWrapper span, #inputWrapper select'); hideExtraElems.forEach(elem => { elem.style.display = 'none'; }); inputLabel.innerHTML = questions[position].question inputField.type = questions[position].type || 'text' //add a name attribute so that radio buttons can work inputField.name = questions[position].name if (questions[position].type == 'radio' || questions[position].type == 'checkbox') { //validation for type radio and checkbox //hide the main input field inputField.style.visibility = 'hidden'; //loop through each value for questions[position].values.forEach(item => { //create a span that will contain your new input fields let innerWrapper = document.createElement("span"); //style a bit innerWrapper.style.display = 'flex'; innerWrapper.style. justifyContent = 'space-between'; //add value name inside the span before adding the element itself let textNode = document.createTextNode(item.toUpperCase()); //append the value name to the span element innerWrapper.appendChild(textNode); //create a new input either using createElement or clone your inputField let newInput = document.getElementById('inputField').cloneNode(true); //assign attributes accordingly newInput.type = questions[position].type; newInput.value = item; newInput.id = newInput.id + '-' + item; newInput.style.visibility = 'visible'; newInput.style.width = 'auto'; //append the input field inside the span innerWrapper.appendChild(newInput); //append the span inside the wrapper inputWrapper.appendChild(innerWrapper); }); } else if (questions[position].type == 'select') { //validation for select field //hide the main input field inputField.style.visibility = 'hidden'; //create the main select box outside the loop let newSelect = document.createElement("select"); //loop through the options questions[position].values.forEach(item => { //create an option element and assign the attributes let newOption = document.createElement("option"); let textNode = document.createTextNode(item.toUpperCase()); newOption.value = item; newOption.appendChild(textNode); //append the final option inside the select newSelect.appendChild(newOption); }); //append the select inside the inputWrapper inputWrapper.appendChild(newSelect); } else { //show the inputField inputField.style.visibility = 'visible'; inputField.value = questions[position].values || '' 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; } select{ width: 100%; border: 0; outline: none; padding: 1rem 0; } .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"> <div id="inputWrapper"> <input id="inputField" required multiple /> <label id="inputLabel"></label> </div> <div id="inputProgress"></div> </div> </div> </div>Hice lo mejor que pude para hacer lo que me pediste, verás que agregué 3 divs para incluir tus diferentes solicitudes
También modifico tu condición de validación.
Esperando que pueda ayudarte
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>