File: //home/bdedition/bddiary.com/assets/js/app.js
/*
Professional JavaScript enhancements for Bootstrap 5+ design
Additional functionality and UI improvements
*/
// Enhanced UI interactions
document.addEventListener('DOMContentLoaded', function() {
// Smooth reveal animations for cards
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('fade-in');
observer.unobserve(entry.target);
}
});
}, observerOptions);
// Observe all cards for animation
document.querySelectorAll('.card-custom').forEach(card => {
observer.observe(card);
});
// Enhanced keyboard navigation
document.addEventListener('keydown', function(e) {
// ESC key to close any active states
if (e.key === 'Escape') {
const activeModals = document.querySelectorAll('.modal.show');
activeModals.forEach(modal => {
const modalInstance = bootstrap.Modal.getInstance(modal);
if (modalInstance) {
modalInstance.hide();
}
});
}
// Enter key on focused buttons
if (e.key === 'Enter' && document.activeElement.classList.contains('btn')) {
document.activeElement.click();
}
});
// Professional loading states
function setLoadingState(element, isLoading) {
if (isLoading) {
element.disabled = true;
element.classList.add('btn-loading');
const originalText = element.innerHTML;
element.dataset.originalText = originalText;
element.innerHTML = `<span class="spinner-border spinner-border-sm me-2" role="status"></span>Loading...`;
} else {
element.disabled = false;
element.classList.remove('btn-loading');
if (element.dataset.originalText) {
element.innerHTML = element.dataset.originalText;
}
}
}
// Export enhanced loading function for use in main app
window.setLoadingState = setLoadingState;
// Enhanced tooltips
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
const tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
// Auto-hide alerts after delay
document.querySelectorAll('.alert').forEach(alert => {
setTimeout(() => {
const alertInstance = new bootstrap.Alert(alert);
alertInstance.close();
}, 5000);
});
// Enhanced form validation
const forms = document.querySelectorAll('form');
forms.forEach(form => {
form.addEventListener('submit', function(e) {
if (!form.checkValidity()) {
e.preventDefault();
e.stopPropagation();
}
form.classList.add('was-validated');
});
});
// Smooth scrolling to sections
function scrollToSection(sectionId) {
const section = document.getElementById(sectionId);
if (section) {
section.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
}
window.scrollToSection = scrollToSection;
// Performance optimization - lazy load images
const images = document.querySelectorAll('img[data-src]');
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.removeAttribute('data-src');
imageObserver.unobserve(img);
}
});
});
images.forEach(img => imageObserver.observe(img));
// Add professional focus management
let lastFocusedElement = null;
document.addEventListener('focusin', function(e) {
lastFocusedElement = e.target;
});
// Restore focus when modals close
document.addEventListener('hidden.bs.modal', function() {
if (lastFocusedElement) {
lastFocusedElement.focus();
}
});
});
// Enhanced error handling
window.addEventListener('error', function(e) {
console.error('JavaScript Error:', e.error);
// Could show user-friendly error message here
});
// Professional resize handler
let resizeTimeout;
window.addEventListener('resize', function() {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(function() {
// Trigger custom resize event for canvas and other components
window.dispatchEvent(new Event('optimizedResize'));
}, 250);
});
// Export utilities for global use
window.UIUtils = {
setLoadingState,
scrollToSection,
// Show professional notifications
showNotification: function(message, type = 'info', duration = 3000) {
const alertDiv = document.createElement('div');
alertDiv.className = `alert alert-${type} alert-dismissible fade show position-fixed`;
alertDiv.style.cssText = 'top: 20px; right: 20px; z-index: 1060; min-width: 300px;';
alertDiv.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.body.appendChild(alertDiv);
setTimeout(() => {
const alert = new bootstrap.Alert(alertDiv);
alert.close();
}, duration);
},
// Confirm dialog
confirm: function(message, callback) {
if (window.confirm(message)) {
callback();
}
}
};