| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 |
- // ==========================================================================
- // CONTACT FORM DEMO VERSION - contact-form-demo.js
- // Perfect for portfolios, GitHub Pages, and client demonstrations
- // Shows all functionality without needing a server
- // ==========================================================================
- document.addEventListener('DOMContentLoaded', function() {
- // Get main form elements
- const contactForm = document.getElementById('contactForm');
- const submitBtn = contactForm.querySelector('.form-submit-btn');
- const formSuccess = document.getElementById('form-success');
- const formError = document.getElementById('form-error');
- /**
- * Display field-specific error messages
- */
- function showFieldError(fieldName, message) {
- const errorElement = document.getElementById(`${fieldName}-error`);
- if (errorElement) {
- errorElement.textContent = message;
- errorElement.classList.add('show');
- }
- }
- /**
- * Clear all field error messages
- */
- function clearFieldErrors() {
- const errorElements = contactForm.querySelectorAll('.form-error');
- errorElements.forEach(element => {
- element.textContent = '';
- element.classList.remove('show');
- });
- }
- /**
- * Show success message with personalization
- */
- function showSuccessMessage(message) {
- formError.style.display = 'none';
- formSuccess.style.display = 'flex';
-
- if (message) {
- formSuccess.querySelector('span').textContent = message;
- }
- // Smooth scroll to success message
- setTimeout(() => {
- formSuccess.scrollIntoView({ behavior: 'smooth', block: 'center' });
- }, 100);
- }
- /**
- * Setup real-time validation for form fields
- */
- function setupRealTimeValidation() {
- const fields = {
- name: {
- element: contactForm.querySelector('#name'),
- validate: (value) => {
- if (!value.trim()) return 'Name is required';
- if (value.trim().length < 2) return 'Name must be at least 2 characters';
- return null;
- }
- },
- email: {
- element: contactForm.querySelector('#email'),
- validate: (value) => {
- if (!value.trim()) return 'Email is required';
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
- if (!emailRegex.test(value)) return 'Please enter a valid email address';
- return null;
- }
- },
- phone: {
- element: contactForm.querySelector('#phone'),
- validate: (value) => {
- if (value.trim() && !/^[\+]?[1-9][\d\s\-\(\)]{0,15}$/.test(value)) {
- return 'Please enter a valid phone number';
- }
- return null;
- }
- },
- message: {
- element: contactForm.querySelector('#message'),
- validate: (value) => {
- if (!value.trim()) return 'Message is required';
- if (value.trim().length < 10) return 'Message must be at least 10 characters';
- if (value.trim().length > 2000) return 'Message must be less than 2000 characters';
- return null;
- }
- }
- };
- // Attach event listeners to each field
- Object.keys(fields).forEach(fieldName => {
- const field = fields[fieldName];
- const errorElement = document.getElementById(`${fieldName}-error`);
- // Validate on blur
- field.element.addEventListener('blur', function() {
- const error = field.validate(this.value);
- if (error) {
- showFieldError(fieldName, error);
- } else if (errorElement) {
- errorElement.textContent = '';
- errorElement.classList.remove('show');
- }
- });
- // Clear error on input if field becomes valid
- field.element.addEventListener('input', function() {
- if (errorElement && errorElement.classList.contains('show')) {
- const error = field.validate(this.value);
- if (!error) {
- errorElement.textContent = '';
- errorElement.classList.remove('show');
- }
- }
- });
- });
- // Privacy policy checkbox validation
- const privacyCheckbox = contactForm.querySelector('input[name="privacy"]');
- privacyCheckbox.addEventListener('change', function() {
- const errorElement = document.getElementById('privacy-error');
- if (this.checked && errorElement) {
- errorElement.textContent = '';
- errorElement.classList.remove('show');
- }
- });
- }
- /**
- * Handle form submission - DEMO SIMULATION
- */
- contactForm.addEventListener('submit', function(e) {
- e.preventDefault();
- // Clear previous messages
- clearFieldErrors();
- formSuccess.style.display = 'none';
- formError.style.display = 'none';
- // Get form data
- const formData = new FormData(contactForm);
-
- // Client-side validation
- let isValid = true;
- if (!formData.get('name').trim()) {
- showFieldError('name', 'Name is required');
- isValid = false;
- }
- if (!formData.get('email').trim()) {
- showFieldError('email', 'Email is required');
- isValid = false;
- } else {
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
- if (!emailRegex.test(formData.get('email'))) {
- showFieldError('email', 'Please enter a valid email address');
- isValid = false;
- }
- }
- if (!formData.get('message').trim()) {
- showFieldError('message', 'Message is required');
- isValid = false;
- } else if (formData.get('message').trim().length < 10) {
- showFieldError('message', 'Message must be at least 10 characters');
- isValid = false;
- }
- if (!formData.get('privacy')) {
- showFieldError('privacy', 'You must accept the privacy policy');
- isValid = false;
- }
- // If validation fails, scroll to first error
- if (!isValid) {
- const firstError = contactForm.querySelector('.form-error.show');
- if (firstError) {
- firstError.scrollIntoView({ behavior: 'smooth', block: 'center' });
- }
- return;
- }
- // Simulate successful submission
- const userName = formData.get('name').trim();
- const userService = formData.get('service') || 'our services';
-
- // Personalized success message
- const messages = [
- `Thank you ${userName}! Your message has been received. We'll get back to you within 24 hours.`,
- `Hi ${userName}! Thanks for your interest in ${userService}. We'll contact you soon!`,
- `Great to hear from you, ${userName}! Your inquiry is important to us and we'll respond shortly.`
- ];
-
- const randomMessage = messages[Math.floor(Math.random() * messages.length)];
- showSuccessMessage(randomMessage);
-
- // Reset form after delay
- setTimeout(() => {
- contactForm.reset();
- updateCharCounter();
- }, 1500);
- });
- // Initialize validation
- setupRealTimeValidation();
- // Character counter for message textarea
- const messageTextarea = contactForm.querySelector('#message');
- const messageGroup = messageTextarea.closest('.form-group');
-
- const charCounter = document.createElement('div');
- charCounter.className = 'char-counter';
- charCounter.style.cssText = 'font-size: 12px; color: #6b7280; margin-top: 5px; text-align: right;';
- messageGroup.appendChild(charCounter);
- function updateCharCounter() {
- const length = messageTextarea.value.length;
- const maxLength = 2000;
- charCounter.textContent = `${length}/${maxLength} characters`;
-
- if (length > maxLength * 0.9) {
- charCounter.style.color = '#f59e0b';
- } else if (length > maxLength) {
- charCounter.style.color = '#dc3545';
- } else {
- charCounter.style.color = '#6b7280';
- }
- }
- messageTextarea.addEventListener('input', updateCharCounter);
- updateCharCounter();
- // Auto-resize textarea
- messageTextarea.addEventListener('input', function() {
- this.style.height = 'auto';
- this.style.height = Math.min(this.scrollHeight, 200) + 'px';
- });
- // Enhanced UX features
- const formInputs = contactForm.querySelectorAll('input, select, textarea');
-
- formInputs.forEach((input, index) => {
- // Focus styling
- input.addEventListener('focus', function() {
- this.closest('.form-group').classList.add('focused');
- });
- input.addEventListener('blur', function() {
- this.closest('.form-group').classList.remove('focused');
- });
- // Keyboard navigation
- if (input.type !== 'textarea') {
- input.addEventListener('keydown', function(e) {
- if (e.key === 'Enter') {
- e.preventDefault();
- const nextInput = formInputs[index + 1];
- if (nextInput) {
- nextInput.focus();
- } else {
- submitBtn.click();
- }
- }
- });
- }
- });
- // Auto-focus budget when service is selected
- const serviceSelect = contactForm.querySelector('#service');
- const budgetSelect = contactForm.querySelector('#budget');
-
- serviceSelect.addEventListener('change', function() {
- if (this.value && !budgetSelect.value) {
- setTimeout(() => budgetSelect.focus(), 100);
- }
- });
- // Analytics tracking (demo mode)
- let formStartTime = null;
- let fieldsInteracted = new Set();
- contactForm.addEventListener('focusin', function() {
- if (!formStartTime) {
- formStartTime = Date.now();
- }
- }, { once: true });
- formInputs.forEach(input => {
- input.addEventListener('input', function() {
- fieldsInteracted.add(this.name || this.id);
- });
- });
- contactForm.addEventListener('submit', function() {
- if (formStartTime) {
- const completionTime = Date.now() - formStartTime;
- }
- });
- });
|