code optimized
This commit is contained in:
parent
4df044c9c5
commit
1b0cdedc14
@ -4,6 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>Global Tea Emporium - Premium Authentic Teas Worldwide</title>
|
<title>Global Tea Emporium - Premium Authentic Teas Worldwide</title>
|
||||||
<link rel="stylesheet" href="styles.css" />
|
<link rel="stylesheet" href="styles.css" />
|
||||||
|
<script src="script.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Skip to main content link for screen readers -->
|
<!-- Skip to main content link for screen readers -->
|
||||||
|
|||||||
448
script.js
448
script.js
@ -1,295 +1,185 @@
|
|||||||
// Global Tea Emporium - JavaScript Functionality
|
// Global Tea Emporium - Optimized JavaScript
|
||||||
|
|
||||||
// Image Gallery Functionality
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
// ===== IMAGE GALLERY =====
|
||||||
console.log('Gallery script loading...');
|
const galleryImages = [
|
||||||
|
{
|
||||||
|
src: "images/green-tea-leaves.webp",
|
||||||
|
alt: "Premium Green Tea Leaves",
|
||||||
|
caption: "Premium Green Tea Leaves - Hand-picked from our finest gardens",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "images/Indian Black Teas.webp",
|
||||||
|
alt: "Indian Black Tea Selection",
|
||||||
|
caption:
|
||||||
|
"Authentic Indian Black Teas - Rich and robust flavors from Assam and Darjeeling",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "images/Oolong Selections.jpg",
|
||||||
|
alt: "Oolong Tea Collection",
|
||||||
|
caption:
|
||||||
|
"Traditional Oolong Selections - Complex flavors from Taiwan and China",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const galleryImages = [
|
const galleryImage = document.getElementById("gallery-image");
|
||||||
{
|
const galleryCaption = document.getElementById("gallery-caption");
|
||||||
src: 'images/green-tea-leaves.webp',
|
const thumbnails = document.querySelectorAll(".thumbnail");
|
||||||
alt: 'Premium Green Tea Leaves',
|
let currentIndex = 0;
|
||||||
caption: 'Premium Green Tea Leaves - Hand-picked from our finest gardens'
|
|
||||||
},
|
if (!galleryImage || !galleryCaption) return;
|
||||||
{
|
|
||||||
src: 'images/Indian Black Teas.webp',
|
// Update gallery display
|
||||||
alt: 'Indian Black Tea Selection',
|
function updateGallery(index) {
|
||||||
caption: 'Authentic Indian Black Teas - Rich and robust flavors from Assam and Darjeeling'
|
currentIndex = index;
|
||||||
},
|
const image = galleryImages[index];
|
||||||
{
|
galleryImage.src = image.src;
|
||||||
src: 'images/Oolong Selections.jpg',
|
galleryImage.alt = image.alt;
|
||||||
alt: 'Oolong Tea Collection',
|
galleryCaption.textContent = image.caption;
|
||||||
caption: 'Traditional Oolong Selections - Complex flavors from Taiwan and China'
|
|
||||||
|
thumbnails.forEach((thumb, i) => {
|
||||||
|
thumb.classList.toggle("active", i === index);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigation functions
|
||||||
|
const nextImage = () =>
|
||||||
|
updateGallery((currentIndex + 1) % galleryImages.length);
|
||||||
|
const prevImage = () =>
|
||||||
|
updateGallery(
|
||||||
|
(currentIndex - 1 + galleryImages.length) % galleryImages.length
|
||||||
|
);
|
||||||
|
|
||||||
|
// Event listeners
|
||||||
|
document.getElementById("next-btn")?.addEventListener("click", nextImage);
|
||||||
|
document.getElementById("prev-btn")?.addEventListener("click", prevImage);
|
||||||
|
|
||||||
|
thumbnails.forEach((thumb, index) => {
|
||||||
|
thumb.addEventListener("click", () => updateGallery(index));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Auto-advance every 10 seconds
|
||||||
|
setInterval(nextImage, 10000);
|
||||||
|
|
||||||
|
// ===== CONTACT FORM =====
|
||||||
|
const contactForm = document.querySelector(".contact-form");
|
||||||
|
if (contactForm) {
|
||||||
|
const validateField = (field) => {
|
||||||
|
const group = field.closest(".form-group");
|
||||||
|
const error = group?.querySelector(".error-message");
|
||||||
|
let isValid = true;
|
||||||
|
let message = "";
|
||||||
|
|
||||||
|
group?.classList.remove("error", "success");
|
||||||
|
|
||||||
|
if (field.required && !field.value.trim()) {
|
||||||
|
isValid = false;
|
||||||
|
message = "This field is required.";
|
||||||
|
} else if (
|
||||||
|
field.type === "email" &&
|
||||||
|
field.value &&
|
||||||
|
!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(field.value)
|
||||||
|
) {
|
||||||
|
isValid = false;
|
||||||
|
message = "Please enter a valid email address.";
|
||||||
|
} else if (
|
||||||
|
field.type === "tel" &&
|
||||||
|
field.value &&
|
||||||
|
!/^[\d\s\-\+\(\)]+$/.test(field.value)
|
||||||
|
) {
|
||||||
|
isValid = false;
|
||||||
|
message = "Please enter a valid phone number.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) error.textContent = message;
|
||||||
|
group?.classList.add(
|
||||||
|
isValid && field.value.trim() ? "success" : isValid ? "" : "error"
|
||||||
|
);
|
||||||
|
return isValid;
|
||||||
|
};
|
||||||
|
|
||||||
|
contactForm.querySelectorAll("input, textarea").forEach((field) => {
|
||||||
|
field.addEventListener("blur", () => validateField(field));
|
||||||
|
field.addEventListener("input", () => {
|
||||||
|
if (field.closest(".form-group")?.classList.contains("error")) {
|
||||||
|
validateField(field);
|
||||||
}
|
}
|
||||||
];
|
});
|
||||||
|
|
||||||
let currentImageIndex = 0;
|
|
||||||
const galleryImage = document.getElementById('gallery-image');
|
|
||||||
const galleryCaption = document.getElementById('gallery-caption');
|
|
||||||
const prevBtn = document.getElementById('prev-btn');
|
|
||||||
const nextBtn = document.getElementById('next-btn');
|
|
||||||
const thumbnails = document.querySelectorAll('.thumbnail');
|
|
||||||
|
|
||||||
// Check if gallery elements exist
|
|
||||||
if (!galleryImage || !galleryCaption) {
|
|
||||||
console.log('Gallery image or caption not found');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Gallery elements found:', {
|
|
||||||
image: !!galleryImage,
|
|
||||||
caption: !!galleryCaption,
|
|
||||||
prevBtn: !!prevBtn,
|
|
||||||
nextBtn: !!nextBtn,
|
|
||||||
thumbnails: thumbnails.length
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function updateGallery(index) {
|
contactForm.addEventListener("submit", (e) => {
|
||||||
console.log('Updating gallery to index:', index);
|
e.preventDefault();
|
||||||
currentImageIndex = index;
|
const fields = contactForm.querySelectorAll("input, textarea");
|
||||||
galleryImage.src = galleryImages[index].src;
|
const isValid = Array.from(fields).every(validateField);
|
||||||
galleryImage.alt = galleryImages[index].alt;
|
|
||||||
galleryCaption.textContent = galleryImages[index].caption;
|
|
||||||
|
|
||||||
// Update active thumbnail
|
if (isValid) {
|
||||||
thumbnails.forEach((thumb, i) => {
|
const submitBtn = contactForm.querySelector(".submit-btn");
|
||||||
thumb.classList.toggle('active', i === index);
|
const originalText = submitBtn.textContent;
|
||||||
});
|
submitBtn.textContent = "Sending...";
|
||||||
}
|
submitBtn.disabled = true;
|
||||||
|
|
||||||
function nextImage() {
|
setTimeout(() => {
|
||||||
console.log('Next image clicked');
|
const formData = new FormData(contactForm);
|
||||||
const newIndex = (currentImageIndex + 1) % galleryImages.length;
|
const data = Object.fromEntries(formData);
|
||||||
updateGallery(newIndex);
|
alert(`Thank you, ${data.name}! We'll contact you at ${data.email}.`);
|
||||||
}
|
contactForm.reset();
|
||||||
|
fields.forEach((f) =>
|
||||||
function prevImage() {
|
f.closest(".form-group")?.classList.remove("error", "success")
|
||||||
console.log('Previous image clicked');
|
);
|
||||||
const newIndex = (currentImageIndex - 1 + galleryImages.length) % galleryImages.length;
|
submitBtn.textContent = originalText;
|
||||||
updateGallery(newIndex);
|
submitBtn.disabled = false;
|
||||||
}
|
}, 2000);
|
||||||
|
}
|
||||||
// Event listeners for gallery controls
|
|
||||||
if (prevBtn) {
|
|
||||||
console.log('Adding prev button listener');
|
|
||||||
prevBtn.addEventListener('click', prevImage);
|
|
||||||
} else {
|
|
||||||
console.log('Prev button not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextBtn) {
|
|
||||||
console.log('Adding next button listener');
|
|
||||||
nextBtn.addEventListener('click', nextImage);
|
|
||||||
} else {
|
|
||||||
console.log('Next button not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thumbnail click events
|
|
||||||
thumbnails.forEach((thumbnail) => {
|
|
||||||
thumbnail.addEventListener('click', () => {
|
|
||||||
const index = parseInt(thumbnail.getAttribute('data-index'));
|
|
||||||
console.log('Thumbnail clicked, index:', index);
|
|
||||||
if (!isNaN(index)) {
|
|
||||||
updateGallery(index);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Auto-advance gallery every 5 seconds
|
// ===== FAQ ACCORDION =====
|
||||||
setInterval(nextImage, 5000);
|
document.querySelectorAll(".faq-question").forEach((question) => {
|
||||||
console.log('Gallery initialized successfully');
|
question.addEventListener("click", function () {
|
||||||
}); // End of DOMContentLoaded for gallery
|
const answer = this.nextElementSibling;
|
||||||
|
const toggle = this.querySelector(".faq-toggle");
|
||||||
|
const isOpen = this.getAttribute("aria-expanded") === "true";
|
||||||
|
|
||||||
// Enhanced Contact Form Validation
|
// Close all other FAQs
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.querySelectorAll(".faq-question").forEach((q) => {
|
||||||
const contactForm = document.querySelector('.contact-form');
|
if (q !== this) {
|
||||||
|
q.setAttribute("aria-expanded", "false");
|
||||||
if (contactForm) {
|
q.nextElementSibling?.classList.remove("active");
|
||||||
const formGroups = contactForm.querySelectorAll('.form-group');
|
q.querySelector(".faq-toggle")?.classList.remove("active");
|
||||||
|
|
||||||
// Real-time validation function
|
|
||||||
function validateField(field) {
|
|
||||||
const formGroup = field.closest('.form-group');
|
|
||||||
const errorSpan = formGroup.querySelector('.error-message');
|
|
||||||
let isValid = true;
|
|
||||||
let errorMessage = '';
|
|
||||||
|
|
||||||
// Remove previous validation classes
|
|
||||||
formGroup.classList.remove('error', 'success');
|
|
||||||
|
|
||||||
// Validate required fields
|
|
||||||
if (field.hasAttribute('required') && !field.value.trim()) {
|
|
||||||
isValid = false;
|
|
||||||
errorMessage = 'This field is required.';
|
|
||||||
} else if (field.type === 'email' && field.value) {
|
|
||||||
// Email validation
|
|
||||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
||||||
if (!emailRegex.test(field.value)) {
|
|
||||||
isValid = false;
|
|
||||||
errorMessage = 'Please enter a valid email address.';
|
|
||||||
}
|
|
||||||
} else if (field.type === 'tel' && field.value) {
|
|
||||||
// Phone validation (basic)
|
|
||||||
const phoneRegex = /^[\d\s\-\+\(\)]+$/;
|
|
||||||
if (!phoneRegex.test(field.value)) {
|
|
||||||
isValid = false;
|
|
||||||
errorMessage = 'Please enter a valid phone number.';
|
|
||||||
}
|
|
||||||
} else if (field.tagName === 'TEXTAREA' && field.value.length < 10 && field.hasAttribute('required')) {
|
|
||||||
// Message length validation
|
|
||||||
isValid = false;
|
|
||||||
errorMessage = 'Message must be at least 10 characters long.';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display validation results
|
|
||||||
if (errorSpan) {
|
|
||||||
errorSpan.textContent = errorMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isValid && field.value.trim()) {
|
|
||||||
formGroup.classList.add('success');
|
|
||||||
} else if (!isValid) {
|
|
||||||
formGroup.classList.add('error');
|
|
||||||
}
|
|
||||||
|
|
||||||
return isValid;
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Add real-time validation to all form fields
|
// Toggle current FAQ
|
||||||
formGroups.forEach(group => {
|
this.setAttribute("aria-expanded", !isOpen);
|
||||||
const input = group.querySelector('input, textarea');
|
answer?.classList.toggle("active");
|
||||||
if (input) {
|
toggle?.classList.toggle("active");
|
||||||
input.addEventListener('blur', () => validateField(input));
|
});
|
||||||
input.addEventListener('input', () => {
|
});
|
||||||
if (group.classList.contains('error')) {
|
|
||||||
validateField(input);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
contactForm.addEventListener('submit', function(e) {
|
// ===== HOVER EFFECTS =====
|
||||||
e.preventDefault();
|
document
|
||||||
|
.querySelectorAll(".services-list li, .value-item, .timeline-item")
|
||||||
|
.forEach((item) => {
|
||||||
|
item.addEventListener("mouseenter", () => {
|
||||||
|
item.style.transform = "translateY(-2px)";
|
||||||
|
item.style.transition = "transform 0.3s ease";
|
||||||
|
});
|
||||||
|
item.addEventListener("mouseleave", () => {
|
||||||
|
item.style.transform = "translateY(0)";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
let isFormValid = true;
|
// ===== ACTIVE NAVIGATION =====
|
||||||
const formData = new FormData(contactForm);
|
const currentPage = location.pathname.split("/").pop();
|
||||||
const data = Object.fromEntries(formData);
|
document.querySelectorAll(".main-nav a").forEach((link) => {
|
||||||
|
const linkPage = link.getAttribute("href");
|
||||||
// Validate all fields
|
if (
|
||||||
formGroups.forEach(group => {
|
linkPage === currentPage ||
|
||||||
const input = group.querySelector('input, textarea');
|
(currentPage === "" && linkPage === "index.html")
|
||||||
if (input && !validateField(input)) {
|
) {
|
||||||
isFormValid = false;
|
link.style.backgroundColor = "#daa520";
|
||||||
}
|
link.style.color = "#5d4037";
|
||||||
});
|
|
||||||
|
|
||||||
if (!isFormValid) {
|
|
||||||
// Focus on first error field
|
|
||||||
const firstError = contactForm.querySelector('.form-group.error input, .form-group.error textarea');
|
|
||||||
if (firstError) {
|
|
||||||
firstError.focus();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simulate form submission with loading state
|
|
||||||
const submitBtn = contactForm.querySelector('.submit-btn');
|
|
||||||
const originalText = submitBtn.textContent;
|
|
||||||
submitBtn.textContent = 'Sending...';
|
|
||||||
submitBtn.disabled = true;
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
alert(`Thank you for your message, ${data.name}! We'll get back to you soon at ${data.email}.`);
|
|
||||||
contactForm.reset();
|
|
||||||
formGroups.forEach(group => group.classList.remove('error', 'success'));
|
|
||||||
submitBtn.textContent = originalText;
|
|
||||||
submitBtn.disabled = false;
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// FAQ Accordion Functionality
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const faqQuestions = document.querySelectorAll('.faq-question');
|
|
||||||
|
|
||||||
faqQuestions.forEach(question => {
|
|
||||||
question.addEventListener('click', function() {
|
|
||||||
const answer = this.nextElementSibling;
|
|
||||||
const toggle = this.querySelector('.faq-toggle');
|
|
||||||
const isExpanded = this.getAttribute('aria-expanded') === 'true';
|
|
||||||
|
|
||||||
// Close all other FAQ items
|
|
||||||
faqQuestions.forEach(otherQuestion => {
|
|
||||||
if (otherQuestion !== this) {
|
|
||||||
const otherAnswer = otherQuestion.nextElementSibling;
|
|
||||||
const otherToggle = otherQuestion.querySelector('.faq-toggle');
|
|
||||||
|
|
||||||
otherQuestion.setAttribute('aria-expanded', 'false');
|
|
||||||
otherAnswer.classList.remove('active');
|
|
||||||
otherToggle.classList.remove('active');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Toggle current FAQ item
|
|
||||||
this.setAttribute('aria-expanded', !isExpanded);
|
|
||||||
answer.classList.toggle('active');
|
|
||||||
toggle.classList.toggle('active');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add keyboard navigation
|
|
||||||
question.addEventListener('keydown', function(e) {
|
|
||||||
if (e.key === 'Enter' || e.key === ' ') {
|
|
||||||
e.preventDefault();
|
|
||||||
this.click();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Smooth scrolling for navigation links
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const navLinks = document.querySelectorAll('.main-nav a');
|
|
||||||
|
|
||||||
navLinks.forEach(link => {
|
|
||||||
link.addEventListener('click', function(e) {
|
|
||||||
// Check if it's an internal link
|
|
||||||
if (this.hostname === window.location.hostname) {
|
|
||||||
// Add a subtle animation effect
|
|
||||||
this.style.transform = 'scale(0.95)';
|
|
||||||
setTimeout(() => {
|
|
||||||
this.style.transform = 'scale(1)';
|
|
||||||
}, 150);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add hover effects to service items
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const serviceItems = document.querySelectorAll('.services-list li, .value-item, .timeline-item');
|
|
||||||
|
|
||||||
serviceItems.forEach(item => {
|
|
||||||
item.addEventListener('mouseenter', function() {
|
|
||||||
this.style.transform = 'translateY(-2px)';
|
|
||||||
this.style.transition = 'transform 0.3s ease';
|
|
||||||
});
|
|
||||||
|
|
||||||
item.addEventListener('mouseleave', function() {
|
|
||||||
this.style.transform = 'translateY(0)';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Active navigation highlighting
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const currentPage = window.location.pathname.split('/').pop();
|
|
||||||
const navLinks = document.querySelectorAll('.main-nav a');
|
|
||||||
|
|
||||||
navLinks.forEach(link => {
|
|
||||||
const linkPage = link.getAttribute('href');
|
|
||||||
if (linkPage === currentPage || (currentPage === '' && linkPage === 'index.html')) {
|
|
||||||
link.style.backgroundColor = '#daa520';
|
|
||||||
link.style.color = '#5d4037';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user