Я пытаюсь создать многошаговую форму, используя карусель Slick для продукта по подписке. В форме предполагается указать пол, размер, предметы, а затем выставить счет. В конце формы я хочу отобразить выбор пользователя со ссылкой на продукт, основанный на выборе пользователя.
Я нашел веб-сайт, который делает почти то, что я хочу — https://www.gruntstyle.com/pages/club-grunt-style
Вот что у меня есть на данный момент - https://codepen.io/Alex-Baker-the-decoder/pen/MWxdKBa
$(document).ready(function () {
// Define products
const products = [
{
gender: "male",
size: "s",
product: "SPIKE SHIRT BOX",
billing: "1st Month",
productName: "Men’s Small Shirt - Monthly",
link: ""
},
{
gender: "female",
size: "2XL",
product: "shirt",
billing: "monthly",
productName: "Women’s 2XL Shirt - Monthly",
link: ""
}
];
// Hide all steps except the first one initially
$(".join-club-steps:not(:first)").hide();
// When a radio button or select option is changed
$('input[type = "radio"]').on("change", function () {
var currentStep = $(this).closest(".join-club-steps");
var nextStep = currentStep.next(".join-club-steps");
// Show final slide if on the last step and valid
if (nextStep.length === 0 && isStepValid(currentStep)) {
showFinalSlide();
} else {
navigateSteps(this, "next");
}
});
// Function to check if the step is valid
function isStepValid(step) {
var stepId = step.attr("id");
switch (stepId) {
case "join-step-gender":
return $('input[name = "gender"]:checked').length > 0;
case "join-step-size":
return $('input[name = "size"]:checked').length > 0;
case "join-step-product":
return $('input[name = "product"]:checked').length > 0;
case "join-step-billing":
return $('input[name = "billing"]:checked').length > 0;
default:
return true;
}
}
// Function to display the final slide with user selections
function showFinalSlide() {
var selectedGender = $('input[name = "gender"]:checked').val();
var selectedSize = $('input[name = "size"]:checked').val();
var selectedProduct = $('input[name = "product"]:checked').val();
var selectedBilling = $('input[name = "billing"]:checked').val();
// Update final slide with user's selections
$("#selectedGender").text(selectedGender);
$("#selectedSize").text(selectedSize);
$("#selectedProduct").text(selectedProduct);
$("#selectedBilling").text(selectedBilling);
// Hide all steps and show final slide
$(".join-club-steps").hide();
$(".final-slide").show();
}
// Function to navigate steps
function navigateSteps(element, direction) {
var currentStep = $(element).closest(".join-club-steps");
var stepIndex = $(".join-club-steps").index(currentStep) + 1; // Get current step index
var targetStepIndex = direction === "next" ? stepIndex + 1 : stepIndex - 1;
if (
direction === "next" &&
targetStepIndex <= $(".join-club-steps").length
) {
showStep(targetStepIndex);
}
}
// Function to show a specific step
function showStep(stepNumber) {
$(".join-club-steps").hide(); // Hide all steps
$(".join-club-steps")
.eq(stepNumber - 1)
.show(); // Show the target step
highlightCurrentStep(stepNumber);
}
// Function to highlight the current step in the progress bar
function highlightCurrentStep(stepNumber) {
$(".circle").removeClass("active"); // Remove active class from all circles
$(".circle[data-step='" + stepNumber + "']").addClass("active"); // Add active class to the current circle
}
// Navigate to a specific step by clicking on the progress bar
function navigateToStep(stepNumber) {
showStep(stepNumber);
}
// Reset button functionality
$("#reset-btn").click(function () {
resetForm();
});
// Function to reset the form and go back to the first step
function resetForm() {
$('input[type = "radio"]').prop("checked", false);
$("select").val($("select option:first").val());
showStep(1); // Show the first step
}
// Initial setup
highlightCurrentStep(1); // Highlight step 1
});/* Center everything */
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 1500px;
background-color: #000;
padding-bottom: 100px;
}
/* Style each step */
.join-club-steps {
background-color: #fff;
padding: 20px;
border: 0px solid #ddd;
border-radius: 8px;
background-color: #000;
margin-bottom: 20px;
color: #fff;
}
/* Style the progress bar */
#join-club-progress-bar {
display: flex;
justify-content: center;
align-items: center;
}
#join-club-progress-bar ul {
list-style: none;
padding: 0;
display: flex;
}
#join-club-progress-bar ul li {
margin: 0 10px;
}
.circle {
width: 40px;
height: 40px;
background-color: #ddd;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
font-size: 20px;
color: #f1402b;
cursor: pointer;
}
.active {
background-color: #f1402b;
color: #fff;
}
.gender_male {
font-size: 25px;
border: solid 2px #fff;
padding: 25px;
color: #fff;
}
.gender_male:hover {
font-size: 25px;
border: solid 2px #f1402b;
padding: 25px;
cursor: none !important;
background-color: #f1402b;
}
.gender_female {
font-size: 25px;
border: solid 2px #fff;
padding: 25px;
color: #fff;
}
.gender_female:hover {
font-size: 25px;
border: solid 2px #f1402b;
padding: 25px;
cursor: none !important;
background-color: #f1402b;
}
.size_club {
font-size: 40px;
border: solid 2px #fff;
padding: 25px;
color: #fff;
}
.size_club:hover {
font-size: 40px;
border: solid 2px #f1402b;
padding: 25px;
cursor: none !important;
background-color: #f1402b;
}
.product_club {
font-size: 25px;
border: solid 2px #fff;
padding: 25px;
text-align: center;
max-width: 280px;
color: #fff;
}
.product_club:hover {
font-size: 25px;
border: solid 2px #f1402b;
padding: 25px;
text-align: center;
max-width: 280px;
cursor: none !important;
background-color: #f1402b;
}
.billing_club {
font-size: 25px;
border: solid 2px #fff;
padding: 25px;
text-align: center;
max-width: 280px;
color: #fff;
}
.billing_club:hover {
font-size: 25px;
border: solid 2px #f1402b;
padding: 25px;
text-align: center;
max-width: 280px;
cursor: none !important;
background-color: #f1402b;
}
#product_detail_club {
font-size: 14px;
text-align: center;
color: #fff;
}
.feq_club {
font-size: 25px;
border: solid 2px #fff;
padding: 25px;
text-align: center;
max-width: 280px;
color: #fff;
}
.feq_club:hover {
font-size: 25px;
border: solid 2px #f1402b;
padding: 25px;
text-align: center;
max-width: 280px;
cursor: none !important;
background-color: #f1402b;
}
#feq_detail_club {
font-size: 14px;
text-align: center;
color: #fff;
}
.hide_radio {
display: none;
}
button,
input,
select,
textarea {
font-family: inherit;
font-size: 25px;
line-height: inherit;
background-color: #f1402b;
color: #fff;
width: 100%;
margin-bottom: 10px;
}<script src = "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<div id = "steps-container" class = "container">
<img src = "https://ucarecdn.com/ffcf27ab-90b2-46ac-ae8d-83b11d8ada7c/-/format/auto/-/preview/3000x3000/-/quality/lighter/ClubAdx%20Logo%20w%20box.png" width = "500">
<div id = "steps" class = "slick-initialized slick-slider">
<!-- Step slides -->
<div class = "join-club-steps" id = "join-step-gender">
<!-- Step 1 Content: Pick Gender -->
<h2>Step 1: Pick Gender</h2>
<label class = "gender_male"><input class = "hide_radio" type = "radio" name = "gender" value = "male"> Male</label>
<label class = "gender_female"><input class = "hide_radio" type = "radio" name = "gender" value = "female"> Female</label>
</div>
<div class = "join-club-steps" id = "join-step-size">
<!-- Step 2 Content: Pick Size -->
<h2>Step 2: Pick Size</h2>
<label class = "size_club"><input class = "hide_radio" type = "radio" name = "size" value = "s"> S</label>
<label class = "size_club"><input class = "hide_radio" type = "radio" name = "size" value = "m"> M</label>
<label class = "size_club"><input class = "hide_radio" type = "radio" name = "size" value = "l"> L</label>
<label class = "size_club"><input class = "hide_radio" type = "radio" name = "size" value = "xl"> XL</label>
<label class = "size_club"><input class = "hide_radio" type = "radio" name = "size" value = "2xl"> 2XL</label>
</div>
<div class = "join-club-steps" id = "join-step-product">
<!-- Step 3 Content: Pick Product -->
<h2>Step 3: Pick Product</h2>
<label class = "product_club"><input class = "hide_radio" type = "radio" name = "product" value = "SPIKE SHIRT BOX"> SPIKE SHIRT BOX<br><span id = "product_detail_club">Receive One Shirt Each Month</span></label>
<label class = "product_club"><input class = "hide_radio" type = "radio" name = "product" value = "SPIKE HAT BOX"> SPIKE HAT BOX<br><span id = "product_detail_club">Receive One Hat Each Month</span></label>
<label class = "product_club"><input class = "hide_radio" type = "radio" name = "product" value = "TROPHY COMBO BOX"> TROPHY COMBO BOX<br><span id = "product_detail_club">2 Items - Receive One Shirt AND One Hat Each Month </span></label>
<label class = "product_club"><input class = "hide_radio" type = "radio" name = "product" value = "DEAL BOX"> DEAL BOX<br><span id = "product_detail_club">Hat OR Shirt from PAST Designs to Save You Money Each Month Alternates Between Shirt and Hat</span></label>
</div>
<div class = "join-club-steps" id = "join-step-billing">
<!-- Step 4 Content: Pick Billing Frequency -->
<h2>Step 4: Pick Billing Frequency</h2>
<label class = "billing_club"><input class = "hide_radio" type = "radio" name = "billing" value = "1st Month"> 1st Month $1.00<br><span id = "product_detail_club">THEN $28 MONTHLY 6 MONTH MIN AGREEMENT</span></label>
<label class = "feq_club"><input class = "hide_radio" type = "radio" name = "billing" value = "Monthly"> Monthly<br><span id = "feq_detail_club">$28.00 every month, no contract</span></label>
<label class = "feq_club"><input class = "hide_radio" type = "radio" name = "billing" value = "3 Months"> 3 Months<br><span id = "feq_detail_club">$25.50/SHIRT $75.60 ONE TIME </span></label>
<label class = "feq_club"><input class = "hide_radio" type = "radio" name = "billing" value = "6 Months"> 6 Months<br><span id = "feq_detail_club">$23.80/SHIRT $142.80 ONE TIME</span></label>
<label class = "feq_club"><input class = "hide_radio" type = "radio" name = "billing" value = "12 Months"> 12 Months<br><span id = "feq_detail_club">$22.40/SHIRT $268.80 ONE TIME</span></label>
</div>
<!-- Final slide to show user selections -->
<div class = "join-club-steps final-slide">
<h2>Your Selections</h2>
<p>Gender: <span id = "selectedGender"></span></p>
<p>Size: <span id = "selectedSize"></span></p>
<p>Product: <span id = "selectedProduct"></span></p>
<p>Billing Frequency: <span id = "selectedBilling"></span></p>
<a href = "" id = "product-link" target = "_blank"><button id = "join-now-btn">Join Now</button></a>
<button id = "reset-btn">Reset</button>
</div>
</div>
<div id = "join-club-progress-bar">
<ul>
<li>
<div class = "circle" data-step = "1">1</div>
</li>
<li>
<div class = "circle" data-step = "2">2</div>
</li>
<li>
<div class = "circle" data-step = "3">3</div>
</li>
<li>
<div class = "circle" data-step = "4">4</div>
</li>
</ul>
</div>
</div>Заранее спасибо!
Я проверил, что идентификаторы, используемые для обновления окончательного содержимого слайда (selectedGender, selectedSize, selectedProduct, selectedBilling), соответствуют идентификаторам, указанным в моем HTML.
Я проверил, что привязка событий для изменений переключателей работает правильно и что событие изменения запускается, как ожидалось.
Форма перейдет на финальный слайд, но не будет отображать введенные пользователем данные и ссылку на продукт, которому она соответствует.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вам нужно немного настроить nextStep в обработчике событий change для ваших переключателей.
Вместо
$('input[type = "radio"]').on("change", function () {
var currentStep = $(this).closest(".join-club-steps");
var nextStep = currentStep.next(".join-club-steps");
// Show final slide if on the last step and valid
if (nextStep.length === 0 && isStepValid(currentStep)) {
showFinalSlide();
} else {
navigateSteps(this, "next");
}
});
сделай это
$('input[type = "radio"]').on("change", function () {
var currentStep = $(this).closest(".join-club-steps");
var nextStep = currentStep.next(".join-club-steps:not(.final-slide)"); // <--- this was added
// Show final slide if on the last step and valid
if (nextStep.length === 0 && isStepValid(currentStep)) {
showFinalSlide();
} else {
navigateSteps(this, "next");
}
});
Слайд :not(.final-slide) необходим, потому что без него nextStep подбирает элемент с классом .final-slide. Причина такого выбора в том, что у него также есть класс .join-club-steps.
Если вы не уверены в моем объяснении, сделайте следующее. Первым делом в коде Javascript перед $(document).ready(...) добавьте что-то вроде var tmp;. Затем внутри $('input[type = "radio"]').on("change", function () {...}); добавьте tmp = $(this);. Это установит переменную tmp в значение, проверенное на последнем шаге, и затем вы сможете вручную, через консоль браузера, сделать что-то вроде
console.info(tmp);
console.info(tmp.closest('.join-club-steps'));
console.info(tmp.next('.join-club-steps'));
чтобы лучше понять, что происходит. Конечно, как только вы это сделаете, вам не нужно будет оставлять tmp и его использование в своем коде.
Кроме того, вам, вероятно, следует решить проблему CLS, которая возникает при изменении шагов.
РЕДАКТИРОВАТЬ
Как предложил Марк Шультайс в комментариях, поскольку currentStep и nextStep не будут изменены в течение всего времени существования контекста, в котором они использовались (внутренние переменные внутри анонимной функции), вместо использования
$('input[type = "radio"]').on("change", function () {
var currentStep = $(this).closest(".join-club-steps");
var nextStep = currentStep.next(".join-club-steps:not(.final-slide)");
// rest of your code for this event handler
});
вы можете изменить его, чтобы он выглядел так:
$('input[type = "radio"]').on("change", function () {
const currentStep = $(this).closest(".join-club-steps");
const nextStep = currentStep.next(".join-club-steps:not(.final-slide)");
// rest of your code for this event handler
});
Это также можно сделать и в других частях кода, где переменные объявляются внутри ваших функций (isStepValid, showFinalSlide, navigateSteps).
Это не является абсолютной необходимостью — в данном случае ваш код будет работать как с var, так и с const. Но желательно внести это изменение — см. следующий ТАК-ответ, чтобы лучше понять, почему.
рассмотрите возможность предложения таких объявлений, как
const currentStep, поскольку они не изменятся.