Complete website optimization: Color scheme standardization and hero page enhancement
- STANDARDIZED COLOR SCHEMES: Unified all service pages to use consistent purple theme (#6d28d9) - Removed theme-specific body classes from automation-flows.html and agentic-ai.html - Removed inline CSS color overrides from machine-learning.html, generative-ai.html, and data-analysis.html - Updated 15+ instances of hardcoded border colors to use rgba(var(--primary-color-rgb), 0.3) - Updated 5+ hover effects to use rgba(var(--primary-color-rgb), 0.4) for consistent glow colors - All service pages now maintain visual consistency with the main website - ENHANCED HERO PAGE: Improved data node scattering for better organic distribution - Repositioned core service nodes (ML, AI, Analytics, Automation) for natural scatter pattern - Enhanced industry nodes positioning with organic right-side distribution - Improved strategic nodes placement across right quadrant for better visual balance - Added varied animation delays and floating patterns for different node groups - Implemented micro-positioning adjustments for more natural appearance - Created size variations and rotation effects for enhanced organic feel - NEW FEATURES ADDED: - Enhanced CSS animations with varied floating patterns - Improved node interaction effects with staggered hover animations - Better responsive design for mobile devices - New documentation files for color standardization completion - TECHNICAL IMPROVEMENTS: - Updated hero-animation.css with enhanced organic scatter positioning - Maintained all existing functionality while improving visual aesthetics - Ensured cross-browser compatibility and mobile responsiveness
This commit is contained in:
311
js/form-handler.js
Normal file
311
js/form-handler.js
Normal file
@@ -0,0 +1,311 @@
|
||||
/**
|
||||
* Form validation and submission handler
|
||||
* Provides interactive form experiences with validation and feedback
|
||||
*/
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Initialize all forms on the page
|
||||
const forms = document.querySelectorAll('form');
|
||||
|
||||
forms.forEach(form => {
|
||||
initFormValidation(form);
|
||||
});
|
||||
|
||||
// Contact form special handling
|
||||
const contactForm = document.querySelector('.contact-form');
|
||||
if (contactForm) {
|
||||
initContactForm(contactForm);
|
||||
}
|
||||
|
||||
// Newsletter form special handling
|
||||
const newsletterForm = document.querySelector('.newsletter-form');
|
||||
if (newsletterForm) {
|
||||
initNewsletterForm(newsletterForm);
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize form validation
|
||||
function initFormValidation(form) {
|
||||
const inputs = form.querySelectorAll('input, textarea, select');
|
||||
|
||||
// Add validation classes and event listeners
|
||||
inputs.forEach(input => {
|
||||
// Skip submit buttons
|
||||
if (input.type === 'submit') return;
|
||||
|
||||
// Create validation message element
|
||||
const validationMessage = document.createElement('div');
|
||||
validationMessage.className = 'validation-message';
|
||||
input.parentNode.appendChild(validationMessage);
|
||||
|
||||
// Add input event listeners
|
||||
input.addEventListener('blur', function() {
|
||||
validateInput(input);
|
||||
});
|
||||
|
||||
input.addEventListener('input', function() {
|
||||
// Remove error styling on input
|
||||
input.classList.remove('input-error');
|
||||
validationMessage.textContent = '';
|
||||
});
|
||||
|
||||
// Add floating label effect for text inputs
|
||||
if (['text', 'email', 'tel', 'password', 'textarea'].includes(input.type)) {
|
||||
const label = document.querySelector(`label[for="${input.id}"]`);
|
||||
|
||||
if (label) {
|
||||
// Position the label over the input initially
|
||||
label.classList.add('floating-label');
|
||||
|
||||
// Move label up when input is focused or has content
|
||||
input.addEventListener('focus', function() {
|
||||
label.classList.add('label-active');
|
||||
});
|
||||
|
||||
input.addEventListener('blur', function() {
|
||||
if (!input.value) {
|
||||
label.classList.remove('label-active');
|
||||
}
|
||||
});
|
||||
|
||||
// Check initial state
|
||||
if (input.value) {
|
||||
label.classList.add('label-active');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Form submission
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
let isValid = true;
|
||||
|
||||
// Validate all inputs
|
||||
inputs.forEach(input => {
|
||||
if (!validateInput(input)) {
|
||||
isValid = false;
|
||||
}
|
||||
});
|
||||
|
||||
// If form is valid, proceed with submission
|
||||
if (isValid) {
|
||||
submitForm(form);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Validate individual input
|
||||
function validateInput(input) {
|
||||
const validationMessage = input.parentNode.querySelector('.validation-message');
|
||||
let isValid = true;
|
||||
let message = '';
|
||||
|
||||
// Required field validation
|
||||
if (input.required && !input.value.trim()) {
|
||||
isValid = false;
|
||||
message = 'This field is required';
|
||||
}
|
||||
|
||||
// Email validation
|
||||
if (input.type === 'email' && input.value.trim()) {
|
||||
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailPattern.test(input.value)) {
|
||||
isValid = false;
|
||||
message = 'Please enter a valid email address';
|
||||
}
|
||||
}
|
||||
|
||||
// Phone validation
|
||||
if (input.type === 'tel' && input.value.trim()) {
|
||||
const phonePattern = /^[\d\s\+\-\(\)]{7,20}$/;
|
||||
if (!phonePattern.test(input.value)) {
|
||||
isValid = false;
|
||||
message = 'Please enter a valid phone number';
|
||||
}
|
||||
}
|
||||
|
||||
// Custom validation based on data attributes
|
||||
if (input.dataset.minLength && input.value.length < parseInt(input.dataset.minLength)) {
|
||||
isValid = false;
|
||||
message = `Minimum length is ${input.dataset.minLength} characters`;
|
||||
}
|
||||
|
||||
// Update UI based on validation result
|
||||
if (!isValid) {
|
||||
input.classList.add('input-error');
|
||||
if (validationMessage) {
|
||||
validationMessage.textContent = message;
|
||||
validationMessage.classList.add('error-message');
|
||||
}
|
||||
} else {
|
||||
input.classList.remove('input-error');
|
||||
if (validationMessage) {
|
||||
validationMessage.textContent = '';
|
||||
validationMessage.classList.remove('error-message');
|
||||
}
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
// Submit form with animation
|
||||
function submitForm(form) {
|
||||
// Create loading overlay
|
||||
const loadingOverlay = document.createElement('div');
|
||||
loadingOverlay.className = 'form-loading-overlay';
|
||||
loadingOverlay.innerHTML = `
|
||||
<div class="loading-spinner"></div>
|
||||
<p>Submitting...</p>
|
||||
`;
|
||||
form.appendChild(loadingOverlay);
|
||||
|
||||
// Simulate form submission (replace with actual AJAX submission)
|
||||
setTimeout(() => {
|
||||
loadingOverlay.innerHTML = `
|
||||
<div class="success-icon">✓</div>
|
||||
<p>Thank you! We'll be in touch soon.</p>
|
||||
`;
|
||||
loadingOverlay.classList.add('submission-success');
|
||||
|
||||
// Reset form after success
|
||||
setTimeout(() => {
|
||||
form.reset();
|
||||
form.removeChild(loadingOverlay);
|
||||
|
||||
// Reset all floating labels
|
||||
const labels = form.querySelectorAll('.floating-label');
|
||||
labels.forEach(label => {
|
||||
label.classList.remove('label-active');
|
||||
});
|
||||
}, 3000);
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
// Special handling for contact form
|
||||
function initContactForm(form) {
|
||||
// Add animation for form sections
|
||||
const formSections = form.querySelectorAll('.form-section');
|
||||
if (formSections.length > 1) {
|
||||
// Create navigation buttons
|
||||
const navigation = document.createElement('div');
|
||||
navigation.className = 'form-navigation';
|
||||
|
||||
let currentSection = 0;
|
||||
|
||||
// Initially hide all sections except the first
|
||||
formSections.forEach((section, index) => {
|
||||
if (index > 0) {
|
||||
section.classList.add('hidden-section');
|
||||
}
|
||||
});
|
||||
|
||||
// Next button functionality
|
||||
const nextBtn = document.createElement('button');
|
||||
nextBtn.type = 'button';
|
||||
nextBtn.className = 'btn btn-secondary next-section';
|
||||
nextBtn.textContent = 'Next';
|
||||
nextBtn.addEventListener('click', () => {
|
||||
// Validate current section before proceeding
|
||||
const currentInputs = formSections[currentSection].querySelectorAll('input, textarea, select');
|
||||
let sectionIsValid = true;
|
||||
|
||||
currentInputs.forEach(input => {
|
||||
if (!validateInput(input)) {
|
||||
sectionIsValid = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (sectionIsValid && currentSection < formSections.length - 1) {
|
||||
formSections[currentSection].classList.add('hidden-section');
|
||||
formSections[currentSection].classList.add('section-left');
|
||||
currentSection++;
|
||||
formSections[currentSection].classList.remove('hidden-section');
|
||||
|
||||
updateNavigationState();
|
||||
}
|
||||
});
|
||||
|
||||
// Previous button functionality
|
||||
const prevBtn = document.createElement('button');
|
||||
prevBtn.type = 'button';
|
||||
prevBtn.className = 'btn btn-outline prev-section';
|
||||
prevBtn.textContent = 'Previous';
|
||||
prevBtn.style.display = 'none';
|
||||
prevBtn.addEventListener('click', () => {
|
||||
if (currentSection > 0) {
|
||||
formSections[currentSection].classList.add('hidden-section');
|
||||
currentSection--;
|
||||
formSections[currentSection].classList.remove('hidden-section');
|
||||
formSections[currentSection].classList.remove('section-left');
|
||||
|
||||
updateNavigationState();
|
||||
}
|
||||
});
|
||||
|
||||
// Add navigation to form
|
||||
navigation.appendChild(prevBtn);
|
||||
navigation.appendChild(nextBtn);
|
||||
form.appendChild(navigation);
|
||||
|
||||
// Update navigation buttons state
|
||||
function updateNavigationState() {
|
||||
prevBtn.style.display = currentSection > 0 ? 'inline-block' : 'none';
|
||||
nextBtn.textContent = currentSection === formSections.length - 1 ? 'Submit' : 'Next';
|
||||
|
||||
// Replace next button with submit on last section
|
||||
if (currentSection === formSections.length - 1) {
|
||||
nextBtn.classList.add('submit-form');
|
||||
nextBtn.addEventListener('click', () => {
|
||||
form.dispatchEvent(new Event('submit'));
|
||||
});
|
||||
} else {
|
||||
nextBtn.classList.remove('submit-form');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Newsletter subscription form handling
|
||||
function initNewsletterForm(form) {
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const emailInput = form.querySelector('input[type="email"]');
|
||||
if (!emailInput) return;
|
||||
|
||||
// Validate email
|
||||
if (!validateInput(emailInput)) return;
|
||||
|
||||
// Create animated check mark for subscription
|
||||
const subscribeBtn = form.querySelector('button[type="submit"]');
|
||||
if (subscribeBtn) {
|
||||
subscribeBtn.innerHTML = '<div class="subscribe-loader"></div>';
|
||||
subscribeBtn.disabled = true;
|
||||
|
||||
setTimeout(() => {
|
||||
subscribeBtn.innerHTML = '<span class="subscribe-check">✓</span>';
|
||||
subscribeBtn.classList.add('subscribed');
|
||||
|
||||
// Display thank you message
|
||||
const thankYou = document.createElement('div');
|
||||
thankYou.className = 'subscription-thanks';
|
||||
thankYou.textContent = 'Thank you for subscribing!';
|
||||
form.appendChild(thankYou);
|
||||
|
||||
// Reset form after delay
|
||||
setTimeout(() => {
|
||||
form.reset();
|
||||
subscribeBtn.innerHTML = 'Subscribe';
|
||||
subscribeBtn.disabled = false;
|
||||
subscribeBtn.classList.remove('subscribed');
|
||||
|
||||
if (form.contains(thankYou)) {
|
||||
form.removeChild(thankYou);
|
||||
}
|
||||
}, 3000);
|
||||
}, 1500);
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user