Files
ukaiautomation/assets/js/main.js

549 lines
19 KiB
JavaScript
Raw Normal View History

// Enhanced JavaScript for UK Data Services Website
document.addEventListener('DOMContentLoaded', function() {
// Rotating Hero Text Effect (like original UK Data Services site)
const rotatingText = document.getElementById('rotating-text');
const heroSubtitle = document.getElementById('hero-subtitle');
if (rotatingText && heroSubtitle) {
const slides = [
{
title: "Voted UK's No.1 Web Scraping Service",
subtitle: "We are experts in web scraping, data analysis and competitor price monitoring."
},
{
title: "UK-based Team",
subtitle: "Our team is based in the UK for a clearer, faster response."
},
{
title: "Professional Price Monitoring",
subtitle: "Let us monitor your competitor's pricing and product ranges."
},
{
title: "Bespoke Software Solutions",
subtitle: "Let our experts build your ideal scraping solution."
}
];
let currentSlide = 0;
function rotateSlide() {
// Fade out
rotatingText.style.opacity = '0';
heroSubtitle.style.opacity = '0';
setTimeout(() => {
// Change text
rotatingText.textContent = slides[currentSlide].title;
heroSubtitle.textContent = slides[currentSlide].subtitle;
// Fade in
rotatingText.style.opacity = '1';
heroSubtitle.style.opacity = '1';
currentSlide = (currentSlide + 1) % slides.length;
}, 500);
}
// Add transition styles immediately
rotatingText.style.transition = 'opacity 0.5s ease-in-out';
heroSubtitle.style.transition = 'opacity 0.5s ease-in-out';
// Start rotation after a short delay
setTimeout(() => {
rotateSlide();
setInterval(rotateSlide, 4000); // Change every 4 seconds
}, 2000); // Start after 2 seconds
console.log('Hero text rotation initialized');
} else {
console.log('Rotating text elements not found');
}
// Mobile Navigation Toggle
const navToggle = document.getElementById('nav-toggle');
const navMenu = document.getElementById('nav-menu');
if (navToggle && navMenu) {
navToggle.addEventListener('click', function() {
navMenu.classList.toggle('active');
navToggle.classList.toggle('active');
});
// Close mobile menu when clicking on a link
const navLinks = document.querySelectorAll('.nav-link');
navLinks.forEach(link => {
link.addEventListener('click', () => {
navMenu.classList.remove('active');
navToggle.classList.remove('active');
});
});
}
// Navbar Scroll Effect
const navbar = document.getElementById('navbar');
function handleNavbarScroll() {
if (window.scrollY > 50) {
navbar.classList.add('scrolled');
} else {
navbar.classList.remove('scrolled');
}
}
window.addEventListener('scroll', handleNavbarScroll);
// Smooth Scrolling for Navigation Links
const smoothScrollLinks = document.querySelectorAll('a[href^="#"]');
smoothScrollLinks.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href');
const targetSection = document.querySelector(targetId);
if (targetSection) {
const headerOffset = 80;
const elementPosition = targetSection.getBoundingClientRect().top;
const offsetPosition = elementPosition + window.pageYOffset - headerOffset;
window.scrollTo({
top: offsetPosition,
behavior: 'smooth'
});
}
});
});
// Enhanced Scroll Animations
const animatedElements = document.querySelectorAll('.animate-on-scroll, .service-card, .feature, .step');
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('animated');
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
observer.unobserve(entry.target);
}
});
}, observerOptions);
animatedElements.forEach((element, index) => {
// Set initial state
element.style.opacity = '0';
element.style.transform = 'translateY(30px)';
element.style.transition = `opacity 0.8s ease-out ${index * 0.1}s, transform 0.8s ease-out ${index * 0.1}s`;
observer.observe(element);
});
// Add hover animations to service cards
const serviceCards = document.querySelectorAll('.service-card');
serviceCards.forEach(card => {
card.addEventListener('mouseenter', function() {
this.style.transform = 'translateY(-10px) scale(1.02)';
this.style.boxShadow = '0 20px 40px rgba(0, 0, 0, 0.15)';
});
card.addEventListener('mouseleave', function() {
this.style.transform = 'translateY(0) scale(1)';
this.style.boxShadow = '0 4px 20px rgba(0, 0, 0, 0.08)';
});
});
// Add pulse animation to CTA buttons
const ctaButtons = document.querySelectorAll('.btn-primary');
ctaButtons.forEach(btn => {
btn.addEventListener('mouseenter', function() {
this.style.animation = 'pulse 0.5s ease-in-out';
});
btn.addEventListener('mouseleave', function() {
this.style.animation = 'none';
});
});
console.log('Enhanced animations initialized');
// Form Validation and Enhancement
const contactForm = document.querySelector('.contact-form form');
if (contactForm) {
contactForm.addEventListener('submit', function(e) {
e.preventDefault();
// Basic form validation
const formData = new FormData(this);
const name = formData.get('name');
const email = formData.get('email');
const message = formData.get('message');
// Validation
let isValid = true;
const errors = [];
if (!name || name.trim().length < 2) {
errors.push('Please enter a valid name');
isValid = false;
}
if (!email || !isValidEmail(email)) {
errors.push('Please enter a valid email address');
isValid = false;
}
if (!message || message.trim().length < 10) {
errors.push('Please provide more details about your project (minimum 10 characters)');
isValid = false;
}
if (isValid) {
// Show loading state
const submitButton = this.querySelector('button[type="submit"]');
const originalText = submitButton.textContent;
submitButton.textContent = 'Sending...';
submitButton.disabled = true;
// Submit form (you'll need to implement the backend handler)
fetch('contact-handler.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('Message sent successfully! We\'ll get back to you soon.', 'success');
this.reset();
} else {
showNotification('There was an error sending your message. Please try again.', 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('There was an error sending your message. Please try again.', 'error');
})
.finally(() => {
submitButton.textContent = originalText;
submitButton.disabled = false;
});
} else {
showNotification(errors.join('<br>'), 'error');
}
});
}
// Email validation function
function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
// Notification system
function showNotification(message, type = 'info') {
// Remove existing notifications
const existingNotification = document.querySelector('.notification');
if (existingNotification) {
existingNotification.remove();
}
// Create notification element
const notification = document.createElement('div');
notification.className = `notification notification-${type}`;
notification.innerHTML = `
<div class="notification-content">
<span class="notification-message">${message}</span>
<button class="notification-close">&times;</button>
</div>
`;
// Add styles
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
z-index: 10000;
background: ${type === 'success' ? '#10b981' : type === 'error' ? '#ef4444' : '#3b82f6'};
color: white;
padding: 16px 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
max-width: 400px;
font-family: 'Inter', sans-serif;
font-size: 14px;
opacity: 0;
transform: translateX(100%);
transition: all 0.3s ease;
`;
notification.querySelector('.notification-content').style.cssText = `
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
`;
notification.querySelector('.notification-close').style.cssText = `
background: none;
border: none;
color: white;
font-size: 18px;
cursor: pointer;
padding: 0;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
`;
// Add to page
document.body.appendChild(notification);
// Animate in
setTimeout(() => {
notification.style.opacity = '1';
notification.style.transform = 'translateX(0)';
}, 100);
// Handle close button
notification.querySelector('.notification-close').addEventListener('click', () => {
hideNotification(notification);
});
// Auto hide after 5 seconds
setTimeout(() => {
if (document.body.contains(notification)) {
hideNotification(notification);
}
}, 5000);
}
function hideNotification(notification) {
notification.style.opacity = '0';
notification.style.transform = 'translateX(100%)';
setTimeout(() => {
if (document.body.contains(notification)) {
notification.remove();
}
}, 300);
}
// Stats Counter Animation
const stats = document.querySelectorAll('.stat-number');
function animateStats() {
stats.forEach(stat => {
const originalText = stat.textContent.trim();
console.log('Animating stat:', originalText);
// Handle different stat types
if (originalText.includes('£2.5M+')) {
return; // Keep as is, don't animate currency
} else if (originalText.includes('99.8%')) {
animateNumber(stat, 0, 99.8, '%');
} else if (originalText.includes('ISO 27001')) {
return; // Keep as is, don't animate certification
}
});
}
function animateNumber(element, start, end, suffix = '') {
let current = start;
const increment = (end - start) / 60; // 60 steps for smoother animation
const timer = setInterval(() => {
current += increment;
if (current >= end) {
current = end;
clearInterval(timer);
}
element.textContent = current.toFixed(1) + suffix;
}, 50); // Every 50ms
}
// Trigger stats animation when section is visible
const statsSection = document.querySelector('.hero-stats');
if (statsSection) {
const statsObserver = new IntersectionObserver(function(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('Stats section is visible, starting animation');
setTimeout(() => {
animateStats();
}, 500); // Small delay
statsObserver.unobserve(entry.target);
}
});
}, { threshold: 0.3 });
statsObserver.observe(statsSection);
} else {
console.log('Stats section not found');
}
// Lazy Loading for Images
const images = document.querySelectorAll('img[loading="lazy"]');
if ('IntersectionObserver' in window) {
const imageObserver = new IntersectionObserver(function(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src || img.src;
img.classList.add('loaded');
imageObserver.unobserve(img);
}
});
});
images.forEach(img => imageObserver.observe(img));
}
// Scroll to Top Button
const scrollTopBtn = document.createElement('button');
scrollTopBtn.innerHTML = '↑';
scrollTopBtn.className = 'scroll-top-btn';
scrollTopBtn.style.cssText = `
position: fixed;
bottom: 30px;
right: 30px;
width: 50px;
height: 50px;
border: none;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-size: 20px;
cursor: pointer;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
z-index: 1000;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
`;
document.body.appendChild(scrollTopBtn);
scrollTopBtn.addEventListener('click', () => {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
// Show/hide scroll to top button
function handleScrollTopButton() {
if (window.scrollY > 500) {
scrollTopBtn.style.opacity = '1';
scrollTopBtn.style.visibility = 'visible';
} else {
scrollTopBtn.style.opacity = '0';
scrollTopBtn.style.visibility = 'hidden';
}
}
window.addEventListener('scroll', handleScrollTopButton);
// Performance: Throttle scroll events
let scrollTimeout;
const originalHandlers = [handleNavbarScroll, handleScrollTopButton];
function throttledScrollHandler() {
if (!scrollTimeout) {
scrollTimeout = setTimeout(() => {
originalHandlers.forEach(handler => handler());
scrollTimeout = null;
}, 16); // ~60fps
}
}
window.removeEventListener('scroll', handleNavbarScroll);
window.removeEventListener('scroll', handleScrollTopButton);
window.addEventListener('scroll', throttledScrollHandler);
// Preload critical resources
function preloadResource(href, as = 'image') {
const link = document.createElement('link');
link.rel = 'preload';
link.href = href;
link.as = as;
document.head.appendChild(link);
}
// Preload hero image and other critical assets
// preloadResource('assets/images/hero-data-analytics.svg');
// preloadResource('assets/images/logo.svg');
// Initialize tooltips (if needed)
const tooltipElements = document.querySelectorAll('[data-tooltip]');
tooltipElements.forEach(element => {
element.addEventListener('mouseenter', function() {
const tooltipText = this.getAttribute('data-tooltip');
const tooltip = document.createElement('div');
tooltip.className = 'tooltip';
tooltip.textContent = tooltipText;
tooltip.style.cssText = `
position: absolute;
background: #1a1a1a;
color: white;
padding: 8px 12px;
border-radius: 6px;
font-size: 14px;
white-space: nowrap;
z-index: 10000;
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
`;
document.body.appendChild(tooltip);
const rect = this.getBoundingClientRect();
tooltip.style.left = rect.left + (rect.width / 2) - (tooltip.offsetWidth / 2) + 'px';
tooltip.style.top = rect.top - tooltip.offsetHeight - 10 + 'px';
setTimeout(() => {
tooltip.style.opacity = '1';
}, 100);
this.addEventListener('mouseleave', function() {
tooltip.style.opacity = '0';
setTimeout(() => {
if (document.body.contains(tooltip)) {
tooltip.remove();
}
}, 300);
}, { once: true });
});
});
// Security: Prevent XSS in dynamic content
function sanitizeHTML(str) {
const temp = document.createElement('div');
temp.textContent = str;
return temp.innerHTML;
}
// Service Worker Registration (for PWA capabilities)
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('SW registered: ', registration);
})
.catch(registrationError => {
console.log('SW registration failed: ', registrationError);
});
});
}
console.log('UK Data Services website initialized successfully');
});