- CREAR: template-parts/content-toc.php
* Función PHP apu_generate_toc() para generar TOC automáticamente
* Regex para buscar H2 con atributo ID
* Solo se muestra en is_single()
- MODIFICAR: single.php (sidebar)
* Agregar div.sidebar-sticky wrapper
* Integrar get_template_part('template-parts/content', 'toc')
* Preparar espacio para CTA Box (componente 12)
- MODIFICAR: style.css (+87 líneas)
* 9 selectores CSS + 4 pseudo-elementos scrollbar
* Sticky positioning (top: 85px)
* Max-height calculado con calc()
* Scroll interno con overflow-y: auto
* Min-height: 0 (crítico para scroll en flexbox)
* Border-left indicator (navy, NO naranja)
* Scrollbar personalizado 6px (solo Webkit)
- MODIFICAR: main.js (+66 líneas)
* Función updateActiveSection() para ScrollSpy
* Algoritmo SIMPLE: verifica si pasaste el top
* Offset scroll: navbarHeight + 100
* Smooth scroll: navbarHeight + 40
* Selector .toc-container a (NO .toc-link)
* NO tiene auto-scroll del TOC
* NO actualiza URL con history.pushState()
Características:
✅ Generación automática desde H2 con ID
✅ ScrollSpy custom (JavaScript vanilla, NO Bootstrap)
✅ Sticky positioning con flexbox
✅ Scroll interno solo en lista
✅ Border-left indicator activo
✅ Scrollbar delgado personalizado
Selectores REALES usados:
- .toc-container a (NO .toc-link)
- .toc-container h4 (NO .toc-title)
- .toc-container li (NO .toc-list li)
🎨 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
334 lines
11 KiB
JavaScript
334 lines
11 KiB
JavaScript
/**
|
|
* APU MÉXICO - MAIN JAVASCRIPT
|
|
*/
|
|
|
|
// Navbar scroll effect
|
|
window.addEventListener('scroll', function() {
|
|
const navbar = document.querySelector('.navbar');
|
|
if (navbar) {
|
|
if (window.scrollY > 50) {
|
|
navbar.classList.add('scrolled');
|
|
} else {
|
|
navbar.classList.remove('scrolled');
|
|
}
|
|
}
|
|
});
|
|
|
|
/**
|
|
* TOC (Table of Contents) - Handled by toc.js
|
|
* No duplicate code needed here - toc.js provides:
|
|
* - ScrollSpy with IntersectionObserver
|
|
* - Smooth scroll with prefers-reduced-motion support
|
|
* - Toggle functionality
|
|
* - localStorage state
|
|
*/
|
|
|
|
// A/B Testing for CTA sections
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const ctaVariant = Math.random() < 0.5 ? 'A' : 'B';
|
|
|
|
if (ctaVariant === 'A') {
|
|
const variantA = document.querySelector('.cta-variant-a');
|
|
if (variantA) variantA.style.display = 'block';
|
|
} else {
|
|
const variantB = document.querySelector('.cta-variant-b');
|
|
if (variantB) variantB.style.display = 'block';
|
|
}
|
|
|
|
document.querySelectorAll('.cta-button').forEach(button => {
|
|
button.addEventListener('click', function() {
|
|
const variant = this.getAttribute('data-cta-variant');
|
|
console.log('CTA clicked - Variant: ' + variant);
|
|
|
|
if (typeof gtag !== 'undefined') {
|
|
gtag('event', 'cta_click', {
|
|
'event_category': 'CTA',
|
|
'event_label': 'Variant_' + variant,
|
|
'value': variant
|
|
});
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
// Contact Modal - Dynamic Loading
|
|
function loadContactModal() {
|
|
const modalContainer = document.getElementById('modalContainer');
|
|
if (!modalContainer) return;
|
|
|
|
// Use theme URL from localized script
|
|
const modalUrl = (typeof apusTheme !== 'undefined' && apusTheme.themeUrl)
|
|
? apusTheme.themeUrl + '/modal-contact.html'
|
|
: '/wp-content/themes/apus-theme/modal-contact.html';
|
|
|
|
fetch(modalUrl)
|
|
.then(response => response.text())
|
|
.then(html => {
|
|
modalContainer.innerHTML = html;
|
|
initContactForm();
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading modal:', error);
|
|
});
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', loadContactModal);
|
|
|
|
// Contact Form - Webhook Submission
|
|
function initContactForm() {
|
|
const form = document.getElementById('contactForm');
|
|
if (!form) return;
|
|
|
|
form.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
const WEBHOOK_URL = 'https://tu-webhook.com/contacto';
|
|
|
|
const formData = {
|
|
fullName: document.getElementById('fullName').value,
|
|
company: document.getElementById('company').value,
|
|
whatsapp: document.getElementById('whatsapp').value,
|
|
email: document.getElementById('email').value,
|
|
comments: document.getElementById('comments').value,
|
|
timestamp: new Date().toISOString(),
|
|
source: 'APU Website - Modal'
|
|
};
|
|
|
|
if (!formData.fullName || !formData.whatsapp || !formData.email) {
|
|
showFormMessage('Por favor completa todos los campos requeridos', 'danger');
|
|
return;
|
|
}
|
|
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
if (!emailRegex.test(formData.email)) {
|
|
showFormMessage('Por favor ingresa un correo electrónico válido', 'danger');
|
|
return;
|
|
}
|
|
|
|
const submitButton = form.querySelector('button[type="submit"]');
|
|
const originalText = submitButton.innerHTML;
|
|
submitButton.disabled = true;
|
|
submitButton.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Enviando...';
|
|
|
|
fetch(WEBHOOK_URL, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(formData)
|
|
})
|
|
.then(response => {
|
|
if (!response.ok) throw new Error('Error en el envío');
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
showFormMessage('¡Mensaje enviado exitosamente!', 'success');
|
|
form.reset();
|
|
|
|
if (typeof gtag !== 'undefined') {
|
|
gtag('event', 'form_submission', {
|
|
'event_category': 'Contact Form',
|
|
'event_label': 'Form Submitted'
|
|
});
|
|
}
|
|
|
|
setTimeout(() => {
|
|
const modal = bootstrap.Modal.getInstance(document.getElementById('contactModal'));
|
|
if (modal) modal.hide();
|
|
}, 2000);
|
|
})
|
|
.catch(error => {
|
|
showFormMessage('Error al enviar el mensaje', 'danger');
|
|
})
|
|
.finally(() => {
|
|
submitButton.disabled = false;
|
|
submitButton.innerHTML = originalText;
|
|
});
|
|
});
|
|
}
|
|
|
|
function showFormMessage(message, type) {
|
|
const messageDiv = document.getElementById('formMessage');
|
|
if (!messageDiv) return;
|
|
|
|
messageDiv.textContent = message;
|
|
messageDiv.className = `mt-3 alert alert-${type}`;
|
|
messageDiv.style.display = 'block';
|
|
|
|
setTimeout(() => {
|
|
messageDiv.style.display = 'none';
|
|
}, 5000);
|
|
}
|
|
|
|
// Footer Contact Form
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const footerForm = document.getElementById('footerContactForm');
|
|
if (!footerForm) return;
|
|
|
|
footerForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
const WEBHOOK_URL = 'https://tu-webhook.com/contacto';
|
|
|
|
const formData = {
|
|
fullName: document.getElementById('footerFullName').value,
|
|
company: document.getElementById('footerCompany').value,
|
|
whatsapp: document.getElementById('footerWhatsapp').value,
|
|
email: document.getElementById('footerEmail').value,
|
|
comments: document.getElementById('footerComments').value,
|
|
timestamp: new Date().toISOString(),
|
|
source: 'APU Website - Footer'
|
|
};
|
|
|
|
if (!formData.fullName || !formData.whatsapp || !formData.email) {
|
|
showFooterFormMessage('Por favor completa todos los campos requeridos', 'danger');
|
|
return;
|
|
}
|
|
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
if (!emailRegex.test(formData.email)) {
|
|
showFooterFormMessage('Por favor ingresa un correo válido', 'danger');
|
|
return;
|
|
}
|
|
|
|
const submitButton = footerForm.querySelector('button[type="submit"]');
|
|
const originalText = submitButton.innerHTML;
|
|
submitButton.disabled = true;
|
|
submitButton.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Enviando...';
|
|
|
|
fetch(WEBHOOK_URL, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(formData)
|
|
})
|
|
.then(response => {
|
|
if (!response.ok) throw new Error('Error en el envío');
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
showFooterFormMessage('¡Mensaje enviado exitosamente!', 'success');
|
|
footerForm.reset();
|
|
|
|
if (typeof gtag !== 'undefined') {
|
|
gtag('event', 'form_submission', {
|
|
'event_category': 'Footer Form',
|
|
'event_label': 'Form Submitted'
|
|
});
|
|
}
|
|
})
|
|
.catch(error => {
|
|
showFooterFormMessage('Error al enviar el mensaje', 'danger');
|
|
})
|
|
.finally(() => {
|
|
submitButton.disabled = false;
|
|
submitButton.innerHTML = originalText;
|
|
});
|
|
});
|
|
});
|
|
|
|
function showFooterFormMessage(message, type) {
|
|
const messageDiv = document.getElementById('footerFormMessage');
|
|
if (!messageDiv) return;
|
|
|
|
messageDiv.textContent = message;
|
|
messageDiv.className = `col-12 mt-2 alert alert-${type}`;
|
|
messageDiv.style.display = 'block';
|
|
|
|
setTimeout(() => {
|
|
messageDiv.style.display = 'none';
|
|
}, 5000);
|
|
}
|
|
|
|
// Smooth scroll for all anchor links
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
anchor.addEventListener('click', function (e) {
|
|
const href = this.getAttribute('href');
|
|
|
|
if (href === '#' || this.getAttribute('data-bs-toggle') === 'modal') {
|
|
return;
|
|
}
|
|
|
|
const targetElement = document.querySelector(href);
|
|
if (!targetElement) return;
|
|
|
|
e.preventDefault();
|
|
|
|
const navbar = document.querySelector('.navbar');
|
|
const navbarHeight = navbar ? navbar.offsetHeight : 0;
|
|
const offsetTop = targetElement.offsetTop - navbarHeight - 20;
|
|
|
|
window.scrollTo({
|
|
top: offsetTop,
|
|
behavior: 'smooth'
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
console.log('%c APU México ', 'background: #1e3a5f; color: #FF8600; font-size: 16px; font-weight: bold; padding: 10px;');
|
|
|
|
// === SIDEBAR TOC - ScrollSpy ===
|
|
|
|
// Table of Contents - ScrollSpy
|
|
function updateActiveSection() {
|
|
const tocLinks = document.querySelectorAll('.toc-container a');
|
|
if (!tocLinks.length) return;
|
|
|
|
const navbar = document.querySelector('.navbar');
|
|
const navbarHeight = navbar ? navbar.offsetHeight : 0;
|
|
|
|
const sectionIds = Array.from(tocLinks).map(link => {
|
|
const href = link.getAttribute('href');
|
|
return href ? href.substring(1) : null;
|
|
}).filter(id => id !== null);
|
|
|
|
const sections = sectionIds.map(id => document.getElementById(id)).filter(el => el !== null);
|
|
const scrollPosition = window.scrollY + navbarHeight + 100;
|
|
|
|
let activeSection = null;
|
|
|
|
for (let i = 0; i < sections.length; i++) {
|
|
const section = sections[i];
|
|
const sectionTop = section.offsetTop;
|
|
|
|
if (scrollPosition >= sectionTop) {
|
|
activeSection = section.getAttribute('id');
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
tocLinks.forEach(link => {
|
|
link.classList.remove('active');
|
|
const href = link.getAttribute('href');
|
|
if (href === '#' + activeSection) {
|
|
link.classList.add('active');
|
|
}
|
|
});
|
|
}
|
|
|
|
// Smooth scroll for TOC links
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
document.querySelectorAll('.toc-container a').forEach(anchor => {
|
|
anchor.addEventListener('click', function (e) {
|
|
e.preventDefault();
|
|
const targetId = this.getAttribute('href');
|
|
const targetElement = document.querySelector(targetId);
|
|
|
|
if (targetElement) {
|
|
const navbar = document.querySelector('.navbar');
|
|
const navbarHeight = navbar ? navbar.offsetHeight : 0;
|
|
const offsetTop = targetElement.offsetTop - navbarHeight - 40;
|
|
|
|
window.scrollTo({
|
|
top: offsetTop,
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
updateActiveSection();
|
|
});
|
|
|
|
window.addEventListener('scroll', updateActiveSection);
|