Как динамически у квиза вывести шаги и давать класс активному?
Есть квиз, что-то не как не додумаю как сделать без костылей. Нужно динамически вывести шаги (слева) и давать активному класс _active
snippet
Дополнительно:
Но и сейчас все на костылях: дублируется разметка (и есть шанс ошибиться), состояние квиза «живёт» в разметке. Ввод телефона не удобный. Компоненты отчего б не взять все готовые из какого-нибудь красивого ui-кита, и почему бы не переделать всё на React или Vue?
Данные квиза отдельно, разметка генерится из данных налету.
Ответы:
Так сделай хотя бы так же как у тебя уже сделано перелистывание вопросов.
const quizzes = document.querySelectorAll(".calc__fields"); quizzes.forEach((quiz) => { const questions = quiz.querySelectorAll(".fields__field"), btnsPrev = quiz.querySelectorAll(".btn-prev"), btnsNext = quiz.querySelectorAll(".btn--icon-next"), stages = quiz.querySelectorAll(".fields__stage"); console.log(stages) // progress // const progressCount = questions.length; btnsNext.forEach((btnNext, btnNextIndex) => { btnNext.addEventListener("click", (e) => { e.preventDefault(); questions[btnNextIndex].classList.remove("_active"); questions[btnNextIndex + 1].classList.add("_active"); stages[btnNextIndex].classList.remove("_active"); stages[btnNextIndex + 1].classList.add("_active"); }); if (!questions[btnNextIndex].classList.contains("_range")) { btnNext.disabled = true; } }); btnsPrev.forEach((btnPrev, btnPrevIndex) => { btnPrev.addEventListener("click", (e) => { e.preventDefault(); questions[btnPrevIndex + 1].classList.remove("_active"); questions[btnPrevIndex].classList.add("_active"); }); }); questions.forEach((question, questionIndex) => { if (questionIndex === 0) { question.classList.add("_active"); } else { question.classList.remove("_active"); } question.addEventListener("change", (e) => { const target = e.target, inputsChecked = question.querySelectorAll("input:checked"); if ( inputsChecked.length > 0 || target.classList.contains("range__input") ) { btnsNext[questionIndex].disabled = false; } else { btnsNext[questionIndex].disabled = true; } }); }); }); |
const quizzes = document.querySelectorAll(".calc__fields"); quizzes.forEach((quiz) => { const questions = quiz.querySelectorAll(".fields__field"), btnsPrev = quiz.querySelectorAll(".btn-prev"), btnsNext = quiz.querySelectorAll(".btn--icon-next"), stages = quiz.querySelectorAll(".fields__stage"); console.log(stages) // progress // const progressCount = questions.length; btnsNext.forEach((btnNext, btnNextIndex) => { btnNext.addEventListener("click", (e) => { e.preventDefault(); questions[btnNextIndex].classList.remove("_active"); questions[btnNextIndex + 1].classList.add("_active"); stages[btnNextIndex].classList.remove("_active"); stages[btnNextIndex + 1].classList.add("_active"); }); if (!questions[btnNextIndex].classList.contains("_range")) { btnNext.disabled = true; } }); btnsPrev.forEach((btnPrev, btnPrevIndex) => { btnPrev.addEventListener("click", (e) => { e.preventDefault(); questions[btnPrevIndex + 1].classList.remove("_active"); questions[btnPrevIndex].classList.add("_active"); }); }); questions.forEach((question, questionIndex) => { if (questionIndex === 0) { question.classList.add("_active"); } else { question.classList.remove("_active"); } question.addEventListener("change", (e) => { const target = e.target, inputsChecked = question.querySelectorAll("input:checked"); if ( inputsChecked.length > 0 || target.classList.contains("range__input") ) { btnsNext[questionIndex].disabled = false; } else { btnsNext[questionIndex].disabled = true; } }); }); });
- Не уверен что это без костылей, но вроде работает
- Как вариант, только как шаги js'ом выставить
- Даниил Гладыш, Потому что когда отрисовываю шаги в questions.forEach, это уже будет не в области видимости
stages[btnNextIndex].classList.remove("_active"); stages[btnNextIndex + 1].classList.add("_active");
stages[btnNextIndex].classList.remove("_active"); stages[btnNextIndex + 1].classList.add("_active");
- anmkh, Почему? Шаги как раз двигаются
- Даниил Гладыш,
Генерирую разметку из js и переключение уже не срабатываетconst quizzes = document.querySelectorAll(".calc__fields"); quizzes.forEach((quiz) => { const questions = quiz.querySelectorAll(".fields__field"), btnsPrev = quiz.querySelectorAll(".btn-prev"), btnsNext = quiz.querySelectorAll(".btn--icon-next"), stagesWrapper = quiz.querySelector(".fields__stages"); stages = quiz.querySelectorAll(".fields__stage"); // progress const progressCount = questions.length; btnsNext.forEach((btnNext, btnNextIndex) => { btnNext.addEventListener("click", (e) => { e.preventDefault(); questions[btnNextIndex].classList.remove("_active"); questions[btnNextIndex + 1].classList.add("_active"); stages[btnNextIndex].classList.remove("_active"); stages[btnNextIndex + 1].classList.add("_active"); }); if (!questions[btnNextIndex].classList.contains("_range")) { btnNext.disabled = true; } }); btnsPrev.forEach((btnPrev, btnPrevIndex) => { btnPrev.addEventListener("click", (e) => { e.preventDefault(); questions[btnPrevIndex + 1].classList.remove("_active"); questions[btnPrevIndex].classList.add("_active"); // count--; // currentNum.innerHTML = count; }); }); questions.forEach((question, questionIndex) => { if (questionIndex === 0) { question.classList.add("_active"); } else { question.classList.remove("_active"); } const count = questionIndex + 1; if (count === 1) { stagesWrapper.innerHTML += '<li class="fields__stage _active">' + "Шаг " + count + "</li>"; } else { stagesWrapper.innerHTML += '<li class="fields__stage">' + "Шаг " + count + "</li>"; } question.addEventListener("change", (e) => { const target = e.target, inputsChecked = question.querySelectorAll("input:checked"); if (inputsChecked.length > 0 || target.classList.contains("range__input")) { btnsNext[questionIndex].disabled = false; } else { btnsNext[questionIndex].disabled = true; } }); }); });
const quizzes = document.querySelectorAll(".calc__fields"); quizzes.forEach((quiz) => { const questions = quiz.querySelectorAll(".fields__field"), btnsPrev = quiz.querySelectorAll(".btn-prev"), btnsNext = quiz.querySelectorAll(".btn--icon-next"), stagesWrapper = quiz.querySelector(".fields__stages"); stages = quiz.querySelectorAll(".fields__stage"); // progress const progressCount = questions.length; btnsNext.forEach((btnNext, btnNextIndex) => { btnNext.addEventListener("click", (e) => { e.preventDefault(); questions[btnNextIndex].classList.remove("_active"); questions[btnNextIndex + 1].classList.add("_active"); stages[btnNextIndex].classList.remove("_active"); stages[btnNextIndex + 1].classList.add("_active"); }); if (!questions[btnNextIndex].classList.contains("_range")) { btnNext.disabled = true; } }); btnsPrev.forEach((btnPrev, btnPrevIndex) => { btnPrev.addEventListener("click", (e) => { e.preventDefault(); questions[btnPrevIndex + 1].classList.remove("_active"); questions[btnPrevIndex].classList.add("_active"); // count--; // currentNum.innerHTML = count; }); }); questions.forEach((question, questionIndex) => { if (questionIndex === 0) { question.classList.add("_active"); } else { question.classList.remove("_active"); } const count = questionIndex + 1; if (count === 1) { stagesWrapper.innerHTML += '<li class="fields__stage _active">' + "Шаг " + count + "</li>"; } else { stagesWrapper.innerHTML += '<li class="fields__stage">' + "Шаг " + count + "</li>"; } question.addEventListener("change", (e) => { const target = e.target, inputsChecked = question.querySelectorAll("input:checked"); if (inputsChecked.length > 0 || target.classList.contains("range__input")) { btnsNext[questionIndex].disabled = false; } else { btnsNext[questionIndex].disabled = true; } }); }); });
- anmkh, А зачем так делать? Твой первоначальный код кое-как работал, я немного его изменил -> начали двигаться шаги, замени просто то что было на то что я скинул
- Даниил Гладыш, А если количество шагов может быть разным? Не на беке же через форич прогонять
- anmkh, Ну так в комментариях тебе писали, что у тебя разметка несколько раз повторяется. Если уж исправлять это то не только шагами. Тебе нужно сделать шаблон, а вопросы, варианты ответов и итератор формы хранить в скрипте. Кнопкой прибавлять 1 к итератору и обращаясь к спискам менять вопрос, варианты ответов.
- Даниил Гладыш, Ок, есть дока как мне из php вопросы и типы инпутов тянуть в js?
- https/stackoverflow.com/questions/415868/get-variable-from-php-to-javascript
- Сделай на базе слайдера - swiper, slick итп
- Александр Васильев, Как вариант кстати. Не колхоз получится?
- anmkh, всегда так делаю, там уже все готово
- anmkh, могу пример в личку дать
- Александр Васильев, Да на свайпере есть понимание как сделать, только пишут что данные нужно выносить. Я в принципе проблемы не вижу чтобы их не выносить, но опыта меньше и поэтому не знаю чем чревано держать все в разметке. Там же не проверяется корректность выбранного поля,а просто форма с шагами
- anmkh, да, все это обернуть в форму, на финале кнопку next скрыть и submit открыть. Также сделать запрет на отправку через enter, пока до конца не дойдешь либо вообще.
- Александр Васильев, Даниил Гладыш, ребята, вы настолько дали расширенный ответ, что реально, я лично, не могу пройти мимо ваших ответов.
Для anmkh, осталось только правильно обернуть всё это в код. В логический и правильный, но это уже другая история..
Вам же только БРАВО!.
anmkh, вы можете отслеживать все события, которые возникают непосредственно в форме.
Слушай форму, а не то, что вокруг неё. Инициализируй скрипт только тогда, когда форма доступна. Инициализация скрипта подразумевает под собой то, что ты навешиваешь события сам на все контролы, которые ты сам хочешь отследить. И только ты сам можешь от этих событий отписаться или переопределить эти события под своё усмотрение.
Может быть я сложно навожу на мысль, но твой ход событий правильный.
Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.
Пока нет других ответов. Будьте первым, кто поможет автору.
Ответить на вопрос
Для динамического вывода шагов в квизе и добавления класса активному шагу, можно воспользоваться следующим подходом. Для начала, создадим HTML структуру для квиза, где каждый шаг будет представлен отдельным элементом. Например:
<div class="quiz"> <div class="step active">Шаг 1</div> <div class="step">Шаг 2</div> <div class="step">Шаг 3</div> <div class="step">Шаг 4</div> </div><div class="quiz"> <div class="step active">Шаг 1</div> <div class="step">Шаг 2</div> <div class="step">Шаг 3</div> <div class="step">Шаг 4</div> </div>
Затем, добавим JavaScript код для обработки событий и динамического изменения классов:
document.addEventListener('DOMContentLoaded', function() { const steps = document.querySelectorAll('.step'); steps.forEach((step, index) => { step.addEventListener('click', () => { steps.forEach(s => s.classList.remove('active')); step.classList.add('active'); }); }); });
В данном примере, при клике на каждый шаг квиза, удаляем класс 'active' у всех шагов и добавляем его только активному шагу. Таким образом, можно отслеживать текущий шаг квиза и стилизовать его соответственно.
Также, можно добавить дополнительную логику для переключения шагов в зависимости от действий пользователя или других условий. Например, можно добавить проверку на заполнение всех обязательных полей перед переходом к следующему шагу.
Таким образом, данный подход позволяет динамически выводить шаги квиза и добавлять класс активному шагу, что делает процесс прохождения квиза более удобным и интерактивным для пользователя.