To give overview, I’ve just started building this website and wanted to take a different approach to building a form for onboarding / purchasing a service.
I’ve tried about 10 different approaches in attempts to achieve my desired behaviour but still nothing is working how I expected.
const packageTypes = document.querySelectorAll('.package_type');
const deliveryTimeSelect = document.querySelector('#delivery_time');
const providerSelect = document.querySelector('#provider');
const uniqueTemplatesInput = document.querySelector('#unique_templates');
function updateOrderTotal() {
// Get the value of the selected package type
let packageTypeValue = 0;
packageTypes.forEach((packageType) => {
const radio = packageType.querySelector('input[type="radio"]:checked');
if (radio) {
packageTypeValue = parseFloat(radio.value);
}
});
// Get the value of the delivery time select box
const deliveryTimeValue = parseFloat(deliveryTimeSelect.value) || 0;
// Get the value of the provider select box
const providerValue = parseFloat(providerSelect.value) || 0;
// Get the value of the unique templates input
const uniqueTemplatesValue = parseFloat(uniqueTemplatesInput.value) || 0;
// Calculate the order total
const orderTotal = (packageTypeValue + deliveryTimeValue + providerValue) * uniqueTemplatesValue;
// Update the order_total div with the calculated value
const orderTotalDiv = document.querySelector('#order_total');
orderTotalDiv.innerHTML = orderTotal.toFixed(2);
}
// Add event listeners to the delivery time, provider, and unique templates select boxes
deliveryTimeSelect.addEventListener('change', updateOrderTotal);
providerSelect.addEventListener('change', updateOrderTotal);
uniqueTemplatesInput.addEventListener('input', updateOrderTotal);
// Loop over all package_type divs
packageTypes.forEach((packageType) => {
// Add a click event listener to each package_type div
packageType.addEventListener('click', (event) => {
// Remove the "selected" class from all package_type divs
packageTypes.forEach((p) => {
p.classList.remove('selected');
});
// Add the "selected" class to the clicked package_type div
packageType.classList.add('selected');
// Update the order total whenever the package type is changed
updateOrderTotal();
});
// Add a change event listener to each radio button
const radio = packageType.querySelector('input[type="radio"]');
radio.addEventListener('change', (event) => {
// Update the order total whenever a radio button is selected
updateOrderTotal();
});
});
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@300;400;500;600;700&display=swap');
@import url('grid.css');
html,
body {
margin: 0;
padding: 24px;
background-color: #f0f0f0;
font-family: 'DM Sans', Sans-serif;
font-size: 16px;
height: 100%;
width: 100%;
color: #0E1C1B;
font-smooth: always;
scroll-behavior: smooth;
box-sizing: border-box;
}
button {
border-radius: 4px;
background-color: #0E1C1B;
border: solid 1px #0E1C1B;
color: #ffffff;
font-family: inherit;
font-size: 16px;
padding: 12px 16px;
font-weight: 500;
cursor: pointer;
-webkit-transition: .4s;
-moz-transition: .4s;
transition: .4s;
}
button:hover {
border: solid 1px #00F0E2;
color: #00F0E2;
-webkit-transition: .4s;
-moz-transition: .4s;
transition: .4s;
}
button:disabled,
button[disabled] {
opacity: 0.2;
cursor: no-drop;
}
.mar-left-16 {
margin-left: 16px !important;
}
input {
width: 100%;
margin-bottom: 16px;
font-size: 16px;
line-height: 16px;
font-family: inherit;
color: #0E1C1B;
border: solid 1px #0E1C1B;
padding: 12px;
outline: none;
border-radius: 4px;
background-color: transparent;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-transition: .2s;
-moz-transition: .2s;
transition: .2s;
}
input:focus {
background-color: #ffffff;
-webkit-transition: .2s;
-moz-transition: .2s;
transition: .2s;
}
select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
width: 66%;
color: #0E1C1B;
border: solid 1px #0E1C1B;
font-family: inherit;
-webkit-transition: .2s;
-moz-transition: .2s;
transition: .2s;
box-sizing: border-box;
padding: 12px;
margin-bottom: 16px;
font-size: 16px;
line-height: 16px;
background-color: #ffffff;
cursor: pointer;
}
h1 {
font-size: 40px;
line-height: 48px;
}
h2 {
font-size: 32px;
line-height: 40px;
}
h3 {
font-size: 24px;
line-height: 32px;
}
p {
font-size: 16px;
line-height: 24px;
}
.step_title {
font-size: 24px;
color: #0E1C1B;
margin-top: 64px;
margin-bottom: 32px;
clear: both;
}
.step_count {
font-size: 16px;
line-height: 24px;
font-weight: 500;
width: 32px;
height: 32px;
line-height: 32px !important;
background-color: #00F0E2;
border-radius: 32px;
text-align: center;
float: left;
font-weight: bold;
margin-right: 16px;
}
.packages {
width: 66%;
clear: both;
}
.package_type {
width: 50%;
float: left;
margin: 0;
padding: 0;
background-color: #ffffff;
padding: 0px 16px;
height: 160px;
box-sizing: border-box;
border: solid 1px #0E1C1B;
cursor: pointer;
position: relative;
}
.package_type:hover {
background-color: #00F0E2;
}
.package_type:active {
opacity: 0.5;
}
.package_type:nth-child(2) {
border-left: none;
}
.package_type input[type="radio"] {
display: none;
}
.package_type label {
display: block;
cursor: pointer;
}
.package_type.selected {
background-color: #00F0E2;
}
.package_type h1 {
margin: 0;
padding: 0;
font-size: 24px;
line-height: 32px;
text-align: center;
box-sizing: border-box;
margin-top: 32px;
}
.package_type p {
text-align: center;
}
.clearfix {
clear: both;
}
.quantity {
position: relative;
}
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type=number] {
-moz-appearance: textfield;
}
.quantity input {
width: 66%;
height: 42px;
line-height: 1.65;
float: left;
display: block;
padding: 0;
margin: 0;
padding-left: 20px;
border: solid 1px #0E1C1B;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.08);
font-size: 1rem;
border-radius: 4px;
background-color: #ffffff;
}
.quantity input:focus {
outline: 0;
}
.quantity-nav {
float: left;
position: relative;
height: 42px;
}
.quantity-button {
position: relative;
cursor: pointer;
border: none;
border-left: 1px solid #0E1C1B;
width: 21px;
text-align: center;
color: #333;
font-size: 13px;
font-family: "FontAwesome" !important;
line-height: 1.5;
padding: 0;
background: #FAFAFA;
-webkit-transform: translateX(-100%);
transform: translateX(-100%);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
.quantity-button:active {
background: #EAEAEA;
}
.quantity-button.quantity-up {
position: absolute;
height: 50%;
top: 0;
border-bottom: 1px solid #0E1C1B;
border-top: 1px solid #0E1C1B;
border-right: 1px solid #0E1C1B;
font-family: "FontAwesome";
border-radius: 0 4px 0 0;
line-height: 1.6
}
.quantity-button.quantity-down {
position: absolute;
bottom: 0;
height: 50%;
font-family: "FontAwesome";
border-radius: 0 0 4px 0;
border-bottom: 1px solid #0E1C1B;
border-right: 1px solid #0E1C1B;
}
textarea {
width: 100%;
font-family: inherit;
font-size: 16px;
padding: 12px;
line-height: 24px;
resize: none;
min-height: 240px;
border: solid 1px #0E1C1B;
background-color: #ffffff;
color: #0E1C1B;
box-sizing: border-box;
outline: none;
}
#order_screen textarea {
width: 66%;
}
.upload_btn {
margin-bottom: 16px;
}
.screen_contain {
position: relative;
}
#order_summary_parent {
width: 100%;
}
#order_summary {
position: absolute;
right: 0;
top: 63px;
width: 30%;
background-color: #0E1C1B;
min-height: 200px;
box-sizing: border-box;
padding: 24px;
color: #ffffff;
}
#order_summary h1 {
font-size: 24px;
line-height: 32px;
margin: 0;
}
#order_summary button {
background-color: #00F0E2;
color: #0E1C1B;
width: 100%;
margin-top: 64px;
}
.fixed {
position: fixed;
top: 86px;
right: 0;
width: 100%;
}
@media only screen and (max-width: 820px) {
.packages {
width: 100%;
}
.package_type {
width: 100%;
}
.package_type:nth-child(2) {
border-left: solid 1px #0E1C1B;
border-top: none;
}
#order_screen textarea {
width: 100%;
}
.upload_btn {
width: 100%;
}
select {
width: 100%;
}
.quantity input {
width: 100%;
}
#order_summary {
position: static;
right: 0;
top: 0px;
width: 100%;
background-color: #0E1C1B;
min-height: 200px;
box-sizing: border-box;
padding: 24px;
color: #ffffff;
margin-top: 64px;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h2>
Place your email template order here
</h2>
<div id="email_template_order">
<div class="screen_contain">
<div class="step_title">
<div class="step_count">
1
</div>Choose your package
</div>
<div class="packages">
<label for="code_only_email">
<div class="package_type">
<input id="code_only_email" name="package_type_1" type="radio" value="50.00"> </label>
<h1>
Code Only
</h1>
<p>
We’ll hand code your designs to HTML
</p>
</div>
</label>
<label for="code_design_email">
<div class="package_type">
<input id="code_design_email" name="package_type_2" type="radio" value="130.00">
<h1>
Design + Code
</h1>
<p>
We’ll create eye catching designs & then code them into HTML for you
</p>
</div>
</label>
</div>
<div class="clearfix"></div>
<div class="step_title">
<div class="step_count">
2
</div>Number of unique templates
</div>
<div class="quantity">
<input max="9" min="1" step="1" id="unique_templates" type="number" value="1">
</div>
<div class="clearfix"></div>
<div class="step_title">
<div class="step_count">
3
</div>Delivery time
</div>
<div id="deliveryTime">
<div class="custom-select">
<select id="delivery_time">
<option>
Select time:
</option>
<option value="72">
72 hrs (£48)
</option>
<option value="48">
48 hrs (£56)
</option>
<option value="24">
24 hrs (£75)
</option>
<option value="12">
12 hrs (£99)
</option>
<option value="8">
8 hrs (£112)
</option>
</select>
</div>
<div class="clearfix"></div>
<div class="step_title">
<div class="step_count">
4
</div>Email Service Provider
</div>
</div>
<div id="email_provider">
<div class="custom-select">
<select id="provider">
<option>
Select Provider:
</option>
<option name="adestra" value="31">
Adestra (£31)
</option>
<option name="exacttarget" value="31">
Exact Target SFMC (£31)
</option>
<option name="campaignmonitor" value="23">
Campaign Monitor (£23)
</option>
<option name="mailchimp" value="23">
Mailchimp (£23)
</option>
<option name="other" value="40">
Other (£40)
</option>
</select>
</div>
</div>
<div class="clearfix"></div>
<div class="step_title">
<div class="step_count">
5
</div>Interactive Add-Ons
</div>
<div class="clearfix"></div>
<div class="step_title">
<div class="step_count">
6
</div>Project details
</div>
<textarea placeholder="Kindly share detailed information regarding usage/coding standards of this Email. Attach design files in layered PSD/AI/EPS/PDF/SKETCH/ZIP format."></textarea>
<div class="clearfix"></div>
<div class="step_title">
<div class="step_count">
7
</div>Supply brief
</div>
<button class="upload_btn">Upload brief for this email template</button>
<input type="checkbox" style="width: auto; margin-left: 16px; margin-right: 16px;">I do not want to upload a brief.
<div id="order_summary_parent">
<div id="order_summary">
<h1>Order summary</h1>
£<span id="order_total">0</span>
<button>Complete order</button>
</div>
</div>
</div>
</div>
I am expecting to be able to update order_total automatically when something is changed in the form.. If the package_type is changed, the order_total updates, etc etc..
So far, if you click a package type, in this example the packages are called "code only" and "code and design", it will update the order_total the first time you click them, but if you toggle between then again, nothing changes.. also, I can’t seem to get the order total to multiple by the input id "unique_templates", it only seems to update when I select another input first, then hit the multiplier, then select another input, then it works. (CONFUSING Ey.)
I just want it to be super simple and I think I’ve over complicated this all a little.
Everything is in here to see an example of my problem
2
Answers
@ChrisHaas kindly solved the issue.
It was as simple as the radio button names needing to be the same name.
I also added the updateOrderTotal(); to the btnUp.click and btnDown.click and that solved the quantity issue too.
Thank you for all who took time to read my mess.
Have a great rest of your week. 🙏🏽
Just extracted a part of your code to have a better overview.
As already stated, the radio input elements only work if the
name
is the same.(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/radio)
Also, you do not need two event listeners for
'change'
and for'click'
, one is enough.