Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

402
Visualizações
How to add radio buttons, checkboxes and select field in a multi step javascript form?

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:

  1. Question no.1 is just fine as text input as it would accept name.
  2. Question no.2 should be a radio button as it would accept gender.
  3. Question no.3 should be text input field again with a datepicker for DOB,
  4. Question no.4 should be a select box for selecting a country from the list and lastly,
  5. Question no.5 should have the input type as checkbox for selected all those user is interested in seeing (male, female, other).

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 :)

over 4 years ago · Santiago Trujillo
3 Respostas
Responde à pergunta

0

Looks like you're not defining the type in the questions array. This line is defaulting every input field to text because it couldn't find the type.

inputField.type = questions[position].type || 'text'

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>

over 4 years ago · Santiago Trujillo Relatório

0

Okay first, you need to modify your questions object so it contains the properties required to create your elements.

Then, modify your html a bit so that it contains another wrapper inside the input container where you can append additional input fields (radio, checkbox, select)

Not sure if this is what you want, but you may refer this:

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>

over 4 years ago · Santiago Trujillo Relatório

0

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>

over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda