Am I over complicating my form validation?
I am trying to make my booking form a little more user friendly... I have three sections...
- First is a first/last name, email and phone number.
- Second is a calendar to pick your date
- Finally there is a dropdown select that shows a list of different time slots that are available.
It looks like this:
I am using tzjs to get the users time zone, and I am using moment.js to compare dates and times.
The reason this form is complex is because I am only showing dates and times that are one hour in the future of that users time zone.
I am auto detecting the time zone and also ding some animations, for example if the user selects a date without putting in their personal details they will be scrolled up to the first section and the title will turn red and 'shake'.
Q: I am wondering if my code is inefficient and all over the place.
- $(function(){
persistInput();
var timezone = jstz.determine(); // get users timezone string
$('#timeZone').val(timezone.name()); // set the name in the hidden form
var date = new Date();
var offset = -date.getTimezoneOffset() + 60; // get users timezone offset (plus 60 minutes)
/**
* if serverside validation fails, the user doesn't lose the date that they have chosen
*/
function persistInput(){
if(okStepTwo()){
days = document.getElementsByClassName('day'); // get array of .day classes
for(i = 0; i < days.length; i++){ // foreach element check if its data attribute is before today using the users timezone offset
var startDate = $('#startDate').val();
if ($(days[i]).data('date') == startDate) {
$('#showStartDate').html(startDate);
$(days[i]).addClass('day-selected'); // if it is change its class to disabled
}
}
}
}
/**
* validate step-1 (about you)
*/
$('#firstName, #lastName, #email, #phone').change(function () {
var a = $('#firstName').val();
var b = $('#lastName').val();
var c = $('#email').val();
var d = $('#phone').val();
if (a == '', b == '', c == '', d == ''){
return false;
}
$("#stepOne").removeClass('text-danger').addClass('text-success');
validate();
});
days = document.getElementsByClassName('day'); // get array of .day classes
for(i = 0; i < days.length; i++){ // foreach element check if its data attribute is before today using the users timezone offset
if (validateDate($(days[i]).data('date')) == false) {
$(days[i]).addClass('day-expired'); // if it is change its class to disabled
} else {
$(days[i]).addClass('day-active'); // else make it selectable
}
}
/**
* check if a date is in the past
* @param date string
*/
function validateDate(thisDate) {
var dateToday = moment().utcOffset(offset);
var selectedDate = moment(thisDate, ['YYYY-MM-DD']);
var dateToday = dateToday.format('YYYY-MM-DD');
var selectedDate = selectedDate.format('YYYY-MM-DD');
var dateToday = moment(dateToday, ['YYYY-MM-DD']);
var selectedDate = moment(selectedDate, ['YYYY-MM-DD']);
if (selectedDate.isBefore(dateToday, 'day')) {
return false;
}
return true;
}
/**
* get the date (step-2)
* @TODO: disable dates in the past according to a users time zone
*/
$('.day-active').click(function(){
var date = $(this).data('date'); // get the date attribute
$('.day-active').removeClass('day-selected'); // remove the selected class from all elements
$(this).addClass('day-selected'); // and apply it to just this element
$('#showStartDate').html(date); // show the selected date to the user
$('#startDate').val(date); // and put it in the hidden form
$('#stepTwo').removeClass('text-danger').addClass('text-success'); // make header green
filterTimeSlots();
validate();
});
/**
* filters out time slots that are in the past
*/
function filterTimeSlots(){
timeSlots = document.getElementsByClassName('time-slot'); // get array of .day classes
for(i = 0; i < timeSlots.length; i++){ // foreach element check if its data attribute is before today using the users timezone offset
if (validateTime($(timeSlots[i]).val()) == false) { // hide expired timeslots
$(timeSlots[i]).hide(); // if it is change its class to disabled
} else {
$(timeSlots[i]).show(); // else make sure it's visible
}
}
}
/**
* validate start time
*/
function validateTime(timeSlot){
date = $('#startDate').val();
var selected_datetime = date + ' ' + timeSlot;
var current_datetime = moment().utcOffset(offset).format('YYYY-MM-DD HH:mm A');
var selectedDateTime = moment(selected_datetime, ["YYYY-MM-DD HH:mm A"]).utcOffset(offset);
var currentDateTime = moment(current_datetime, ["YYYY-MM-DD HH:mm A"]).utcOffset(offset);
if (selectedDateTime.isBefore(currentDateTime, 'minute')) {
return false;
}
return true;
}
/**
* step-3 (start time)
*/
$("#getStartTime").on('change', function() {
$("#showStartTime").html(this.value);
$("#startTime").val(this.value);
$('#stepThree').removeClass('text-danger').addClass('text-success');
validate();
});
/**
*
*/
$('#submitButton').click(function(){
validate();
});
/**
* validate everything
*/
function validate(){
if(okStepOne()){
if(okStepTwo()){
if(okStepThree()){
$('#submitButton').attr('type', 'submit').removeClass('disabled').addClass('btn-primary-cta animated shake');
$('html, body').animate({ // scroll to step one and shake
scrollTop: $("#submitButton").offset().top - 100,
}, 300);
} else {
// $('#stepThree').addClass('animated shake text-danger');
}
} else {
}
} else { // show scroll to step one and shake
if(okStepTwo()){
} else {
if(okStepThree()){
} else {
$('#stepThree').addClass('animated shake text-danger')
}
$('#stepTwo').addClass('animated shake text-danger')
}
$('html, body').animate({ // scroll to step one and shake
scrollTop: $("#sectionOne").offset().top - 100,
}, 300);
$('#stepOne').addClass('animated shake text-danger');
}
}
/**
* check if there are values in the user details page
*/
function okStepOne() {
var a = $('#firstName').val();
var b = $('#lastName').val();
var c = $('#email').val();
var d = $('#phone').val();
if (a == '', b == '', c == '', d == ''){
return false;
}
return true;
}
/**
* check if there is a selected start date
*/
function okStepTwo(){
if($('#startDate').val() !== ''){
return true;
}
return false;
}
/**
* check if there is a selected start time
*/
function okStepThree(){
if($('#startTime').val() !== ''){
return true;
}
return false;
}
});
Here is my HTML Markup...
- <div class="container padding-section-large">
<!-- progress bar -->
<div class="row padding-angled-small">
<div class="col-md-8 col-md-offset-2">
<ul class="progressbar progressbarfree">
<li class="active">
<?php echo $this->lang->line('progress_bar_select_course'); ?>
</li>
<li>
<?php echo $this->lang->line('progress_bar_pick_date'); ?>
</li>
<li>
<?php echo $this->lang->line('progress_bar_success'); ?>
</li>
</ul>
</div>
</div>
<!-- end progress bar -->
<!-- course details -->
<div class="row blurb-box">
<div class="col-md-8">
<h1 class="heading-border-left">
<?php echo $this->lang->line('book_evaluation_title'); ?>
</h1>
<h4>
<?php echo $this->lang->line('book_evaluation_description'); ?>
</h4>
<p class="footnote">
<?php echo $this->lang->line('book_course_what_you_need'); ?>
</p>
<br>
<img alt="" src="<?php echo base_url('Public/images/frontend/book/english-certificates.png'); ?>" class="img-responsive">
</div>
<div class="col-md-4">
<h3>
<?php echo $this->lang->line('book_evaluation_cost'); ?>
<?php if ($option->discount_percent != 0) : ?>
<span class="brand-color">
<b>(-<?php echo $option->discount_percent; ?>%)</b>
</span>
<?php endif; ?>
</h3>
<table class="table">
<tbody>
<tr>
<td>
<i class="fa fa-check brand-color"></i>
<?php echo $option->duration; ?>
<?php echo $this->lang->line('book_evaluation_minutes'); ?>
</td>
</tr>
<tr>
<td>
<i class="fa fa-check brand-color"></i>
<?php echo $this->lang->line('book_qualified_teacher'); ?>
</td>
</tr>
<tr>
<td>
<i class="fa fa-check brand-color"></i>
<?php echo $this->lang->line('book_materials_provided'); ?>
</td>
</tr>
</tbody>
</table>
<br>
<h2 class="text-center text-success">
<?php echo $this->lang->line('book_evaluation_free'); ?>
</h2>
</div>
</div>
<!-- end course details -->
<!-- personal details -->
<div class="row padding-section-large" id="sectionOne">
<div class="col-md-4 col-md-offset-4">
<h3 id="stepOne">
<i class="fa fa-user"></i>
<?php echo $this->lang->line('book_step_one'); ?>
</h3>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<?php echo (form_error('first_name')) ? form_error('first_name') : '' ?>
<input class="form-control frontend-form" id="firstName" form="theForm" type="text" name="first_name" value="<?php echo set_value('first_name'); ?>" placeholder="<?php echo $this->lang->line('form_first_name'); ?>">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<?php echo (form_error('last_name')) ? form_error('last_name') : '' ?>
<input class="form-control frontend-form" id="lastName" form="theForm" type="text" name="last_name" value="<?php echo set_value('last_name'); ?>" placeholder="<?php echo $this->lang->line('form_last_name'); ?>">
</div>
</div>
</div>
<div class="form-group">
<?php echo (form_error('email')) ? form_error('email') : '' ?>
<input class="form-control frontend-form" id="email" form="theForm" type="email" name="email" value="<?php echo set_value('email'); ?>" placeholder="<?php echo $this->lang->line('form_email'); ?>">
</div>
<div class="form-group">
<?php echo (form_error('phone')) ? form_error('phone') : '' ?>
<input class="form-control frontend-form" id="phone" form="theForm" type="tel" name="phone" value="<?php echo set_value('phone'); ?>" placeholder="<?php echo $this->lang->line('form_phone'); ?>">
</div>
<div class="checkbox">
<label>
<?php echo (form_error('terms')) ? form_error('terms') : '' ?>
<input form="theForm" type="checkbox" name="terms" value="1">
<?php echo $this->lang->line('book_terms_one'); ?>
<a target="_blank" href="<?php echo base_url('terms'); ?>">
<?php echo $this->lang->line('book_terms_two'); ?>
</a>
</label>
</div>
</div>
</div>
<!-- end personal details -->
<!-- three calendars -->
<div class="row padding-section-small">
<div class="col-md-8 col-md-offset-2">
<?php echo (form_error('start_time')) ? form_error('start_date') : '' ?>
<h3 id="stepTwo">
<i class="fa fa-calendar"></i>
<?php echo $this->lang->line('book_step_two'); ?>
</h3>
<div class="row">
<div class="col-md-4">
<!-- calendar one -->
<?php echo $cal_one; ?>
</div>
<div class="col-md-4">
<!-- calendar two -->
<?php echo $cal_two; ?>
</div>
<div class="col-md-4">
<!-- calendar three -->
<?php echo $cal_three; ?>
</div>
</div>
</div>
</div>
<!-- end three calendars -->
<!-- select time -->
<div class="row padding-section-large">
<div class="col-md-4 col-md-offset-4">
<?php echo (form_error('start_date')) ? form_error('start_time') : '' ?>
<h3 id="stepThree">
<i class="fa fa-clock-o"></i>
<?php echo $this->lang->line('book_step_three'); ?>
</h3>
<?php echo (form_error('start_time')) ? form_error('start_time') : '' ?>
<div class="form-group">
<select class="form-control pick-time" id="getStartTime">
<option></option>
<?php foreach ($slots as $seconds => $time) : ?>
<option class="time-slot" value="<?php echo $time; ?>"><?php echo $time; ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>
<!-- end select time -->
<!-- start show selected options -->
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h1 class="text-center">
<span id="showStartTime">--/-- --</span>
</h1>
<h3 class="text-center">
<span id="showStartDate">----/--/--</span>
</h3>
</div>
</div>
<!-- end show selected options -->
<!-- submit button -->
<div class="row">
<div class="col-md-6 col-md-offset-3">
<!-- start hidden form -->
<form id="theForm" action="<?php echo base_url('book/free/' . $option->option_id); ?>" method="post">
<input id="timeZone" type="hidden" name="time_zone" value="<?php echo set_value('time_zone'); ?>">
<input id="startDate" type="hidden" name="start_date" value="<?php echo set_value('start_date'); ?>">
<input id="startTime" type="hidden" name="start_time" value="<?php echo set_value('start_time'); ?>">
<button id="submitButton" class="btn btn-block disabled" type="button">
<?php echo $this->lang->line('book_now'); ?>
</button>
</form>
<!-- end hidden form -->
</div>
</div>
<!-- end submit button -->
</div>
<!-- external scripts -->
<script src="<?php echo base_url('Public/js/custom/date-validation.js'); ?>"></script>
<script src="<?php echo base_url('Public/js/moment/moment.js'); ?>"></script>
<script src="<?php echo base_url('Public/js/jstz/jstz.min.js'); ?>"></script>
Sorry for so much code, not sure how else to ask for advice.
Many thanks in advance!