Required and index (step skipping) issue in Wizard's form steps - JavaScript

112 views Asked by At

I have 6 Wizard with different number of steps. If all input elements in all steps of these wizards have an empty value, the wizard does not proceed to the next step, that is, the next button does not work if the inputs are empty. But I don't want to apply null/empty validation to two inputs of two steps in two of these Wizards. Even if the user leaves the two inputs in these two steps blank, the relevant steps must be passable/skipable and the next button must work under all conditions for these two special inputs. I want to keep checking for null values for the remaining steps and inputs. This is how I created the skeleton of my Wizards to implement this scenario:

const tabContents = document.querySelectorAll('.tab-content .tab-pane')
const progressBar = document.querySelector('.progress-bar')
const nextButtons = document.querySelectorAll('.next')
const prevButtons = document.querySelectorAll('.prev')
const stepCounter = document.querySelector('.step-counter')
let currentTab = 0
const selectedValues = []
const transitionContainers = document.querySelectorAll('.transition-container')
const firstStepId = 'wizard-step-99'
const secondStepId = 'step22'
// Next button click management
nextButtons.forEach((nextButton) => {
   nextButton.addEventListener('click', () => {
     if (validateInputs()) {
       saveSelectedValue()
       if (currentTab < tabContents.length - 1) {
         if (tabContents[currentTab].id === firstStepId) {
           // Apply the transition effect of the first step

           // Delay the second step for the specified time (2.5 seconds)
           const delayTime = 2500

           setTimeout(() => {
             // Switch to my new name
             tabContents[currentTab].style.display = 'none'
             currentTab++
             tabContents[currentTab].style.display = 'block'

             updateProgressBar()
             restoreSelectedRadios()
           }, delayTime)
         } else {
           tabContents[currentTab].style.display = 'none'
           currentTab++
           tabContents[currentTab].style.display = 'block'
           updateProgressBar()
           restoreSelectedRadios()
         }
       }
     }
   })
})
// Back button click management
prevButtons.forEach((prevButton) => {
   prevButton.addEventListener('click', () => {
     saveSelectedValue()
     if (currentTab > 0) {
       tabContents[currentTab].style.display = 'none'
       currentTab--
       tabContents[currentTab].style.display = 'block'
       updateProgressBar()
       restoreSelectedRadios()
     }
   })
})
// Function to save the selected value
function saveSelectedValue() {
   const selectedRadio = tabContents[currentTab].querySelector(
     'input[type="radio"]:checked'
   )
   if (selectedRadio) {
     const value = selectedRadio.value
     selectedValues[currentTab] = value
   }
}
// Restore selected buttons
function restoreSelectedRadios() {
   const radios = tabContents[currentTab].querySelectorAll('input[type="radio"]')
   radios.forEach((radio) => {
     const value = radio.value
     radio.checked = value === selectedValues[currentTab]
     radio.disabled = false
   })
}
// Check the input validation for the relevant step
function validateInputs() {
   const inputs = tabContents[currentTab].querySelectorAll(
     '.form-control, input[type="radio"]'
   )

   for (let i = 0; i < inputs.length; i++) {
      const input = inputs[i]
      if (input.id !== 'input-number-2' && input.id !== 'materialLoss') {
           if (input.tagName === 'INPUT' && input.type === 'radio') {
    
             const radioGroup = input.name
             const radioButtons = tabContents[currentTab].querySelectorAll(
               `input[name="${radioGroup}"]`
             )
             const isRadioButtonChecked = Array.from(radioButtons).some(
               (radioButton) => radioButton.checked
             )
             if (!isRadioButtonChecked) {
               return false
             }
           } else if (isEmpty(input.value)) {
             return false
           }
     }
   }
  return true
}
// Update the progress bar
function updateProgressBar() {
   progressBar.style.width = ((currentTab + 1) / tabContents.length) * 100 + '%'
}
// Helper function: Check for null
function isEmpty(str) {
   return !str.trim().length
}
// Initial settings
tabContents[currentTab].style.display = 'block'
updateProgressBar()
restoreSelectedRadios()

1st step function (if the "mortal" option is selected in this step, it is necessary to skip from step 4 to step 6, step 5 should be ignored):

function handleStatusButton() {
   let accidentStatusRadio = document.querySelector('.status:checked').value
   localStorage.setItem('accidentStatusRadio', accidentStatusRadio)
   console.log(currentTab)
   console.log(accidentStatusRadio)
   if (accidentStatusRadio === 'Mortal') {
     let captionChangeValidate = document.getElementById('caption-change')
     captionChangeValidate.textContent = 'Age of Death:'
     captionChangeValidate.classList.add('form-label')
   }
}

5th step function (the function where the skip will occur):

function handleDefectRadioRadioButtons() {
   const defectRatio = document.querySelector('.defectRatio:checked').value
   localStorage.setItem('defectRatio', defectRatio)
   console.log(defectRatio)
   const accidentStatusRadio = localStorage.getItem('accidentStatusRadio ')
   console.log(currentTab)
   if (accidentStatusRadio === 'Mortal') {
     currentTab = 6
     console.log(tabContents)
     tabContents.forEach((content, index) => {
       if (index === currentTab) {
         content.style.display = 'block'
         console.log(content)
       } else {
         content.style.display = 'none'
         console.log(content)
       }
     })
     updateProgressBar()
   } else {
   }
}

Step functions are triggered by the onClick function added to the button element in the code on the HTML side. The skip feature does not work in these codes. While it should be skipped directly from step 5 to step 7, it skips to step 8 and misses the step where the input with materialLoss ID is located. By skip feature I mean not "do going through the steps in order", this code doesn't work correctly:

if (accidentStatusRadio === 'Mortal') {
     currentTab = 6 // index starts from 0. (0, 1, 2, 3, 4, 5, 6)
     console.log(tabContents)
     tabContents.forEach((content, index) => {
       if (index === currentTab) {
         content.style.display = 'block'
         console.log(content)
       } else {
         content.style.display = 'none'
         console.log(content)
       }
     })
     updateProgressBar()
   } else {
   }

When I check it with console.log, the steps (currentTab) in the wizard are correct, but it still acts as if step 7 is missing and goes to step 8. However, this code wizard should take the 7th step. If I delete the if (input.id !== 'input-number-2' && input.id !== 'materialLoss') line in the for loop in the validateInputs() function, the above skip feature code works correctly. But if I add this if condition it doesn't see step 7. What is the reason of this?

We can pass by leaving it blank, or jump to the step you want when you choose the mortal. But we cannot do both at the same time. One destroys the other.

When we activate passing by leaving blank, we skip one step more instead of stepping. Where we do not activate, the empty input is not passed/skipped anyway and everything else works correctly.

You can go to this link to see and try the demo of the codes and the Wizard: Wizard Demo

Thank you very much in advance for any help and support!

0

There are 0 answers