【问题标题】:How to add radio buttons, checkboxes and select field in a multi step javascript form?如何在多步 javascript 表单中添加单选按钮、复选框和选择字段?
【发布时间】:2021-07-15 15:29:35
【问题描述】:

我有以下 sn-p,我通过多步骤表单接受详细信息。问题是目前它只接受文本输入字段。如您所见,问题数组包含以下问题类型:

  1. 问题 1 可以作为文本输入,因为它可以接受名称。
  2. 问题 2 应该是一个单选按钮,因为它会接受性别。
  3. 第 3 个问题应该是文本输入字段,并带有 DOB 的日期选择器,
  4. 问题 4 应该是一个选择框,用于从列表中选择一个国家,最后,
  5. 问题 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()
}

完整的工作 sn-p。

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>

我知道我需要用有限的代码给出一个最小的可重现示例,但我认为创建一个有效的 sn-p 可以很好地了解这个微型项目。任何帮助,将不胜感激。在此先感谢:)

【问题讨论】:

    标签: javascript html jquery css


    【解决方案1】:

    我尽力按照你的要求去做,你会看到我添加了 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>

    【讨论】:

    • 哇......天才......你是纯 100% 宝石......这正是我想要的......但是,我可以请你帮个忙吗?对于单选、复选框和选择(非文本字段),您可以在问题和选项之间放置进度条(下划线)吗?那是因为,样式看起来有点奇怪。请问?
    • 如果您正在寻找纯 JavaScript 选项,请查看我的回复。
    • 我编辑了我的代码我只是改变了地方#inputProgress
    • @Funky 有趣的是,我在询问你一分钟后发现完全相同的事情:) 还为进度条添加了 10 像素的顶部和底部边距,以获得更好的外观。谢谢你的时间你天才家伙..爱你:)
    • @Funky 我有一个问题...为什么函数putQuestion() 在这里创建了两次?一个在中间,另一个在最后?有什么相关性吗?还是一个不起眼的错误?
    【解决方案2】:

    好的,首先,您需要修改问题对象,使其包含创建元素所需的属性。

    然后,稍微修改您的 html,使其在输入容器中包含另一个包装器,您可以在其中附加其他输入字段(单选、复选框、选择)

    不确定这是否是您想要的,但您可以参考:

    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>

    【讨论】:

      【解决方案3】:

      看起来您没有在问题数组中定义类型。此行将每个输入字段默认为文本,因为它找不到类型。

      inputField.type = questions[位置].type || '文本'

      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>

      【讨论】:

      • 你检查了sn-p吗?遗憾的是,仅定义类型并没有给出预期的结果。
      • 是的,我知道,这只是一个例子。这不是完整的答案。你需要调整你的数组。您还需要根据类似这样的问题类型在数组中包含所需的属性,例如 {question: "What's your gender?", type: "radio", options: ["male","female"] }并在您的 putQuestion() 中处理
      • 知道了..让我试试,然后我会尽快更新。
      猜你喜欢
      • 2012-12-19
      • 2012-12-14
      • 2023-04-02
      • 2016-08-22
      • 1970-01-01
      • 2018-03-23
      • 2019-09-07
      • 2012-05-12
      • 2020-03-08
      相关资源
      最近更新 更多