291 lines
8.6 KiB
JavaScript
291 lines
8.6 KiB
JavaScript
// Kobelly Base Website - Main JavaScript
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
'use strict';
|
|
|
|
// Initialize all components
|
|
initNavbar();
|
|
initAnimations();
|
|
initFormValidation();
|
|
initScrollEffects();
|
|
initContactForm();
|
|
initAccordion();
|
|
initTooltips();
|
|
initModals();
|
|
|
|
console.log('Kobelly Base Website loaded successfully!');
|
|
});
|
|
|
|
// Navbar functionality
|
|
function initNavbar() {
|
|
const navbar = document.querySelector('.navbar');
|
|
const navbarToggler = document.querySelector('.navbar-toggler');
|
|
const navbarCollapse = document.querySelector('.navbar-collapse');
|
|
|
|
// Navbar scroll effect
|
|
window.addEventListener('scroll', function() {
|
|
if (window.scrollY > 50) {
|
|
navbar.classList.add('scrolled');
|
|
} else {
|
|
navbar.classList.remove('scrolled');
|
|
}
|
|
});
|
|
|
|
// Close mobile menu when clicking on a link
|
|
const navLinks = document.querySelectorAll('.navbar-nav .nav-link');
|
|
navLinks.forEach(link => {
|
|
link.addEventListener('click', function() {
|
|
if (navbarCollapse.classList.contains('show')) {
|
|
navbarToggler.click();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// Animation initialization
|
|
function initAnimations() {
|
|
// Intersection Observer for fade-in animations
|
|
const observerOptions = {
|
|
threshold: 0.1,
|
|
rootMargin: '0px 0px -50px 0px'
|
|
};
|
|
|
|
const observer = new IntersectionObserver(function(entries) {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('fade-in');
|
|
observer.unobserve(entry.target);
|
|
}
|
|
});
|
|
}, observerOptions);
|
|
|
|
// Observe elements for animation
|
|
const animateElements = document.querySelectorAll('.card, .feature-icon, .service-icon, .value-icon');
|
|
animateElements.forEach(el => {
|
|
observer.observe(el);
|
|
});
|
|
}
|
|
|
|
// Form validation
|
|
function initFormValidation() {
|
|
const forms = document.querySelectorAll('form[data-validate]');
|
|
|
|
forms.forEach(form => {
|
|
form.addEventListener('submit', function(e) {
|
|
if (!validateForm(form)) {
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function validateForm(form) {
|
|
let isValid = true;
|
|
const inputs = form.querySelectorAll('input[required], textarea[required]');
|
|
|
|
inputs.forEach(input => {
|
|
if (!input.value.trim()) {
|
|
showFieldError(input, 'This field is required');
|
|
isValid = false;
|
|
} else if (input.type === 'email' && !isValidEmail(input.value)) {
|
|
showFieldError(input, 'Please enter a valid email address');
|
|
isValid = false;
|
|
} else {
|
|
clearFieldError(input);
|
|
}
|
|
});
|
|
|
|
return isValid;
|
|
}
|
|
|
|
function isValidEmail(email) {
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
return emailRegex.test(email);
|
|
}
|
|
|
|
function showFieldError(input, message) {
|
|
clearFieldError(input);
|
|
|
|
const errorDiv = document.createElement('div');
|
|
errorDiv.className = 'invalid-feedback d-block';
|
|
errorDiv.textContent = message;
|
|
|
|
input.classList.add('is-invalid');
|
|
input.parentNode.appendChild(errorDiv);
|
|
}
|
|
|
|
function clearFieldError(input) {
|
|
input.classList.remove('is-invalid');
|
|
const errorDiv = input.parentNode.querySelector('.invalid-feedback');
|
|
if (errorDiv) {
|
|
errorDiv.remove();
|
|
}
|
|
}
|
|
|
|
// Scroll effects
|
|
function initScrollEffects() {
|
|
// Smooth scrolling for anchor links
|
|
const anchorLinks = document.querySelectorAll('a[href^="#"]');
|
|
anchorLinks.forEach(link => {
|
|
link.addEventListener('click', function(e) {
|
|
e.preventDefault();
|
|
const target = document.querySelector(this.getAttribute('href'));
|
|
if (target) {
|
|
target.scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'start'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
// Back to top button
|
|
const backToTopBtn = document.createElement('button');
|
|
backToTopBtn.innerHTML = '<i class="bi bi-arrow-up"></i>';
|
|
backToTopBtn.className = 'btn btn-primary position-fixed';
|
|
backToTopBtn.style.cssText = 'bottom: 20px; right: 20px; z-index: 1000; display: none; border-radius: 50%; width: 50px; height: 50px;';
|
|
backToTopBtn.setAttribute('aria-label', 'Back to top');
|
|
|
|
document.body.appendChild(backToTopBtn);
|
|
|
|
window.addEventListener('scroll', function() {
|
|
if (window.scrollY > 300) {
|
|
backToTopBtn.style.display = 'block';
|
|
} else {
|
|
backToTopBtn.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
backToTopBtn.addEventListener('click', function() {
|
|
window.scrollTo({
|
|
top: 0,
|
|
behavior: 'smooth'
|
|
});
|
|
});
|
|
}
|
|
|
|
// Contact form handling
|
|
function initContactForm() {
|
|
const contactForm = document.querySelector('form[action*="contact"]');
|
|
if (!contactForm) return;
|
|
|
|
contactForm.addEventListener('submit', function(e) {
|
|
const submitBtn = this.querySelector('button[type="submit"]');
|
|
const originalText = submitBtn.textContent;
|
|
|
|
// Show loading state
|
|
submitBtn.disabled = true;
|
|
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Sending...';
|
|
submitBtn.classList.add('loading');
|
|
});
|
|
}
|
|
|
|
// Accordion functionality
|
|
function initAccordion() {
|
|
const accordionButtons = document.querySelectorAll('.accordion-button');
|
|
|
|
accordionButtons.forEach(button => {
|
|
button.addEventListener('click', function() {
|
|
const isExpanded = this.getAttribute('aria-expanded') === 'true';
|
|
|
|
// Add animation class
|
|
const accordionCollapse = this.nextElementSibling;
|
|
if (!isExpanded) {
|
|
accordionCollapse.classList.add('expanding');
|
|
}
|
|
|
|
// Remove animation class after transition
|
|
setTimeout(() => {
|
|
accordionCollapse.classList.remove('expanding');
|
|
}, 300);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Tooltip initialization
|
|
function initTooltips() {
|
|
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
|
tooltipTriggerList.map(function(tooltipTriggerEl) {
|
|
return new bootstrap.Tooltip(tooltipTriggerEl);
|
|
});
|
|
}
|
|
|
|
// Modal initialization
|
|
function initModals() {
|
|
const modalTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="modal"]'));
|
|
modalTriggerList.map(function(modalTriggerEl) {
|
|
return new bootstrap.Modal(modalTriggerEl);
|
|
});
|
|
}
|
|
|
|
// Utility functions
|
|
function debounce(func, wait) {
|
|
let timeout;
|
|
return function executedFunction(...args) {
|
|
const later = () => {
|
|
clearTimeout(timeout);
|
|
func(...args);
|
|
};
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(later, wait);
|
|
};
|
|
}
|
|
|
|
function throttle(func, limit) {
|
|
let inThrottle;
|
|
return function() {
|
|
const args = arguments;
|
|
const context = this;
|
|
if (!inThrottle) {
|
|
func.apply(context, args);
|
|
inThrottle = true;
|
|
setTimeout(() => inThrottle = false, limit);
|
|
}
|
|
};
|
|
}
|
|
|
|
// Performance optimization
|
|
const optimizedScrollHandler = throttle(function() {
|
|
// Handle scroll events efficiently
|
|
}, 16); // ~60fps
|
|
|
|
window.addEventListener('scroll', optimizedScrollHandler);
|
|
|
|
// Error handling
|
|
window.addEventListener('error', function(e) {
|
|
console.error('JavaScript error:', e.error);
|
|
});
|
|
|
|
// Service Worker registration (for PWA features)
|
|
if ('serviceWorker' in navigator) {
|
|
window.addEventListener('load', function() {
|
|
navigator.serviceWorker.register('/sw.js')
|
|
.then(function(registration) {
|
|
console.log('ServiceWorker registration successful');
|
|
})
|
|
.catch(function(err) {
|
|
console.log('ServiceWorker registration failed');
|
|
});
|
|
});
|
|
}
|
|
|
|
// Accessibility improvements
|
|
function initAccessibility() {
|
|
// Skip to main content link
|
|
const skipLink = document.createElement('a');
|
|
skipLink.href = '#main-content';
|
|
skipLink.textContent = 'Skip to main content';
|
|
skipLink.className = 'sr-only sr-only-focusable position-absolute';
|
|
skipLink.style.cssText = 'top: 10px; left: 10px; z-index: 1001; padding: 10px; background: white; border: 1px solid #ccc;';
|
|
|
|
document.body.insertBefore(skipLink, document.body.firstChild);
|
|
|
|
// Add main content id
|
|
const mainContent = document.querySelector('main');
|
|
if (mainContent) {
|
|
mainContent.id = 'main-content';
|
|
}
|
|
}
|
|
|
|
// Initialize accessibility features
|
|
initAccessibility();
|